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-2020 5 */ 6 7/* 8 * DESCRIPTION 9 * hugeshmctl01 - test the IPC_STAT, IPC_SET and IPC_RMID commands as 10 * they are used with shmctl() 11 * 12 * ALGORITHM 13 * loop if that option was specified 14 * create a large shared memory segment with read and write permission 15 * set up any test case specific conditions 16 * call shmctl() using the TEST macro 17 * check the return code 18 * if failure, issue a FAIL message. 19 * otherwise, 20 * if doing functionality testing 21 * call the correct test function 22 * if the conditions are correct, 23 * issue a PASS message 24 * otherwise 25 * issue a FAIL message 26 * otherwise 27 * issue a PASS message 28 * call cleanup 29 * 30 * HISTORY 31 * 03/2001 - Written by Wayne Boyer 32 * 04/2004 - Updated by Robbie Williamson 33 */ 34 35#include <limits.h> 36#include "hugetlb.h" 37 38#define N_ATTACH 4U 39#define NEWMODE 0066 40 41static size_t shm_size; 42static int shm_id_1 = -1; 43static struct shmid_ds buf; 44static time_t save_time; 45static void *attach_to_parent; 46 47static void stat_setup_1(void); 48static void stat_cleanup(void); 49static void stat_setup_2(void); 50static void set_setup(void); 51static void func_stat(void); 52static void func_set(void); 53static void func_rmid(void); 54static void *set_shmat(void); 55 56static struct tcase { 57 int cmd; 58 void (*func_test)(void); 59 void (*func_setup)(void); 60} tcases[] = { 61 {IPC_STAT, func_stat, stat_setup_1}, 62 {IPC_STAT, func_stat, stat_setup_2}, 63 {IPC_SET, func_set, set_setup}, 64 {IPC_RMID, func_rmid, NULL} 65}; 66 67static void test_hugeshmctl(unsigned int i) 68{ 69 /* 70 * Create a shared memory segment with read and write 71 * permissions. Do this here instead of in setup() 72 * so that looping (-i) will work correctly. 73 */ 74 if (i == 0) 75 shm_id_1 = shmget(shmkey, shm_size, 76 SHM_HUGETLB | IPC_CREAT | IPC_EXCL | SHM_RW); 77 if (shm_id_1 == -1) 78 tst_brk(TBROK | TERRNO, "shmget #main"); 79 80 if (tcases[i].func_setup != NULL) 81 (*tcases[i].func_setup) (); 82 83 if (shmctl(shm_id_1, tcases[i].cmd, &buf) == -1) { 84 tst_res(TFAIL | TERRNO, "shmctl #main"); 85 return; 86 } 87 (*tcases[i].func_test)(); 88} 89 90/* 91 * set_shmat() - Attach the shared memory and return the pointer. 92 */ 93static void *set_shmat(void) 94{ 95 void *rval; 96 97 rval = shmat(shm_id_1, 0, 0); 98 if (rval == (void *)-1) 99 tst_brk(TBROK | TERRNO, "set shmat"); 100 101 return rval; 102} 103 104/* 105 * stat_setup_2() - Set up for the IPC_STAT command with shmctl(). 106 * Attach the shared memory to parent process and 107 * some children will inherit the shared memory. 108 */ 109static void stat_setup_2(void) 110{ 111 if (!attach_to_parent) 112 attach_to_parent = set_shmat(); 113 stat_setup_1(); 114} 115 116/* 117 * stat_setup_1() - Set up for the IPC_STAT command with shmctl(). 118 * some children will inherit or attatch the shared memory. 119 * It deponds on whther we attach the shared memory 120 * to parent process. 121 */ 122static void stat_setup_1(void) 123{ 124 unsigned int i; 125 void *test; 126 pid_t pid; 127 128 for (i = 0; i < N_ATTACH; i++) { 129 switch (pid = SAFE_FORK()) { 130 case 0: 131 test = (attach_to_parent == NULL) ? set_shmat() : attach_to_parent; 132 /* do an assignement for fun */ 133 *(int *)test = i; 134 135 TST_CHECKPOINT_WAKE(0); 136 137 TST_CHECKPOINT_WAIT(1); 138 139 /* now we're back - detach the memory and exit */ 140 if (shmdt(test) == -1) 141 tst_brk(TBROK | TERRNO, 142 "shmdt in this function broke"); 143 144 exit(0); 145 default: 146 TST_CHECKPOINT_WAIT(0); 147 } 148 } 149} 150 151 152/* 153 * func_stat() - check the functionality of the IPC_STAT command with shmctl() 154 * by looking at the pid of the creator, the segement size, 155 * the number of attaches and the mode. 156 */ 157static void func_stat(void) 158{ 159 pid_t pid; 160 unsigned int num; 161 162 /* check perm, pid, nattach and size */ 163 pid = getpid(); 164 165 if (buf.shm_cpid != pid) { 166 tst_res(TFAIL, "creator pid is incorrect"); 167 goto fail; 168 } 169 170 if (buf.shm_segsz != shm_size) { 171 tst_res(TFAIL, "segment size is incorrect"); 172 goto fail; 173 } 174 175 /* 176 * The first case, only the children attach the memory, so 177 * the attaches equal N_ATTACH. The second case, the parent 178 * attaches the memory and the children inherit that memory 179 * so the attaches equal N_ATTACH + 1. 180 */ 181 num = (attach_to_parent == NULL) ? 0 : 1; 182 if (buf.shm_nattch != N_ATTACH + num) { 183 tst_res(TFAIL, "# of attaches is incorrect - %lu", 184 (unsigned long)buf.shm_nattch); 185 goto fail; 186 } 187 188 /* use MODE_MASK to make sure we are comparing the last 9 bits */ 189 if ((buf.shm_perm.mode & MODE_MASK) != ((SHM_RW) & MODE_MASK)) { 190 tst_res(TFAIL, "segment mode is incorrect"); 191 goto fail; 192 } 193 194 tst_res(TPASS, "pid, size, # of attaches and mode are correct " 195 "- pass #%d", num); 196 197fail: 198 stat_cleanup(); 199 200 /* save the change time for use in the next test */ 201 save_time = buf.shm_ctime; 202} 203 204/* 205 * stat_cleanup() - signal the children to clean up after themselves and 206 * have the parent make dessert, er, um, make that remove 207 * the shared memory that is no longer needed. 208 */ 209static void stat_cleanup(void) 210{ 211 unsigned int i; 212 int status; 213 214 /* wake up the childern so they can detach the memory and exit */ 215 TST_CHECKPOINT_WAKE2(1, N_ATTACH); 216 217 for (i = 0; i < N_ATTACH; i++) 218 SAFE_WAIT(&status); 219 220 /* remove the parent's shared memory if we set*/ 221 if (attach_to_parent) { 222 if (shmdt(attach_to_parent) == -1) 223 tst_res(TFAIL | TERRNO, 224 "shmdt in this function failed"); 225 attach_to_parent = NULL; 226 } 227} 228 229/* 230 * set_setup() - set up for the IPC_SET command with shmctl() 231 */ 232static void set_setup(void) 233{ 234 /* set up a new mode for the shared memory segment */ 235 buf.shm_perm.mode = SHM_RW | NEWMODE; 236 237 /* sleep for one second to get a different shm_ctime value */ 238 sleep(1); 239} 240 241/* 242 * func_set() - check the functionality of the IPC_SET command with shmctl() 243 */ 244static void func_set(void) 245{ 246 /* first stat the shared memory to get the new data */ 247 if (shmctl(shm_id_1, IPC_STAT, &buf) == -1) { 248 tst_res(TFAIL | TERRNO, "shmctl in this function failed"); 249 return; 250 } 251 252 if ((buf.shm_perm.mode & MODE_MASK) != ((SHM_RW | NEWMODE) & MODE_MASK)) { 253 tst_res(TFAIL, "new mode is incorrect"); 254 return; 255 } 256 257 if (save_time >= buf.shm_ctime) { 258 tst_res(TFAIL, "change time is incorrect"); 259 return; 260 } 261 262 tst_res(TPASS, "new mode and change time are correct"); 263} 264 265/* 266 * func_rmid() - check the functionality of the IPC_RMID command with shmctl() 267 */ 268static void func_rmid(void) 269{ 270 /* Do another shmctl() - we should get EINVAL */ 271 if (shmctl(shm_id_1, IPC_STAT, &buf) != -1) 272 tst_brk(TBROK, "shmctl in this function " 273 "succeeded unexpectedly"); 274 if (errno != EINVAL) 275 tst_res(TFAIL | TERRNO, "shmctl in this function failed " 276 "unexpectedly - expect errno=EINVAL, got"); 277 else 278 tst_res(TPASS, "shmctl in this function failed as expected, " 279 "shared memory appears to be removed"); 280 shm_id_1 = -1; 281} 282 283static void setup(void) 284{ 285 long hpage_size; 286 287 if (tst_hugepages == 0) 288 tst_brk(TCONF, "No enough hugepages for testing."); 289 290 hpage_size = SAFE_READ_MEMINFO("Hugepagesize:") * 1024; 291 292 shm_size = hpage_size * tst_hugepages / 2; 293 update_shm_size(&shm_size); 294 shmkey = getipckey(); 295} 296 297static void cleanup(void) 298{ 299 rm_shm(shm_id_1); 300} 301 302static struct tst_test test = { 303 .tcnt = ARRAY_SIZE(tcases), 304 .needs_root = 1, 305 .forks_child = 1, 306 .options = (struct tst_option[]) { 307 {"s:", &nr_opt, "Set the number of the been allocated hugepages"}, 308 {} 309 }, 310 .setup = setup, 311 .cleanup = cleanup, 312 .test = test_hugeshmctl, 313 .needs_checkpoints = 1, 314 .hugepages = {128, TST_REQUEST}, 315}; 316