[1/3] syscalls/tgkill01: add new test

Message ID 1551943728-16222-2-git-send-email-sumit.garg@linaro.org
State Superseded
Headers show
Series
  • syscalls: add tgkill test-cases
Related show

Commit Message

Sumit Garg March 7, 2019, 7:28 a.m.
From: Greg Hackmann <ghackmann@google.com>

tgkill() delivers a signal to a specific thread.  Test this by
installing a SIGUSR1 handler which records the current pthread ID.
Start a number of threads in parallel, then one-by-one call tgkill(...,
tid, SIGUSR1) and check that the expected pthread ID was recorded.

Signed-off-by: Greg Hackmann <ghackmann@google.com>
Signed-off-by: Sumit Garg <sumit.garg@linaro.org>
---
 runtest/syscalls                            |   2 +
 testcases/kernel/syscalls/tgkill/.gitignore |   1 +
 testcases/kernel/syscalls/tgkill/Makefile   |  10 ++
 testcases/kernel/syscalls/tgkill/tgkill.h   |  22 +++++
 testcases/kernel/syscalls/tgkill/tgkill01.c | 138 ++++++++++++++++++++++++++++
 5 files changed, 173 insertions(+)
 create mode 100644 testcases/kernel/syscalls/tgkill/.gitignore
 create mode 100644 testcases/kernel/syscalls/tgkill/Makefile
 create mode 100644 testcases/kernel/syscalls/tgkill/tgkill.h
 create mode 100644 testcases/kernel/syscalls/tgkill/tgkill01.c

Comments

Li Wang March 12, 2019, 1:29 p.m. | #1
On Thu, Mar 7, 2019 at 3:29 PM Sumit Garg <sumit.garg@linaro.org> wrote:

> From: Greg Hackmann <ghackmann@google.com>

>

> tgkill() delivers a signal to a specific thread.  Test this by

> installing a SIGUSR1 handler which records the current pthread ID.

> Start a number of threads in parallel, then one-by-one call tgkill(...,

> tid, SIGUSR1) and check that the expected pthread ID was recorded.

>

> Signed-off-by: Greg Hackmann <ghackmann@google.com>

> Signed-off-by: Sumit Garg <sumit.garg@linaro.org>

>

Reviewed-by: Li Wang <liwang@redhat.com>


This patch set looks pretty good to me. Just have a tiny comments for PATCH
1/3 in below.


> ---

>  runtest/syscalls                            |   2 +

>  testcases/kernel/syscalls/tgkill/.gitignore |   1 +

>  testcases/kernel/syscalls/tgkill/Makefile   |  10 ++

>  testcases/kernel/syscalls/tgkill/tgkill.h   |  22 +++++

>  testcases/kernel/syscalls/tgkill/tgkill01.c | 138

> ++++++++++++++++++++++++++++

>  5 files changed, 173 insertions(+)

>  create mode 100644 testcases/kernel/syscalls/tgkill/.gitignore

>  create mode 100644 testcases/kernel/syscalls/tgkill/Makefile

>  create mode 100644 testcases/kernel/syscalls/tgkill/tgkill.h

>  create mode 100644 testcases/kernel/syscalls/tgkill/tgkill01.c

>

> diff --git a/runtest/syscalls b/runtest/syscalls

> index b43de7a..9ed1214 100644

> --- a/runtest/syscalls

> +++ b/runtest/syscalls

> @@ -1395,6 +1395,8 @@ syslog10 syslog10

>  syslog11 syslog11

>  syslog12 syslog12

>

> +tgkill01 tgkill01

> +

>  time01 time01

>  time02 time02

>

> diff --git a/testcases/kernel/syscalls/tgkill/.gitignore

> b/testcases/kernel/syscalls/tgkill/.gitignore

> new file mode 100644

> index 0000000..f4566fd

> --- /dev/null

> +++ b/testcases/kernel/syscalls/tgkill/.gitignore

> @@ -0,0 +1 @@

> +tgkill01

> diff --git a/testcases/kernel/syscalls/tgkill/Makefile

> b/testcases/kernel/syscalls/tgkill/Makefile

> new file mode 100644

> index 0000000..a51080c

> --- /dev/null

> +++ b/testcases/kernel/syscalls/tgkill/Makefile

> @@ -0,0 +1,10 @@

> +# SPDX-License-Identifier: GPL-2.0-or-later

> +# Copyright (c) 2018 Google, Inc.

> +

> +top_srcdir             ?= ../../../..

> +

> +include $(top_srcdir)/include/mk/testcases.mk

> +

> +include $(top_srcdir)/include/mk/generic_leaf_target.mk

> +

> +LDLIBS                 += -pthread

> diff --git a/testcases/kernel/syscalls/tgkill/tgkill.h

> b/testcases/kernel/syscalls/tgkill/tgkill.h

> new file mode 100644

> index 0000000..a7d96f4

> --- /dev/null

> +++ b/testcases/kernel/syscalls/tgkill/tgkill.h

> @@ -0,0 +1,22 @@

> +// SPDX-License-Identifier: GPL-2.0-or-later

> +/*

> + * Copyright (c) 2018 Google, Inc.

> + */

> +

> +#ifndef TGKILL_H

> +#define TGKILL_H

> +

> +#include "config.h"

> +#include "lapi/syscalls.h"

> +

> +static inline int sys_tgkill(int tgid, int tid, int sig)

> +{

> +       return tst_syscall(__NR_tgkill, tgid, tid, sig);

> +}

> +

> +static inline pid_t sys_gettid(void)

> +{

> +       return tst_syscall(__NR_gettid);

> +}

> +

> +#endif /* TGKILL_H */

> diff --git a/testcases/kernel/syscalls/tgkill/tgkill01.c

> b/testcases/kernel/syscalls/tgkill/tgkill01.c

> new file mode 100644

> index 0000000..1fb1171

> --- /dev/null

> +++ b/testcases/kernel/syscalls/tgkill/tgkill01.c

> @@ -0,0 +1,138 @@

> +// SPDX-License-Identifier: GPL-2.0-or-later

> +/*

> + * Copyright (c) 2018 Google, Inc.

> + *

> + * tgkill() delivers a signal to a specific thread.  Test this by

> installing

> + * a SIGUSR1 handler which records the current pthread ID.  Start a number

> + * of threads in parallel, then one-by-one call tgkill(..., tid, SIGUSR1)

> + * and check that the expected pthread ID was recorded.

> + */

> +

> +#include <pthread.h>

> +

> +#include "tst_safe_pthread.h"

> +#include "tst_test.h"

> +#include "tgkill.h"

> +

> +struct thread_state {

> +       pthread_t thread;

> +       pid_t tid;

> +};

> +

> +static char *str_threads;

> +static int n_threads = 10;

> +static struct thread_state *threads;

> +

> +static pthread_t sigusr1_thread;

> +

> +static int test_running;

> +static pthread_cond_t test_running_cond = PTHREAD_COND_INITIALIZER;

> +static pthread_mutex_t test_running_mutex = PTHREAD_MUTEX_INITIALIZER;

> +

> +static void sigusr1_handler(int signum __attribute__((unused)))

> +{

> +       sigusr1_thread = pthread_self();

> +}

> +

> +static void *thread_func(void *arg)

> +{

> +       struct thread_state *thread = arg;

> +

> +       /**

> +        * There is no standard way to map pthread -> tid, so we will have

> the

> +        * child stash its own tid then notify the parent that the stashed

> tid

> +        * is available.

> +        */

> +       thread->tid = sys_gettid();

> +

> +       TST_CHECKPOINT_WAKE(0);

> +

> +       pthread_mutex_lock(&test_running_mutex);

> +       while (test_running)

> +               pthread_cond_wait(&test_running_cond, &test_running_mutex);

> +       pthread_mutex_unlock(&test_running_mutex);

> +

> +       return arg;

> +}

> +

> +static void start_thread(struct thread_state *thread)

> +{

> +       SAFE_PTHREAD_CREATE(&thread->thread, NULL, thread_func, thread);

> +

> +       TST_CHECKPOINT_WAIT(0);

> +}

> +

> +static void stop_threads(void)

> +{

> +       int i;

> +

> +       test_running = 0;

> +       pthread_cond_broadcast(&test_running_cond);

> +

> +       for (i = 0; i < n_threads; i++) {

> +               if (threads[i].tid == -1)

> +                       continue;

> +

> +               SAFE_PTHREAD_JOIN(threads[i].thread, NULL);

> +               threads[i].tid = -1;

> +       }

>


Maybe we can add free() to release the allocated memory here.

  if (threads)
        free(threads);



> +}

> +

> +static void run(void)

> +{

> +       int i;

> +

> +       for (i = 0; i < n_threads; i++)

> +               threads[i].tid = -1;

> +

> +       test_running = 1;

> +       for (i = 0; i < n_threads; i++)

> +               start_thread(&threads[i]);

> +

> +       for (i = 0; i < n_threads; i++) {

> +               sigusr1_thread = pthread_self();

> +

> +               TEST(sys_tgkill(getpid(), threads[i].tid, SIGUSR1));

> +               if (TST_RET) {

> +                       tst_res(TFAIL | TTERRNO, "tgkill() failed");

> +                       return;

> +               }

> +

> +               while (pthread_equal(sigusr1_thread, pthread_self()))

> +                       usleep(1000);

> +

> +               if (!pthread_equal(sigusr1_thread, threads[i].thread)) {

> +                       tst_res(TFAIL, "SIGUSR1 delivered to wrong

> thread");

> +                       return;

> +               }

> +       }

> +

> +       stop_threads();

>


Since we have set the .cleanup function as stop_threads() in tst_test
struct, I think
we can remove it here.

> +       tst_res(TPASS, "SIGUSR1 delivered to correct threads");

> +}

> +

> +static void setup(void)

> +{

> +       if (tst_parse_int(str_threads, &n_threads, 1, INT_MAX))

> +               tst_brk(TBROK, "Invalid number of threads '%s'",

> str_threads);

> +

> +       threads = SAFE_MALLOC(sizeof(*threads) * n_threads);

> +

> +       struct sigaction sigusr1 = {

> +               .sa_handler = sigusr1_handler,

> +       };

> +       SAFE_SIGACTION(SIGUSR1, &sigusr1, NULL);

> +}

> +

> +static struct tst_option options[] = {

> +       {"t:", &str_threads, "-t       Number of threads (default 10)"},

> +       {NULL, NULL, NULL},

> +};

> +

> +static struct tst_test test = {

> +       .options = options,

> +       .needs_checkpoints = 1,

> +       .setup = setup,

> +       .test_all = run,

> +       .cleanup = stop_threads,

> +};

> --

> 2.7.4

>

>

> --

> Mailing list info: https://lists.linux.it/listinfo/ltp

>



-- 
Regards,
Li Wang
<div dir="ltr"><div dir="ltr"><div dir="ltr"><div class="gmail_default" style="font-size:small"><br></div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Thu, Mar 7, 2019 at 3:29 PM Sumit Garg &lt;<a href="mailto:sumit.garg@linaro.org">sumit.garg@linaro.org</a>&gt; wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">From: Greg Hackmann &lt;<a href="mailto:ghackmann@google.com" target="_blank">ghackmann@google.com</a>&gt;<br>
<br>
tgkill() delivers a signal to a specific thread.  Test this by<br>
installing a SIGUSR1 handler which records the current pthread ID.<br>
Start a number of threads in parallel, then one-by-one call tgkill(...,<br>
tid, SIGUSR1) and check that the expected pthread ID was recorded.<br>
<br>
Signed-off-by: Greg Hackmann &lt;<a href="mailto:ghackmann@google.com" target="_blank">ghackmann@google.com</a>&gt;<br>

Signed-off-by: Sumit Garg &lt;<a href="mailto:sumit.garg@linaro.org" target="_blank">sumit.garg@linaro.org</a>&gt;<br></blockquote><div><span class="gmail_default" style="font-size:small"></span></div><div><span class="gmail_default" style="font-size:small">Reviewed-by: Li Wang &lt;<a href="mailto:liwang@redhat.com">liwang@redhat.com</a>&gt;</span></div><div><span class="gmail_default" style="font-size:small"><br></span></div><div><span class="gmail_default" style="font-size:small">This patch set looks pretty good to me. Just have a tiny comments for PATCH 1/3 in below.</span></div><div><span class="gmail_default" style="font-size:small"></span> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">

---<br>
 runtest/syscalls                            |   2 +<br>
 testcases/kernel/syscalls/tgkill/.gitignore |   1 +<br>
 testcases/kernel/syscalls/tgkill/Makefile   |  10 ++<br>
 testcases/kernel/syscalls/tgkill/tgkill.h   |  22 +++++<br>
 testcases/kernel/syscalls/tgkill/tgkill01.c | 138 ++++++++++++++++++++++++++++<br>
 5 files changed, 173 insertions(+)<br>
 create mode 100644 testcases/kernel/syscalls/tgkill/.gitignore<br>
 create mode 100644 testcases/kernel/syscalls/tgkill/Makefile<br>
 create mode 100644 testcases/kernel/syscalls/tgkill/tgkill.h<br>
 create mode 100644 testcases/kernel/syscalls/tgkill/tgkill01.c<br>
<br>
diff --git a/runtest/syscalls b/runtest/syscalls<br>
index b43de7a..9ed1214 100644<br>
--- a/runtest/syscalls<br>
+++ b/runtest/syscalls<br>
@@ -1395,6 +1395,8 @@ syslog10 syslog10<br>
 syslog11 syslog11<br>
 syslog12 syslog12<br>
<br>
+tgkill01 tgkill01<br>
+<br>
 time01 time01<br>
 time02 time02<br>
<br>
diff --git a/testcases/kernel/syscalls/tgkill/.gitignore b/testcases/kernel/syscalls/tgkill/.gitignore<br>
new file mode 100644<br>
index 0000000..f4566fd<br>
--- /dev/null<br>
+++ b/testcases/kernel/syscalls/tgkill/.gitignore<br>
@@ -0,0 +1 @@<br>
+tgkill01<br>
diff --git a/testcases/kernel/syscalls/tgkill/Makefile b/testcases/kernel/syscalls/tgkill/Makefile<br>
new file mode 100644<br>
index 0000000..a51080c<br>
--- /dev/null<br>
+++ b/testcases/kernel/syscalls/tgkill/Makefile<br>
@@ -0,0 +1,10 @@<br>
+# SPDX-License-Identifier: GPL-2.0-or-later<br>
+# Copyright (c) 2018 Google, Inc.<br>
+<br>
+top_srcdir             ?= ../../../..<br>
+<br>
+include $(top_srcdir)/include/mk/<a href="http://testcases.mk" rel="noreferrer" target="_blank">testcases.mk</a><br>
+<br>
+include $(top_srcdir)/include/mk/<a href="http://generic_leaf_target.mk" rel="noreferrer" target="_blank">generic_leaf_target.mk</a><br>
+<br>
+LDLIBS                 += -pthread<br>
diff --git a/testcases/kernel/syscalls/tgkill/tgkill.h b/testcases/kernel/syscalls/tgkill/tgkill.h<br>
new file mode 100644<br>
index 0000000..a7d96f4<br>
--- /dev/null<br>
+++ b/testcases/kernel/syscalls/tgkill/tgkill.h<br>
@@ -0,0 +1,22 @@<br>
+// SPDX-License-Identifier: GPL-2.0-or-later<br>
+/*<br>
+ * Copyright (c) 2018 Google, Inc.<br>
+ */<br>
+<br>
+#ifndef TGKILL_H<br>
+#define TGKILL_H<br>
+<br>
+#include &quot;config.h&quot;<br>
+#include &quot;lapi/syscalls.h&quot;<br>
+<br>
+static inline int sys_tgkill(int tgid, int tid, int sig)<br>
+{<br>
+       return tst_syscall(__NR_tgkill, tgid, tid, sig);<br>
+}<br>
+<br>
+static inline pid_t sys_gettid(void)<br>
+{<br>
+       return tst_syscall(__NR_gettid);<br>
+}<br>
+<br>
+#endif /* TGKILL_H */<br>
diff --git a/testcases/kernel/syscalls/tgkill/tgkill01.c b/testcases/kernel/syscalls/tgkill/tgkill01.c<br>
new file mode 100644<br>
index 0000000..1fb1171<br>
--- /dev/null<br>
+++ b/testcases/kernel/syscalls/tgkill/tgkill01.c<br>
@@ -0,0 +1,138 @@<br>
+// SPDX-License-Identifier: GPL-2.0-or-later<br>
+/*<br>
+ * Copyright (c) 2018 Google, Inc.<br>
+ *<br>
+ * tgkill() delivers a signal to a specific thread.  Test this by installing<br>
+ * a SIGUSR1 handler which records the current pthread ID.  Start a number<br>
+ * of threads in parallel, then one-by-one call tgkill(..., tid, SIGUSR1)<br>
+ * and check that the expected pthread ID was recorded.<br>
+ */<br>
+<br>
+#include &lt;pthread.h&gt;<br>
+<br>
+#include &quot;tst_safe_pthread.h&quot;<br>
+#include &quot;tst_test.h&quot;<br>
+#include &quot;tgkill.h&quot;<br>
+<br>
+struct thread_state {<br>
+       pthread_t thread;<br>
+       pid_t tid;<br>
+};<br>
+<br>
+static char *str_threads;<br>
+static int n_threads = 10;<br>
+static struct thread_state *threads;<br>
+<br>
+static pthread_t sigusr1_thread;<br>
+<br>
+static int test_running;<br>
+static pthread_cond_t test_running_cond = PTHREAD_COND_INITIALIZER;<br>
+static pthread_mutex_t test_running_mutex = PTHREAD_MUTEX_INITIALIZER;<br>
+<br>
+static void sigusr1_handler(int signum __attribute__((unused)))<br>
+{<br>
+       sigusr1_thread = pthread_self();<br>
+}<br>
+<br>
+static void *thread_func(void *arg)<br>
+{<br>
+       struct thread_state *thread = arg;<br>
+<br>
+       /**<br>
+        * There is no standard way to map pthread -&gt; tid, so we will have the<br>
+        * child stash its own tid then notify the parent that the stashed tid<br>
+        * is available.<br>
+        */<br>
+       thread-&gt;tid = sys_gettid();<br>
+<br>
+       TST_CHECKPOINT_WAKE(0);<br>
+<br>
+       pthread_mutex_lock(&amp;test_running_mutex);<br>
+       while (test_running)<br>
+               pthread_cond_wait(&amp;test_running_cond, &amp;test_running_mutex);<br>
+       pthread_mutex_unlock(&amp;test_running_mutex);<br>
+<br>
+       return arg;<br>
+}<br>
+<br>
+static void start_thread(struct thread_state *thread)<br>
+{<br>
+       SAFE_PTHREAD_CREATE(&amp;thread-&gt;thread, NULL, thread_func, thread);<br>
+<br>
+       TST_CHECKPOINT_WAIT(0);<br>
+}<br>
+<br>
+static void stop_threads(void)<br>
+{<br>
+       int i;<br>
+<br>
+       test_running = 0;<br>
+       pthread_cond_broadcast(&amp;test_running_cond);<br>
+<br>
+       for (i = 0; i &lt; n_threads; i++) {<br>
+               if (threads[i].tid == -1)<br>
+                       continue;<br>
+<br>
+               SAFE_PTHREAD_JOIN(threads[i].thread, NULL);<br>
+               threads[i].tid = -1;<br>
+       }<br></blockquote><div><br></div><div><div class="gmail_default" style="font-size:small">Maybe we can add free() to release the allocated memory here.</div><div class="gmail_default" style="font-size:small"><br></div><div class="gmail_default" style="font-size:small">  if (threads)</div><div class="gmail_default" style="font-size:small">        free(threads);</div><br></div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
+}<br>
+<br>
+static void run(void)<br>
+{<br>
+       int i;<br>
+<br>
+       for (i = 0; i &lt; n_threads; i++)<br>
+               threads[i].tid = -1;<br>
+<br>
+       test_running = 1;<br>
+       for (i = 0; i &lt; n_threads; i++)<br>
+               start_thread(&amp;threads[i]);<br>
+<br>
+       for (i = 0; i &lt; n_threads; i++) {<br>
+               sigusr1_thread = pthread_self();<br>
+<br>
+               TEST(sys_tgkill(getpid(), threads[i].tid, SIGUSR1));<br>
+               if (TST_RET) {<br>
+                       tst_res(TFAIL | TTERRNO, &quot;tgkill() failed&quot;);<br>
+                       return;<br>
+               }<br>
+<br>
+               while (pthread_equal(sigusr1_thread, pthread_self()))<br>
+                       usleep(1000);<br>
+<br>
+               if (!pthread_equal(sigusr1_thread, threads[i].thread)) {<br>
+                       tst_res(TFAIL, &quot;SIGUSR1 delivered to wrong thread&quot;);<br>
+                       return;<br>
+               }<br>
+       }<br>
+<br>
+       stop_threads();<br></blockquote><div><br></div><div class="gmail_default" style="font-size:small">Since we have set the .cleanup function as stop_threads() in tst_test struct, I think</div><div class="gmail_default" style="font-size:small">we can remove it here.</div><div class="gmail_default" style="font-size:small"></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
+       tst_res(TPASS, &quot;SIGUSR1 delivered to correct threads&quot;);<br>
+}<br>
+<br>
+static void setup(void)<br>
+{<br>
+       if (tst_parse_int(str_threads, &amp;n_threads, 1, INT_MAX))<br>
+               tst_brk(TBROK, &quot;Invalid number of threads &#39;%s&#39;&quot;, str_threads);<br>
+<br>
+       threads = SAFE_MALLOC(sizeof(*threads) * n_threads);<br>
+<br>
+       struct sigaction sigusr1 = {<br>
+               .sa_handler = sigusr1_handler,<br>
+       };<br>
+       SAFE_SIGACTION(SIGUSR1, &amp;sigusr1, NULL);<br>
+}<br>
+<br>
+static struct tst_option options[] = {<br>
+       {&quot;t:&quot;, &amp;str_threads, &quot;-t       Number of threads (default 10)&quot;},<br>
+       {NULL, NULL, NULL},<br>
+};<br>
+<br>
+static struct tst_test test = {<br>
+       .options = options,<br>
+       .needs_checkpoints = 1,<br>
+       .setup = setup,<br>
+       .test_all = run,<br>
+       .cleanup = stop_threads,<br>
+};<br>
-- <br>
2.7.4<br>
<br>
<br>
-- <br>
Mailing list info: <a href="https://lists.linux.it/listinfo/ltp" rel="noreferrer" target="_blank">https://lists.linux.it/listinfo/ltp</a><br>
</blockquote></div><br clear="all"><div><br></div>-- <br><div dir="ltr" class="gmail_signature"><div dir="ltr"><div>Regards,<br></div><div>Li Wang<br></div></div></div></div></div>
Sumit Garg March 12, 2019, 1:49 p.m. | #2
On Tue, 12 Mar 2019 at 19:00, Li Wang <liwang@redhat.com> wrote:
>
>
>
> On Thu, Mar 7, 2019 at 3:29 PM Sumit Garg <sumit.garg@linaro.org> wrote:
>>
>> From: Greg Hackmann <ghackmann@google.com>
>>
>> tgkill() delivers a signal to a specific thread.  Test this by
>> installing a SIGUSR1 handler which records the current pthread ID.
>> Start a number of threads in parallel, then one-by-one call tgkill(...,
>> tid, SIGUSR1) and check that the expected pthread ID was recorded.
>>
>> Signed-off-by: Greg Hackmann <ghackmann@google.com>
>> Signed-off-by: Sumit Garg <sumit.garg@linaro.org>
>
> Reviewed-by: Li Wang <liwang@redhat.com>
>
> This patch set looks pretty good to me. Just have a tiny comments for PATCH 1/3 in below.

Thanks for your review.

>
>>
>> ---
>>  runtest/syscalls                            |   2 +
>>  testcases/kernel/syscalls/tgkill/.gitignore |   1 +
>>  testcases/kernel/syscalls/tgkill/Makefile   |  10 ++
>>  testcases/kernel/syscalls/tgkill/tgkill.h   |  22 +++++
>>  testcases/kernel/syscalls/tgkill/tgkill01.c | 138 ++++++++++++++++++++++++++++
>>  5 files changed, 173 insertions(+)
>>  create mode 100644 testcases/kernel/syscalls/tgkill/.gitignore
>>  create mode 100644 testcases/kernel/syscalls/tgkill/Makefile
>>  create mode 100644 testcases/kernel/syscalls/tgkill/tgkill.h
>>  create mode 100644 testcases/kernel/syscalls/tgkill/tgkill01.c
>>
>> diff --git a/runtest/syscalls b/runtest/syscalls
>> index b43de7a..9ed1214 100644
>> --- a/runtest/syscalls
>> +++ b/runtest/syscalls
>> @@ -1395,6 +1395,8 @@ syslog10 syslog10
>>  syslog11 syslog11
>>  syslog12 syslog12
>>
>> +tgkill01 tgkill01
>> +
>>  time01 time01
>>  time02 time02
>>
>> diff --git a/testcases/kernel/syscalls/tgkill/.gitignore b/testcases/kernel/syscalls/tgkill/.gitignore
>> new file mode 100644
>> index 0000000..f4566fd
>> --- /dev/null
>> +++ b/testcases/kernel/syscalls/tgkill/.gitignore
>> @@ -0,0 +1 @@
>> +tgkill01
>> diff --git a/testcases/kernel/syscalls/tgkill/Makefile b/testcases/kernel/syscalls/tgkill/Makefile
>> new file mode 100644
>> index 0000000..a51080c
>> --- /dev/null
>> +++ b/testcases/kernel/syscalls/tgkill/Makefile
>> @@ -0,0 +1,10 @@
>> +# SPDX-License-Identifier: GPL-2.0-or-later
>> +# Copyright (c) 2018 Google, Inc.
>> +
>> +top_srcdir             ?= ../../../..
>> +
>> +include $(top_srcdir)/include/mk/testcases.mk
>> +
>> +include $(top_srcdir)/include/mk/generic_leaf_target.mk
>> +
>> +LDLIBS                 += -pthread
>> diff --git a/testcases/kernel/syscalls/tgkill/tgkill.h b/testcases/kernel/syscalls/tgkill/tgkill.h
>> new file mode 100644
>> index 0000000..a7d96f4
>> --- /dev/null
>> +++ b/testcases/kernel/syscalls/tgkill/tgkill.h
>> @@ -0,0 +1,22 @@
>> +// SPDX-License-Identifier: GPL-2.0-or-later
>> +/*
>> + * Copyright (c) 2018 Google, Inc.
>> + */
>> +
>> +#ifndef TGKILL_H
>> +#define TGKILL_H
>> +
>> +#include "config.h"
>> +#include "lapi/syscalls.h"
>> +
>> +static inline int sys_tgkill(int tgid, int tid, int sig)
>> +{
>> +       return tst_syscall(__NR_tgkill, tgid, tid, sig);
>> +}
>> +
>> +static inline pid_t sys_gettid(void)
>> +{
>> +       return tst_syscall(__NR_gettid);
>> +}
>> +
>> +#endif /* TGKILL_H */
>> diff --git a/testcases/kernel/syscalls/tgkill/tgkill01.c b/testcases/kernel/syscalls/tgkill/tgkill01.c
>> new file mode 100644
>> index 0000000..1fb1171
>> --- /dev/null
>> +++ b/testcases/kernel/syscalls/tgkill/tgkill01.c
>> @@ -0,0 +1,138 @@
>> +// SPDX-License-Identifier: GPL-2.0-or-later
>> +/*
>> + * Copyright (c) 2018 Google, Inc.
>> + *
>> + * tgkill() delivers a signal to a specific thread.  Test this by installing
>> + * a SIGUSR1 handler which records the current pthread ID.  Start a number
>> + * of threads in parallel, then one-by-one call tgkill(..., tid, SIGUSR1)
>> + * and check that the expected pthread ID was recorded.
>> + */
>> +
>> +#include <pthread.h>
>> +
>> +#include "tst_safe_pthread.h"
>> +#include "tst_test.h"
>> +#include "tgkill.h"
>> +
>> +struct thread_state {
>> +       pthread_t thread;
>> +       pid_t tid;
>> +};
>> +
>> +static char *str_threads;
>> +static int n_threads = 10;
>> +static struct thread_state *threads;
>> +
>> +static pthread_t sigusr1_thread;
>> +
>> +static int test_running;
>> +static pthread_cond_t test_running_cond = PTHREAD_COND_INITIALIZER;
>> +static pthread_mutex_t test_running_mutex = PTHREAD_MUTEX_INITIALIZER;
>> +
>> +static void sigusr1_handler(int signum __attribute__((unused)))
>> +{
>> +       sigusr1_thread = pthread_self();
>> +}
>> +
>> +static void *thread_func(void *arg)
>> +{
>> +       struct thread_state *thread = arg;
>> +
>> +       /**
>> +        * There is no standard way to map pthread -> tid, so we will have the
>> +        * child stash its own tid then notify the parent that the stashed tid
>> +        * is available.
>> +        */
>> +       thread->tid = sys_gettid();
>> +
>> +       TST_CHECKPOINT_WAKE(0);
>> +
>> +       pthread_mutex_lock(&test_running_mutex);
>> +       while (test_running)
>> +               pthread_cond_wait(&test_running_cond, &test_running_mutex);
>> +       pthread_mutex_unlock(&test_running_mutex);
>> +
>> +       return arg;
>> +}
>> +
>> +static void start_thread(struct thread_state *thread)
>> +{
>> +       SAFE_PTHREAD_CREATE(&thread->thread, NULL, thread_func, thread);
>> +
>> +       TST_CHECKPOINT_WAIT(0);
>> +}
>> +
>> +static void stop_threads(void)
>> +{
>> +       int i;
>> +
>> +       test_running = 0;
>> +       pthread_cond_broadcast(&test_running_cond);
>> +
>> +       for (i = 0; i < n_threads; i++) {
>> +               if (threads[i].tid == -1)
>> +                       continue;
>> +
>> +               SAFE_PTHREAD_JOIN(threads[i].thread, NULL);
>> +               threads[i].tid = -1;
>> +       }
>
>
> Maybe we can add free() to release the allocated memory here.
>
>   if (threads)
>         free(threads);
>

Ok, will add this here.

>
>>
>> +}
>> +
>> +static void run(void)
>> +{
>> +       int i;
>> +
>> +       for (i = 0; i < n_threads; i++)
>> +               threads[i].tid = -1;
>> +
>> +       test_running = 1;
>> +       for (i = 0; i < n_threads; i++)
>> +               start_thread(&threads[i]);
>> +
>> +       for (i = 0; i < n_threads; i++) {
>> +               sigusr1_thread = pthread_self();
>> +
>> +               TEST(sys_tgkill(getpid(), threads[i].tid, SIGUSR1));
>> +               if (TST_RET) {
>> +                       tst_res(TFAIL | TTERRNO, "tgkill() failed");
>> +                       return;
>> +               }
>> +
>> +               while (pthread_equal(sigusr1_thread, pthread_self()))
>> +                       usleep(1000);
>> +
>> +               if (!pthread_equal(sigusr1_thread, threads[i].thread)) {
>> +                       tst_res(TFAIL, "SIGUSR1 delivered to wrong thread");
>> +                       return;
>> +               }
>> +       }
>> +
>> +       stop_threads();
>
>
> Since we have set the .cleanup function as stop_threads() in tst_test struct, I think
> we can remove it here.

Makes sense, will remove it.

-Sumit

>>
>> +       tst_res(TPASS, "SIGUSR1 delivered to correct threads");
>> +}
>> +
>> +static void setup(void)
>> +{
>> +       if (tst_parse_int(str_threads, &n_threads, 1, INT_MAX))
>> +               tst_brk(TBROK, "Invalid number of threads '%s'", str_threads);
>> +
>> +       threads = SAFE_MALLOC(sizeof(*threads) * n_threads);
>> +
>> +       struct sigaction sigusr1 = {
>> +               .sa_handler = sigusr1_handler,
>> +       };
>> +       SAFE_SIGACTION(SIGUSR1, &sigusr1, NULL);
>> +}
>> +
>> +static struct tst_option options[] = {
>> +       {"t:", &str_threads, "-t       Number of threads (default 10)"},
>> +       {NULL, NULL, NULL},
>> +};
>> +
>> +static struct tst_test test = {
>> +       .options = options,
>> +       .needs_checkpoints = 1,
>> +       .setup = setup,
>> +       .test_all = run,
>> +       .cleanup = stop_threads,
>> +};
>> --
>> 2.7.4
>>
>>
>> --
>> Mailing list info: https://lists.linux.it/listinfo/ltp
>
>
>
> --
> Regards,
> Li Wang
Cyril Hrubis March 14, 2019, 2:04 p.m. | #3
Hi!
> +static void run(void)
> +{
> +	int i;
> +
> +	for (i = 0; i < n_threads; i++)
> +		threads[i].tid = -1;
> +
> +	test_running = 1;
> +	for (i = 0; i < n_threads; i++)
> +		start_thread(&threads[i]);

Just a minor nit these threads should actully be started in the test
setup() since we stop them in the test cleanup(), otherwise the test
breaks with the looping -i option.
Sumit Garg March 15, 2019, 9:48 a.m. | #4
On Thu, 14 Mar 2019 at 19:34, Cyril Hrubis <chrubis@suse.cz> wrote:
>
> Hi!
> > +static void run(void)
> > +{
> > +     int i;
> > +
> > +     for (i = 0; i < n_threads; i++)
> > +             threads[i].tid = -1;
> > +
> > +     test_running = 1;
> > +     for (i = 0; i < n_threads; i++)
> > +             start_thread(&threads[i]);
>
> Just a minor nit these threads should actully be started in the test
> setup() since we stop them in the test cleanup(), otherwise the test
> breaks with the looping -i option.
>

Makes sense, will fix it.

-Sumit

> --
> Cyril Hrubis
> chrubis@suse.cz

Patch

diff --git a/runtest/syscalls b/runtest/syscalls
index b43de7a..9ed1214 100644
--- a/runtest/syscalls
+++ b/runtest/syscalls
@@ -1395,6 +1395,8 @@  syslog10 syslog10
 syslog11 syslog11
 syslog12 syslog12
 
+tgkill01 tgkill01
+
 time01 time01
 time02 time02
 
diff --git a/testcases/kernel/syscalls/tgkill/.gitignore b/testcases/kernel/syscalls/tgkill/.gitignore
new file mode 100644
index 0000000..f4566fd
--- /dev/null
+++ b/testcases/kernel/syscalls/tgkill/.gitignore
@@ -0,0 +1 @@ 
+tgkill01
diff --git a/testcases/kernel/syscalls/tgkill/Makefile b/testcases/kernel/syscalls/tgkill/Makefile
new file mode 100644
index 0000000..a51080c
--- /dev/null
+++ b/testcases/kernel/syscalls/tgkill/Makefile
@@ -0,0 +1,10 @@ 
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2018 Google, Inc.
+
+top_srcdir		?= ../../../..
+
+include $(top_srcdir)/include/mk/testcases.mk
+
+include $(top_srcdir)/include/mk/generic_leaf_target.mk
+
+LDLIBS			+= -pthread
diff --git a/testcases/kernel/syscalls/tgkill/tgkill.h b/testcases/kernel/syscalls/tgkill/tgkill.h
new file mode 100644
index 0000000..a7d96f4
--- /dev/null
+++ b/testcases/kernel/syscalls/tgkill/tgkill.h
@@ -0,0 +1,22 @@ 
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2018 Google, Inc.
+ */
+
+#ifndef TGKILL_H
+#define TGKILL_H
+
+#include "config.h"
+#include "lapi/syscalls.h"
+
+static inline int sys_tgkill(int tgid, int tid, int sig)
+{
+	return tst_syscall(__NR_tgkill, tgid, tid, sig);
+}
+
+static inline pid_t sys_gettid(void)
+{
+	return tst_syscall(__NR_gettid);
+}
+
+#endif /* TGKILL_H */
diff --git a/testcases/kernel/syscalls/tgkill/tgkill01.c b/testcases/kernel/syscalls/tgkill/tgkill01.c
new file mode 100644
index 0000000..1fb1171
--- /dev/null
+++ b/testcases/kernel/syscalls/tgkill/tgkill01.c
@@ -0,0 +1,138 @@ 
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2018 Google, Inc.
+ *
+ * tgkill() delivers a signal to a specific thread.  Test this by installing
+ * a SIGUSR1 handler which records the current pthread ID.  Start a number
+ * of threads in parallel, then one-by-one call tgkill(..., tid, SIGUSR1)
+ * and check that the expected pthread ID was recorded.
+ */
+
+#include <pthread.h>
+
+#include "tst_safe_pthread.h"
+#include "tst_test.h"
+#include "tgkill.h"
+
+struct thread_state {
+	pthread_t thread;
+	pid_t tid;
+};
+
+static char *str_threads;
+static int n_threads = 10;
+static struct thread_state *threads;
+
+static pthread_t sigusr1_thread;
+
+static int test_running;
+static pthread_cond_t test_running_cond = PTHREAD_COND_INITIALIZER;
+static pthread_mutex_t test_running_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+static void sigusr1_handler(int signum __attribute__((unused)))
+{
+	sigusr1_thread = pthread_self();
+}
+
+static void *thread_func(void *arg)
+{
+	struct thread_state *thread = arg;
+
+	/**
+	 * There is no standard way to map pthread -> tid, so we will have the
+	 * child stash its own tid then notify the parent that the stashed tid
+	 * is available.
+	 */
+	thread->tid = sys_gettid();
+
+	TST_CHECKPOINT_WAKE(0);
+
+	pthread_mutex_lock(&test_running_mutex);
+	while (test_running)
+		pthread_cond_wait(&test_running_cond, &test_running_mutex);
+	pthread_mutex_unlock(&test_running_mutex);
+
+	return arg;
+}
+
+static void start_thread(struct thread_state *thread)
+{
+	SAFE_PTHREAD_CREATE(&thread->thread, NULL, thread_func, thread);
+
+	TST_CHECKPOINT_WAIT(0);
+}
+
+static void stop_threads(void)
+{
+	int i;
+
+	test_running = 0;
+	pthread_cond_broadcast(&test_running_cond);
+
+	for (i = 0; i < n_threads; i++) {
+		if (threads[i].tid == -1)
+			continue;
+
+		SAFE_PTHREAD_JOIN(threads[i].thread, NULL);
+		threads[i].tid = -1;
+	}
+}
+
+static void run(void)
+{
+	int i;
+
+	for (i = 0; i < n_threads; i++)
+		threads[i].tid = -1;
+
+	test_running = 1;
+	for (i = 0; i < n_threads; i++)
+		start_thread(&threads[i]);
+
+	for (i = 0; i < n_threads; i++) {
+		sigusr1_thread = pthread_self();
+
+		TEST(sys_tgkill(getpid(), threads[i].tid, SIGUSR1));
+		if (TST_RET) {
+			tst_res(TFAIL | TTERRNO, "tgkill() failed");
+			return;
+		}
+
+		while (pthread_equal(sigusr1_thread, pthread_self()))
+			usleep(1000);
+
+		if (!pthread_equal(sigusr1_thread, threads[i].thread)) {
+			tst_res(TFAIL, "SIGUSR1 delivered to wrong thread");
+			return;
+		}
+	}
+
+	stop_threads();
+	tst_res(TPASS, "SIGUSR1 delivered to correct threads");
+}
+
+static void setup(void)
+{
+	if (tst_parse_int(str_threads, &n_threads, 1, INT_MAX))
+		tst_brk(TBROK, "Invalid number of threads '%s'", str_threads);
+
+	threads = SAFE_MALLOC(sizeof(*threads) * n_threads);
+
+	struct sigaction sigusr1 = {
+		.sa_handler = sigusr1_handler,
+	};
+	SAFE_SIGACTION(SIGUSR1, &sigusr1, NULL);
+}
+
+static struct tst_option options[] = {
+	{"t:", &str_threads, "-t       Number of threads (default 10)"},
+	{NULL, NULL, NULL},
+};
+
+static struct tst_test test = {
+	.options = options,
+	.needs_checkpoints = 1,
+	.setup = setup,
+	.test_all = run,
+	.cleanup = stop_threads,
+};