[LTP] [PATCH 9/9] syscalls/ipc: Rewrite shmctl01
Martin Doucha
mdoucha@suse.cz
Wed Jul 22 16:50:00 CEST 2020
Hello,
redesigned test scenario looks good so the only thing blocking merge
here are the bugs in preceding patches.
On 17. 07. 20 18:34, Cyril Hrubis wrote:
> This commit rewrites the shmctl01 and only keep testcases not covered
> by the rest of the testcases.
>
> Signed-off-by: Cyril Hrubis <chrubis@suse.cz>
Reviewed-by: Martin Doucha <mdoucha@suse.cz>
> ---
> testcases/kernel/syscalls/ipc/shmctl/Makefile | 1 -
> .../kernel/syscalls/ipc/shmctl/shmctl01.c | 586 ++++++------------
> 2 files changed, 178 insertions(+), 409 deletions(-)
>
> diff --git a/testcases/kernel/syscalls/ipc/shmctl/Makefile b/testcases/kernel/syscalls/ipc/shmctl/Makefile
> index a004084ad..64d76112a 100644
> --- a/testcases/kernel/syscalls/ipc/shmctl/Makefile
> +++ b/testcases/kernel/syscalls/ipc/shmctl/Makefile
> @@ -10,7 +10,6 @@ shmctl05: LDLIBS += -lrt
>
> include $(top_srcdir)/include/mk/testcases.mk
>
> -shmctl01: LDLIBS += -lltpipc
> shmctl02 shmctl04 shmctl06: LDLIBS += -lltpnewipc
>
> include $(top_srcdir)/include/mk/generic_leaf_target.mk
> diff --git a/testcases/kernel/syscalls/ipc/shmctl/shmctl01.c b/testcases/kernel/syscalls/ipc/shmctl/shmctl01.c
> index 52bf23a40..3a39a4d74 100644
> --- a/testcases/kernel/syscalls/ipc/shmctl/shmctl01.c
> +++ b/testcases/kernel/syscalls/ipc/shmctl/shmctl01.c
> @@ -1,499 +1,269 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> /*
> * Copyright (c) International Business Machines Corp., 2001
> - *
> - * This program is free software; you can redistribute it and/or modify
> - * it under the terms of the GNU General Public License as published by
> - * the Free Software Foundation; either version 2 of the License, or
> - * (at your option) any later version.
> - *
> - * This program is distributed in the hope that it will be useful,
> - * but WITHOUT ANY WARRANTY; without even the implied warranty of
> - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
> - * the GNU General Public License for more details.
> - *
> - * You should have received a copy of the GNU General Public License
> - * along with this program; if not, write to the Free Software
> - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
> + * Copyright (C) 2020 Cyril Hrubis <chrubis@suse.cz>
> */
> -
> /*
> - * NAME
> - * shmctl01.c
> + * Verify that shmctl() IPC_STAT and SHM_STAT reports correct data.
> + *
> + * The shm_nattach is excercised by:
> *
> - * DESCRIPTION
> - * shmctl01 - test the IPC_STAT, IPC_SET and IPC_RMID commands as
> - * they are used with shmctl()
> + * 1. forking() children that attach and detach SHM
> + * 2. attaching the SHM before fork and letting the children detach it
> *
> - * ALGORITHM
> - * loop if that option was specified
> - * create a shared memory segment with read and write permission
> - * set up any test case specific conditions
> - * call shmctl() using the TEST macro
> - * check the return code
> - * if failure, issue a FAIL message.
> - * otherwise,
> - * if doing functionality testing
> - * call the correct test function
> - * if the conditions are correct,
> - * issue a PASS message
> - * otherwise
> - * issue a FAIL message
> - * otherwise
> - * issue a PASS message
> - * call cleanup
> + * We check that the number shm_nattach is correct after each step we do.
> */
>
> -#ifndef _GNU_SOURCE
> #define _GNU_SOURCE
> -#endif
> -#include "ipcshm.h"
> -#include "safe_macros.h"
> -
> -char *TCID = "shmctl01";
> -
> -static int shm_id_1 = -1;
> -static int shm_index;
> -static struct shmid_ds buf;
> -static struct shminfo info;
> -static long save_time;
> -
> -#define FIRST 0
> -#define SECOND 1
> -static int stat_time;
> -
> -static void *set_shared;
> -
> -#define N_ATTACH 4
> -
> -static pid_t pid_arr[N_ATTACH];
> -
> -/* Setup, cleanup and check routines for IPC_STAT */
> -static void stat_setup(void), func_istat(int ret);
> -static void stat_cleanup(void);
> -
> -/* Setup and check routines for IPC_SET */
> -static void set_setup(void), func_set(int ret);
> -
> -/* Check routine for IPC_INFO */
> -static void func_info(int ret);
> -
> -/* Check routine for SHM_STAT */
> -static void func_sstat(int ret);
> -static void func_sstat_setup(void);
> -
> -/* Check routine for SHM_LOCK */
> -static void func_lock(int ret);
> -
> -/* Check routine for SHM_UNLOCK */
> -static void func_unlock(int ret);
> -
> -/* Check routine for IPC_RMID */
> -static void func_rmid(int ret);
> -
> -/* Child function */
> -static void do_child(void);
> -
> -static struct test_case_t {
> - int *shmid;
> - int cmd;
> - struct shmid_ds *arg;
> - void (*func_test) (int);
> - void (*func_setup) (void);
> -} TC[] = {
> - {&shm_id_1, IPC_STAT, &buf, func_istat, stat_setup},
> -#ifndef UCLINUX
> - /*
> - * The second test is not applicable to uClinux;
> - * shared memory segments are detached on exec(),
> - * so cannot be passed to uClinux children.
> - */
> - {&shm_id_1, IPC_STAT, &buf, func_istat, stat_setup},
> -#endif
> - {&shm_id_1, IPC_SET, &buf, func_set, set_setup},
> - {&shm_id_1, IPC_INFO, (struct shmid_ds *) &info, func_info, NULL},
> - {&shm_index, SHM_STAT, &buf, func_sstat, func_sstat_setup},
> - {&shm_id_1, SHM_LOCK, NULL, func_lock, NULL},
> - {&shm_id_1, SHM_UNLOCK, NULL, func_unlock, NULL},
> - {&shm_id_1, IPC_RMID, NULL, func_rmid, NULL},
> -};
> +#include <stdlib.h>
> +#include "tst_test.h"
> +#include "tst_safe_sysv_ipc.h"
> +#include "libnewipc.h"
>
> -static int TST_TOTAL = ARRAY_SIZE(TC);
> +#define NCHILD 20
>
> -#define NEWMODE 0066
> +static pid_t children[NCHILD];
>
> -#ifdef UCLINUX
> -#define PIPE_NAME "shmctl01"
> -static char *argv0;
> -#endif
> +static int shm_id;
> +static int shm_idx;
> +static time_t ctime_min, ctime_max;
>
> -static int stat_i;
> +static void *addr;
>
> -int main(int argc, char *argv[])
> +static void attach_child(void)
> {
> - int lc;
> - int i;
> -
> - tst_parse_opts(argc, argv, NULL, NULL);
> -#ifdef UCLINUX
> - argv0 = argv[0];
> - maybe_run_child(do_child, "ddd", &stat_i, &stat_time, &shm_id_1);
> -#endif
> -
> - setup();
> -
> - for (lc = 0; TEST_LOOPING(lc); lc++) {
> - tst_count = 0;
> -
> - stat_time = FIRST;
> -
> - /*
> - * Create a shared memory segment with read and write
> - * permissions. Do this here instead of in setup()
> - * so that looping (-i) will work correctly.
> - */
> - shm_id_1 = shmget(shmkey, SHM_SIZE,
> - IPC_CREAT | IPC_EXCL | SHM_RW);
> - if (shm_id_1 == -1)
> - tst_brkm(TBROK, cleanup, "couldn't create the shared"
> - " memory segment");
> -
> - for (i = 0; i < TST_TOTAL; i++) {
> -
> - /*
> - * if needed, set up any required conditions by
> - * calling the appropriate setup function
> - */
> - if (TC[i].func_setup != NULL)
> - (*TC[i].func_setup) ();
> -
> - TEST(shmctl(*(TC[i].shmid), TC[i].cmd, TC[i].arg));
> -
> - if (TEST_RETURN == -1) {
> - tst_resm(TFAIL, "%s call failed - errno "
> - "= %d : %s", TCID, TEST_ERRNO,
> - strerror(TEST_ERRNO));
> - continue;
> - }
> - (*TC[i].func_test) (TEST_RETURN);
> - }
> - }
> + pause();
> +
> + addr = SAFE_SHMAT(shm_id, NULL, 0);
> +
> + pause();
>
> - cleanup();
> - tst_exit();
> + SAFE_SHMDT(addr);
> +
> + pause();
> +
> + exit(0);
> }
>
> -/*
> - * set_shmat() - Attach the shared memory and return the pointer. Use
> - * this seperate routine to avoid code duplication in
> - * stat_setup() below.
> - */
> -void *set_shmat(void)
> +static void detach_child(void)
> {
> - void *rval;
> -
> - /* attach the shared memory */
> - rval = shmat(shm_id_1, 0, 0);
> -
> - /*
> - * if shmat() fails, the only thing we can do is
> - * print a message to that effect.
> - */
> - if (rval == (void *)-1) {
> - tst_resm(TBROK, "shmat() failed - %s", strerror(errno));
> - cleanup();
> - }
> + pause();
> +
> + SAFE_SHMDT(addr);
>
> - return rval;
> + pause();
> +
> + exit(0);
> }
>
> -/*
> - * stat_setup() - Set up for the IPC_STAT command with shmctl().
> - * Make things interesting by forking some children
> - * that will either attach or inherit the shared memory.
> - */
> -void stat_setup(void)
> +static void fork_children(void (*child_func)(void))
> {
> - void *set_shmat();
> - pid_t pid;
> -
> - /*
> - * The first time through, let the children attach the memory.
> - * The second time through, attach the memory first and let
> - * the children inherit the memory.
> - */
> -
> - if (stat_time == SECOND)
> - /*
> - * use the global "set_shared" variable here so that
> - * it can be removed in the stat_func() routine.
> - */
> - set_shared = set_shmat();
> -
> - tst_old_flush();
> - for (stat_i = 0; stat_i < N_ATTACH; stat_i++) {
> - pid = FORK_OR_VFORK();
> - if (pid == -1)
> - tst_brkm(TBROK, cleanup, "could not fork");
> -
> - if (pid == 0) {
> -#ifdef UCLINUX
> - if (self_exec(argv0, "ddd", stat_i, stat_time,
> - shm_id_1) < 0)
> - tst_brkm(TBROK, cleanup, "could not self_exec");
> -#else
> - do_child();
> -#endif
> -
> - } else {
> - /* save the child's pid for cleanup later */
> - pid_arr[stat_i] = pid;
> - TST_PROCESS_STATE_WAIT(cleanup, pid, 'S');
> - }
> + unsigned int i;
> +
> + for (i = 0; i < NCHILD; i++) {
> + pid_t pid = SAFE_FORK();
> +
> + if (!pid)
> + child_func();
> +
> + children[i] = pid;
> }
> }
>
> -void do_child(void)
> +static void wait_for_children(void)
> {
> - void *test;
> + unsigned int i;
>
> - if (stat_time == FIRST)
> - test = set_shmat();
> - else
> - test = set_shared;
> + for (i = 0; i < NCHILD; i++)
> + TST_PROCESS_STATE_WAIT(children[i], 'S', 0);
> +}
>
> - memcpy(test, &stat_i, sizeof(stat_i));
> +static void signal_children(void)
> +{
> + unsigned int i;
>
> - /* pause until we get a signal from stat_cleanup() */
> - pause();
> + for (i = 0; i < NCHILD; i++)
> + SAFE_KILL(children[i], SIGUSR1);
> +}
>
> - /* now we're back - detach the memory and exit */
> - if (shmdt(test) == -1)
> - tst_resm(TBROK, "shmdt() failed - %d", errno);
> +static void reap_children(void)
> +{
> + unsigned int i;
>
> - tst_exit();
> + for (i = 0; i < NCHILD; i++)
> + SAFE_WAITPID(children[i], NULL, 0);
> }
>
> -/*
> - * func_istat() - check the functionality of the IPC_STAT command with shmctl()
> - * by looking at the pid of the creator, the segement size,
> - * the number of attaches and the mode.
> - */
> -void func_istat(int ret)
> +static void check_nattch(int exp_nattch, const char *msg)
> {
> - int fail = 0;
> - pid_t pid;
> + struct shmid_ds ds1;
> + struct shmid_ds ds2;
>
> - /* check perm, pid, nattach and size */
> + SAFE_SHMCTL(shm_id, IPC_STAT, &ds1);
> + SAFE_SHMCTL(shm_idx, SHM_STAT, &ds2);
>
> - pid = getpid();
> -
> - if (buf.shm_cpid != pid) {
> - tst_resm(TFAIL, "creator pid is incorrect");
> - fail = 1;
> + if (ds1.shm_nattch != ds2.shm_nattch) {
> + tst_res(TFAIL, "IPC_STAT nattch=%li SHM_STAT nattch=%li",
> + (long)ds1.shm_nattch, (long)ds2.shm_nattch);
> + return;
> }
>
> - if (!fail && buf.shm_segsz != SHM_SIZE) {
> - tst_resm(TFAIL, "segment size is incorrect");
> - fail = 1;
> + if ((int)ds1.shm_nattch == exp_nattch) {
> + tst_res(TPASS, "%s shm_nattch=%i", msg, exp_nattch);
> + return;
> }
>
> - /*
> - * The first time through, only the children attach the memory, so
> - * the attaches equal N_ATTACH + stat_time (0). The second time
> - * through, the parent attaches the memory and the children inherit
> - * that memory so the attaches equal N_ATTACH + stat_time (1).
> - */
> - if (!fail && buf.shm_nattch != N_ATTACH + stat_time) {
> - tst_resm(TFAIL, "# of attaches is incorrect - %ld",
> - buf.shm_nattch);
> - fail = 1;
> - }
> + tst_res(TFAIL, "%s shm_nattcg=%li expected %i",
> + msg, (long)ds1.shm_nattch, exp_nattch);
> +}
>
> - /* use MODE_MASK to make sure we are comparing the last 9 bits */
> - if (!fail && (buf.shm_perm.mode & MODE_MASK) !=
> - ((SHM_RW) & MODE_MASK)) {
> - tst_resm(TFAIL, "segment mode is incorrect");
> - fail = 1;
> - }
> +static void verify_shmstat_attach(void)
> +{
> + fork_children(attach_child);
> + wait_for_children();
>
> - stat_cleanup();
> + check_nattch(0, "before child shmat()");
>
> - /* save the change time for use in the next test */
> - save_time = buf.shm_ctime;
> + signal_children();
> + wait_for_children();
>
> - if (fail)
> - return;
> + check_nattch(NCHILD, "after child shmat()");
> +
> + signal_children();
> + wait_for_children();
> +
> + check_nattch(0, "after child shmdt()");
>
> - tst_resm(TPASS, "pid, size, # of attaches and mode are correct "
> - "- pass #%d", stat_time);
> + signal_children();
> + reap_children();
> }
>
> -/*
> - * stat_cleanup() - signal the children to clean up after themselves and
> - * have the parent make dessert, er, um, make that remove
> - * the shared memory that is no longer needed.
> - */
> -void stat_cleanup(void)
> +static void verify_shmstat_inherit(void)
> {
> - int i;
> + addr = SAFE_SHMAT(shm_id, NULL, 0);
>
> - /* wake up the childern so they can detach the memory and exit */
> - for (i = 0; i < N_ATTACH; i++) {
> - SAFE_KILL(cleanup, pid_arr[i], SIGUSR1);
> - }
> + fork_children(detach_child);
> + wait_for_children();
>
> - /* remove the parent's shared memory the second time through */
> - if (stat_time == SECOND) {
> - if (shmdt(set_shared) == -1)
> - tst_resm(TINFO, "shmdt() failed");
> - }
> + check_nattch(NCHILD+1, "inherited after fork()");
>
> - for (i = 0; i < N_ATTACH; i++) {
> - SAFE_WAITPID(cleanup, pid_arr[i], NULL, 0);
> - }
> + signal_children();
> + wait_for_children();
>
> - stat_time++;
> -}
> + check_nattch(1, "after child shmdt()");
>
> -/*
> - * set_setup() - set up for the IPC_SET command with shmctl()
> - */
> -void set_setup(void)
> -{
> - /* set up a new mode for the shared memory segment */
> - buf.shm_perm.mode = SHM_RW | NEWMODE;
> + SAFE_SHMDT(addr);
>
> - /* sleep for one second to get a different shm_ctime value */
> - sleep(1);
> + check_nattch(0, "after parent shmdt()");
> +
> + signal_children();
> + reap_children();
> }
>
> -/*
> - * func_set() - check the functionality of the IPC_SET command with shmctl()
> - */
> -void func_set(int ret)
> +static void check_ds(struct shmid_ds *ds, const char *desc)
> {
> - int fail = 0;
> + pid_t pid = getpid();
>
> - /* first stat the shared memory to get the new data */
> - if (shmctl(shm_id_1, IPC_STAT, &buf) == -1) {
> - tst_resm(TBROK, "stat failed in func_set()");
> - return;
> + if (ds->shm_segsz != SHM_SIZE) {
> + tst_res(TFAIL, "%s: shm_segsz=%zu, expected %i",
> + desc, ds->shm_segsz, SHM_SIZE);
> + } else {
> + tst_res(TPASS, "%s: shm_segsz=%i", desc, SHM_SIZE);
> }
>
> - if ((buf.shm_perm.mode & MODE_MASK) !=
> - ((SHM_RW | NEWMODE) & MODE_MASK)) {
> - tst_resm(TFAIL, "new mode is incorrect");
> - fail = 1;
> + if (ds->shm_cpid != pid) {
> + tst_res(TFAIL, "%s: shm_cpid=%i, expected %i",
> + desc, ds->shm_cpid, pid);
> + } else {
> + tst_res(TPASS, "%s: shm_cpid=%i", desc, pid);
> }
>
> - if (!fail && save_time >= buf.shm_ctime) {
> - tst_resm(TFAIL, "change time is incorrect");
> - fail = 1;
> + if (ds->shm_ctime < ctime_min || ds->shm_ctime > ctime_max) {
> + tst_res(TFAIL, "%s: shm_ctime=%li, expected <%li,%li>",
> + desc, ds->shm_ctime, ctime_min, ctime_max);
> + } else {
> + tst_res(TPASS, "%s: shm_ctime=%li in range <%li,%li>",
> + desc, ds->shm_ctime, ctime_min, ctime_max);
> }
> -
> - if (fail)
> - return;
> -
> - tst_resm(TPASS, "new mode and change time are correct");
> }
>
> -static void func_info(int ret)
> +static void shmstat_basic_check(void)
> {
> - if (info.shmmin != 1)
> - tst_resm(TFAIL, "value of shmmin is incorrect");
> - else
> - tst_resm(TPASS, "get correct shared memory limits");
> -}
> + struct shmid_ds ds;
>
> -static void func_sstat(int ret)
> -{
> - if (ret >= 0)
> - tst_resm(TPASS, "get correct shared memory id for index: %d",
> - shm_index);
> - else
> - tst_resm(TFAIL, "shared memory id is incorrect, index: %d",
> - shm_index);
> -}
> + memset(&ds, 0, sizeof(ds));
> + SAFE_SHMCTL(shm_id, IPC_STAT, &ds);
>
> -static void func_sstat_setup(void)
> -{
> - struct shm_info tmp;
> - int ret;
> -
> - ret = shmctl(shm_id_1, SHM_INFO, (void *)&tmp);
> - if (ret < 0)
> - tst_resm(TFAIL|TERRNO, "shmctl(SHM_INFO)");
> - else
> - shm_index = ret;
> -}
> + check_ds(&ds, "IPC_STAT");
>
> -static void func_lock(int ret)
> -{
> - if (shmctl(shm_id_1, IPC_STAT, &buf) == -1) {
> - tst_resm(TBROK, "stat failed in func_lock()");
> - return;
> - }
> + memset(&ds, 0, sizeof(ds));
> + SAFE_SHMCTL(shm_idx, SHM_STAT, &ds);
>
> - if (buf.shm_perm.mode & SHM_LOCKED)
> - tst_resm(TPASS, "SHM_LOCK is set");
> - else
> - tst_resm(TFAIL, "SHM_LOCK is cleared");
> + check_ds(&ds, "SHM_STAT");
> }
>
> -static void func_unlock(int ret)
> -{
> - if (shmctl(shm_id_1, IPC_STAT, &buf) == -1) {
> - tst_resm(TBROK, "stat failed in func_unlock()");
> - return;
> - }
> +static struct tcase {
> + void (*func)(void);
> + const char *desc;
> +} tcases[] = {
> + {shmstat_basic_check, "Basic checks"},
> + {verify_shmstat_attach, "Children attach SHM"},
> + {verify_shmstat_inherit, "Chidlren inherit SHM"},
> +};
>
> - if (buf.shm_perm.mode & SHM_LOCKED)
> - tst_resm(TFAIL, "SHM_LOCK is set");
> - else
> - tst_resm(TPASS, "SHM_LOCK is cleared");
> +static void verify_shmstat(unsigned int n)
> +{
> + tst_res(TINFO, "%s", tcases[n].desc);
> + tcases[n].func();
> }
>
> +static void dummy_sighandler(int sig)
> +{
> + (void)sig;
> +}
>
> -/*
> - * func_rmid() - check the functionality of the IPC_RMID command with shmctl()
> - */
> -void func_rmid(int ret)
> +static int get_shm_idx_from_id(int shm_id)
> {
> - /* Do another shmctl() - we should get EINVAL */
> - if (shmctl(shm_id_1, IPC_STAT, &buf) != -1)
> - tst_brkm(TBROK, cleanup, "shmctl succeeded on expected fail");
> + struct shm_info dummy;
> + struct shmid_ds dummy_ds;
> + int max_idx, i;
>
> - if (errno != EINVAL)
> - tst_resm(TFAIL, "returned unexpected errno %d", errno);
> - else
> - tst_resm(TPASS, "shared memory appears to be removed");
> + max_idx = SAFE_SHMCTL(shm_id, SHM_INFO, (void*)&dummy);
>
> - shm_id_1 = -1;
> -}
> + for (i = 0; i <= max_idx; i++) {
> + if (shmctl(i, SHM_STAT, &dummy_ds) == shm_id)
> + return i;
> + }
>
> -/*
> - * sighandler() - handle signals, in this case SIGUSR1 is the only one expected
> - */
> -void sighandler(int sig)
> -{
> - if (sig != SIGUSR1)
> - tst_resm(TBROK, "received unexpected signal %d", sig);
> + return -1;
> }
>
> -void setup(void)
> +static void setup(void)
> {
> - tst_sig(FORK, sighandler, cleanup);
> + ctime_min = time(NULL);
> + shm_id = SAFE_SHMGET(IPC_PRIVATE, SHM_SIZE, IPC_CREAT | SHM_RW);
> + ctime_max = time(NULL);
> +
> + shm_idx = get_shm_idx_from_id(shm_id);
>
> - TEST_PAUSE;
> + if (shm_idx < 0)
> + tst_brk(TBROK, "Failed to get shm_id to idx mapping");
>
> - tst_tmpdir();
> + tst_res(TINFO, "shm_id=%i maps to kernel index=%i", shm_id, shm_idx);
>
> - shmkey = getipckey();
> + SAFE_SIGNAL(SIGUSR1, dummy_sighandler);
> }
>
> -void cleanup(void)
> +static void cleanup(void)
> {
> - rm_shm(shm_id_1);
> -
> - tst_rmdir();
> + if (shm_id >= 0)
> + SAFE_SHMCTL(shm_id, IPC_RMID, NULL);
> }
> +
> +static struct tst_test test = {
> + .setup = setup,
> + .cleanup = cleanup,
> + .forks_child = 1,
> + .test = verify_shmstat,
> + .tcnt = ARRAY_SIZE(tcases),
> +};
>
--
Martin Doucha mdoucha@suse.cz
QA Engineer for Software Maintenance
SUSE LINUX, s.r.o.
CORSO IIa
Krizikova 148/34
186 00 Prague 8
Czech Republic
More information about the ltp
mailing list