1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Copyright (c) International Business Machines  Corp., 2004
4 * Copyright (c) Linux Test Project, 2004-2017
5 *
6 * DESCRIPTION
7 *	hugeshmdt01 - check that largr shared memory is detached correctly
8 *
9 * ALGORITHM
10 *	create a large shared memory resource
11 *	attach it to the current process and give it a value
12 *	call shmdt() using the TEST macro
13 *	check the return code
14 *	  if failure, issue a FAIL message.
15 *	otherwise,
16 *	  if doing functionality testing
17 *		attempt to write a value to the large shared memory address
18 *		this should generate a SIGSEGV which will be caught in
19 *		    the signal handler
20 *		if correct,
21 *			issue a PASS message
22 *		otherwise
23 *			issue a FAIL message
24 *	call cleanup
25 *
26 * HISTORY
27 *	03/2001 - Written by Wayne Boyer
28 *	04/2004 - Updated by Robbie Williamson
29 */
30
31#include <setjmp.h>
32#include <limits.h>
33#include "hugetlb.h"
34
35static size_t shm_size;
36static int shm_id_1 = -1;
37struct shmid_ds buf;
38static int *shared;
39static int pass;
40static sigjmp_buf env;
41
42static void check_functionality(void);
43static void sighandler(int sig);
44
45static void hugeshmdt_test(void)
46{
47	struct sigaction sa;
48
49	sa.sa_handler = sighandler;
50	sigaction(SIGSEGV, &sa, NULL);
51
52	if (shmdt(shared) == -1)
53		tst_res(TFAIL | TERRNO, "shmdt");
54	else
55		check_functionality();
56
57	/* reattach the shared memory segment in case we are looping */
58	shared = shmat(shm_id_1, 0, 0);
59	if (shared == (void *)-1)
60		tst_brk(TBROK | TERRNO, "shmat #2: reattach");
61
62	/* also reset pass */
63	pass = 0;
64}
65
66static void check_functionality(void)
67{
68	/* stat the shared memory segment */
69	if (shmctl(shm_id_1, IPC_STAT, &buf) == -1)
70		tst_brk(TBROK | TERRNO, "shmctl");
71
72	if (buf.shm_nattch != 0) {
73		tst_res(TFAIL, "# of attaches is incorrect");
74		return;
75	}
76
77	/*
78	 * Try writing to the shared memory.  This should generate a
79	 * SIGSEGV which will be caught below.
80	 *
81	 * This is wrapped by the sigsetjmp() call that will take care of
82	 * restoring the program's context in an elegant way in conjunction
83	 * with the call to siglongjmp() in the signal handler.
84	 *
85	 * An attempt to do the assignment without using the sigsetjmp()
86	 * and siglongjmp() calls will result in an infinite loop.  Program
87	 * control is returned to the assignment statement after the execution
88	 * of the signal handler and another SIGSEGV will be generated.
89	 */
90
91	if (sigsetjmp(env, 1) == 0)
92		*shared = 2;
93
94	if (pass)
95		tst_res(TPASS, "huge shared memory detached correctly");
96	else
97		tst_res(TFAIL, "huge shared memory was not detached "
98			 "correctly");
99}
100
101static void sighandler(int sig)
102{
103	/* if we have received a SIGSEGV, we are almost done */
104	if (sig == SIGSEGV) {
105		/* set the global variable and jump back */
106		pass = 1;
107		siglongjmp(env, 1);
108	} else {
109		tst_brk(TBROK, "unexpected signal received: %d", sig);
110	}
111}
112
113void setup(void)
114{
115	long hpage_size;
116
117	if (tst_hugepages == 0)
118		tst_brk(TCONF, "No enough hugepages for testing.");
119
120	hpage_size = SAFE_READ_MEMINFO("Hugepagesize:") * 1024;
121
122	shm_size = hpage_size * tst_hugepages / 2;
123	update_shm_size(&shm_size);
124	shmkey = getipckey();
125
126	/* create a shared memory resource with read and write permissions */
127	shm_id_1 = shmget(shmkey, shm_size,
128			  SHM_HUGETLB | SHM_RW | IPC_CREAT | IPC_EXCL);
129	if (shm_id_1 == -1)
130		tst_brk(TBROK | TERRNO, "shmget");
131
132	/* attach the shared memory segment */
133	shared = shmat(shm_id_1, 0, 0);
134	if (shared == (void *)-1)
135		tst_brk(TBROK | TERRNO, "shmat #1");
136
137	/* give a value to the shared memory integer */
138	*shared = 4;
139}
140
141void cleanup(void)
142{
143	rm_shm(shm_id_1);
144}
145
146static struct tst_test test = {
147	.needs_root = 1,
148	.options = (struct tst_option[]) {
149		{"s:", &nr_opt, "Set the number of the been allocated hugepages"},
150		{}
151	},
152	.setup = setup,
153	.cleanup = cleanup,
154	.test_all = hugeshmdt_test,
155	.hugepages = {128, TST_REQUEST},
156};
157