]> git.proxmox.com Git - mirror_spl-debian.git/blob - module/splat/splat-thread.c
Fix subtle race in threads test case
[mirror_spl-debian.git] / module / splat / splat-thread.c
1 /*****************************************************************************\
2 * Copyright (C) 2007-2010 Lawrence Livermore National Security, LLC.
3 * Copyright (C) 2007 The Regents of the University of California.
4 * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
5 * Written by Brian Behlendorf <behlendorf1@llnl.gov>.
6 * UCRL-CODE-235197
7 *
8 * This file is part of the SPL, Solaris Porting Layer.
9 * For details, see <http://github.com/behlendorf/spl/>.
10 *
11 * The SPL is free software; you can redistribute it and/or modify it
12 * under the terms of the GNU General Public License as published by the
13 * Free Software Foundation; either version 2 of the License, or (at your
14 * option) any later version.
15 *
16 * The SPL is distributed in the hope that it will be useful, but WITHOUT
17 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
18 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
19 * for more details.
20 *
21 * You should have received a copy of the GNU General Public License along
22 * with the SPL. If not, see <http://www.gnu.org/licenses/>.
23 *****************************************************************************
24 * Solaris Porting LAyer Tests (SPLAT) Thread Tests.
25 \*****************************************************************************/
26
27 #include "splat-internal.h"
28
29 #define SPLAT_THREAD_NAME "thread"
30 #define SPLAT_THREAD_DESC "Kernel Thread Tests"
31
32 #define SPLAT_THREAD_TEST1_ID 0x0601
33 #define SPLAT_THREAD_TEST1_NAME "create"
34 #define SPLAT_THREAD_TEST1_DESC "Validate thread creation"
35
36 #define SPLAT_THREAD_TEST2_ID 0x0602
37 #define SPLAT_THREAD_TEST2_NAME "exit"
38 #define SPLAT_THREAD_TEST2_DESC "Validate thread exit"
39
40 #define SPLAT_THREAD_TEST_MAGIC 0x4488CC00UL
41
42 typedef struct thread_priv {
43 unsigned long tp_magic;
44 struct file *tp_file;
45 spinlock_t tp_lock;
46 wait_queue_head_t tp_waitq;
47 int tp_rc;
48 } thread_priv_t;
49
50 static int
51 splat_thread_rc(thread_priv_t *tp, int rc)
52 {
53 int ret;
54
55 spin_lock(&tp->tp_lock);
56 ret = (tp->tp_rc == rc);
57 spin_unlock(&tp->tp_lock);
58
59 return ret;
60 }
61
62 static void
63 splat_thread_work1(void *priv)
64 {
65 thread_priv_t *tp = (thread_priv_t *)priv;
66
67 spin_lock(&tp->tp_lock);
68 ASSERT(tp->tp_magic == SPLAT_THREAD_TEST_MAGIC);
69 tp->tp_rc = 1;
70 wake_up(&tp->tp_waitq);
71 spin_unlock(&tp->tp_lock);
72
73 thread_exit();
74 }
75
76 static int
77 splat_thread_test1(struct file *file, void *arg)
78 {
79 thread_priv_t tp;
80 kthread_t *thr;
81
82 tp.tp_magic = SPLAT_THREAD_TEST_MAGIC;
83 tp.tp_file = file;
84 spin_lock_init(&tp.tp_lock);
85 init_waitqueue_head(&tp.tp_waitq);
86 tp.tp_rc = 0;
87
88 thr = (kthread_t *)thread_create(NULL, 0, splat_thread_work1, &tp, 0,
89 &p0, TS_RUN, minclsyspri);
90 /* Must never fail under Solaris, but we check anyway since this
91 * can happen in the linux SPL, we may want to change this behavior */
92 if (thr == NULL)
93 return -ESRCH;
94
95 /* Sleep until the thread sets tp.tp_rc == 1 */
96 wait_event(tp.tp_waitq, splat_thread_rc(&tp, 1));
97
98 splat_vprint(file, SPLAT_THREAD_TEST1_NAME, "%s",
99 "Thread successfully started properly\n");
100 return 0;
101 }
102
103 static void
104 splat_thread_work2(void *priv)
105 {
106 thread_priv_t *tp = (thread_priv_t *)priv;
107
108 spin_lock(&tp->tp_lock);
109 ASSERT(tp->tp_magic == SPLAT_THREAD_TEST_MAGIC);
110 tp->tp_rc = 1;
111 wake_up(&tp->tp_waitq);
112 spin_unlock(&tp->tp_lock);
113
114 thread_exit();
115
116 /* The following code is unreachable when thread_exit() is
117 * working properly, which is exactly what we're testing */
118 spin_lock(&tp->tp_lock);
119 tp->tp_rc = 2;
120 wake_up(&tp->tp_waitq);
121 spin_unlock(&tp->tp_lock);
122 }
123
124 static int
125 splat_thread_test2(struct file *file, void *arg)
126 {
127 thread_priv_t tp;
128 kthread_t *thr;
129 int rc = 0;
130
131 tp.tp_magic = SPLAT_THREAD_TEST_MAGIC;
132 tp.tp_file = file;
133 spin_lock_init(&tp.tp_lock);
134 init_waitqueue_head(&tp.tp_waitq);
135 tp.tp_rc = 0;
136
137 thr = (kthread_t *)thread_create(NULL, 0, splat_thread_work2, &tp, 0,
138 &p0, TS_RUN, minclsyspri);
139 /* Must never fail under Solaris, but we check anyway since this
140 * can happen in the linux SPL, we may want to change this behavior */
141 if (thr == NULL)
142 return -ESRCH;
143
144 /* Sleep until the thread sets tp.tp_rc == 1 */
145 wait_event(tp.tp_waitq, splat_thread_rc(&tp, 1));
146
147 /* Sleep until the thread sets tp.tp_rc == 2, or until we hit
148 * the timeout. If thread exit is working properly we should
149 * hit the timeout and never see to.tp_rc == 2. */
150 rc = wait_event_timeout(tp.tp_waitq, splat_thread_rc(&tp, 2), HZ / 10);
151 if (rc > 0) {
152 rc = -EINVAL;
153 splat_vprint(file, SPLAT_THREAD_TEST2_NAME, "%s",
154 "Thread did not exit properly at thread_exit()\n");
155 } else {
156 splat_vprint(file, SPLAT_THREAD_TEST2_NAME, "%s",
157 "Thread successfully exited at thread_exit()\n");
158 }
159
160 return rc;
161 }
162
163 splat_subsystem_t *
164 splat_thread_init(void)
165 {
166 splat_subsystem_t *sub;
167
168 sub = kmalloc(sizeof(*sub), GFP_KERNEL);
169 if (sub == NULL)
170 return NULL;
171
172 memset(sub, 0, sizeof(*sub));
173 strncpy(sub->desc.name, SPLAT_THREAD_NAME, SPLAT_NAME_SIZE);
174 strncpy(sub->desc.desc, SPLAT_THREAD_DESC, SPLAT_DESC_SIZE);
175 INIT_LIST_HEAD(&sub->subsystem_list);
176 INIT_LIST_HEAD(&sub->test_list);
177 spin_lock_init(&sub->test_lock);
178 sub->desc.id = SPLAT_SUBSYSTEM_THREAD;
179
180 SPLAT_TEST_INIT(sub, SPLAT_THREAD_TEST1_NAME, SPLAT_THREAD_TEST1_DESC,
181 SPLAT_THREAD_TEST1_ID, splat_thread_test1);
182 SPLAT_TEST_INIT(sub, SPLAT_THREAD_TEST2_NAME, SPLAT_THREAD_TEST2_DESC,
183 SPLAT_THREAD_TEST2_ID, splat_thread_test2);
184
185 return sub;
186 }
187
188 void
189 splat_thread_fini(splat_subsystem_t *sub)
190 {
191 ASSERT(sub);
192 SPLAT_TEST_FINI(sub, SPLAT_THREAD_TEST2_ID);
193 SPLAT_TEST_FINI(sub, SPLAT_THREAD_TEST1_ID);
194
195 kfree(sub);
196 }
197
198 int
199 splat_thread_id(void) {
200 return SPLAT_SUBSYSTEM_THREAD;
201 }