Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

kselftests: introduce new epoll60 testcase for catching lost wakeups

This test case catches lost wake up introduced by commit 339ddb53d373
("fs/epoll: remove unnecessary wakeups of nested epoll")

The test is simple: we have 10 threads and 10 event fds. Each thread
can harvest only 1 event. 1 producer fires all 10 events at once and
waits that all 10 events will be observed by 10 threads.

In case of lost wakeup epoll_wait() will timeout and 0 will be returned.

Test case catches two sort of problems: forgotten wakeup on event, which
hits the ->ovflist list, this problem was fixed by:

5a2513239750 ("eventpoll: fix missing wakeup for ovflist in ep_poll_callback")

the other problem is when several sequential events hit the same waiting
thread, thus other waiters get no wakeups. Problem is fixed in the
following patch.

Signed-off-by: Roman Penyaev <rpenyaev@suse.de>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Cc: Khazhismel Kumykov <khazhy@google.com>
Cc: Alexander Viro <viro@zeniv.linux.org.uk>
Cc: Heiher <r@hev.cc>
Cc: Jason Baron <jbaron@akamai.com>
Link: http://lkml.kernel.org/r/20200430130326.1368509-1-rpenyaev@suse.de
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by

Roman Penyaev and committed by
Linus Torvalds
474328c0 28307d93

+146
+146
tools/testing/selftests/filesystems/epoll/epoll_wakeup_test.c
··· 3 3 #define _GNU_SOURCE 4 4 #include <poll.h> 5 5 #include <unistd.h> 6 + #include <assert.h> 6 7 #include <signal.h> 7 8 #include <pthread.h> 8 9 #include <sys/epoll.h> ··· 3135 3134 } 3136 3135 close(ctx.efd[0]); 3137 3136 close(ctx.sfd[0]); 3137 + } 3138 + 3139 + enum { 3140 + EPOLL60_EVENTS_NR = 10, 3141 + }; 3142 + 3143 + struct epoll60_ctx { 3144 + volatile int stopped; 3145 + int ready; 3146 + int waiters; 3147 + int epfd; 3148 + int evfd[EPOLL60_EVENTS_NR]; 3149 + }; 3150 + 3151 + static void *epoll60_wait_thread(void *ctx_) 3152 + { 3153 + struct epoll60_ctx *ctx = ctx_; 3154 + struct epoll_event e; 3155 + sigset_t sigmask; 3156 + uint64_t v; 3157 + int ret; 3158 + 3159 + /* Block SIGUSR1 */ 3160 + sigemptyset(&sigmask); 3161 + sigaddset(&sigmask, SIGUSR1); 3162 + sigprocmask(SIG_SETMASK, &sigmask, NULL); 3163 + 3164 + /* Prepare empty mask for epoll_pwait() */ 3165 + sigemptyset(&sigmask); 3166 + 3167 + while (!ctx->stopped) { 3168 + /* Mark we are ready */ 3169 + __atomic_fetch_add(&ctx->ready, 1, __ATOMIC_ACQUIRE); 3170 + 3171 + /* Start when all are ready */ 3172 + while (__atomic_load_n(&ctx->ready, __ATOMIC_ACQUIRE) && 3173 + !ctx->stopped); 3174 + 3175 + /* Account this waiter */ 3176 + __atomic_fetch_add(&ctx->waiters, 1, __ATOMIC_ACQUIRE); 3177 + 3178 + ret = epoll_pwait(ctx->epfd, &e, 1, 2000, &sigmask); 3179 + if (ret != 1) { 3180 + /* We expect only signal delivery on stop */ 3181 + assert(ret < 0 && errno == EINTR && "Lost wakeup!\n"); 3182 + assert(ctx->stopped); 3183 + break; 3184 + } 3185 + 3186 + ret = read(e.data.fd, &v, sizeof(v)); 3187 + /* Since we are on ET mode, thus each thread gets its own fd. */ 3188 + assert(ret == sizeof(v)); 3189 + 3190 + __atomic_fetch_sub(&ctx->waiters, 1, __ATOMIC_RELEASE); 3191 + } 3192 + 3193 + return NULL; 3194 + } 3195 + 3196 + static inline unsigned long long msecs(void) 3197 + { 3198 + struct timespec ts; 3199 + unsigned long long msecs; 3200 + 3201 + clock_gettime(CLOCK_REALTIME, &ts); 3202 + msecs = ts.tv_sec * 1000ull; 3203 + msecs += ts.tv_nsec / 1000000ull; 3204 + 3205 + return msecs; 3206 + } 3207 + 3208 + static inline int count_waiters(struct epoll60_ctx *ctx) 3209 + { 3210 + return __atomic_load_n(&ctx->waiters, __ATOMIC_ACQUIRE); 3211 + } 3212 + 3213 + TEST(epoll60) 3214 + { 3215 + struct epoll60_ctx ctx = { 0 }; 3216 + pthread_t waiters[ARRAY_SIZE(ctx.evfd)]; 3217 + struct epoll_event e; 3218 + int i, n, ret; 3219 + 3220 + signal(SIGUSR1, signal_handler); 3221 + 3222 + ctx.epfd = epoll_create1(0); 3223 + ASSERT_GE(ctx.epfd, 0); 3224 + 3225 + /* Create event fds */ 3226 + for (i = 0; i < ARRAY_SIZE(ctx.evfd); i++) { 3227 + ctx.evfd[i] = eventfd(0, EFD_NONBLOCK); 3228 + ASSERT_GE(ctx.evfd[i], 0); 3229 + 3230 + e.events = EPOLLIN | EPOLLET; 3231 + e.data.fd = ctx.evfd[i]; 3232 + ASSERT_EQ(epoll_ctl(ctx.epfd, EPOLL_CTL_ADD, ctx.evfd[i], &e), 0); 3233 + } 3234 + 3235 + /* Create waiter threads */ 3236 + for (i = 0; i < ARRAY_SIZE(waiters); i++) 3237 + ASSERT_EQ(pthread_create(&waiters[i], NULL, 3238 + epoll60_wait_thread, &ctx), 0); 3239 + 3240 + for (i = 0; i < 300; i++) { 3241 + uint64_t v = 1, ms; 3242 + 3243 + /* Wait for all to be ready */ 3244 + while (__atomic_load_n(&ctx.ready, __ATOMIC_ACQUIRE) != 3245 + ARRAY_SIZE(ctx.evfd)) 3246 + ; 3247 + 3248 + /* Steady, go */ 3249 + __atomic_fetch_sub(&ctx.ready, ARRAY_SIZE(ctx.evfd), 3250 + __ATOMIC_ACQUIRE); 3251 + 3252 + /* Wait all have gone to kernel */ 3253 + while (count_waiters(&ctx) != ARRAY_SIZE(ctx.evfd)) 3254 + ; 3255 + 3256 + /* 1ms should be enough to schedule away */ 3257 + usleep(1000); 3258 + 3259 + /* Quickly signal all handles at once */ 3260 + for (n = 0; n < ARRAY_SIZE(ctx.evfd); n++) { 3261 + ret = write(ctx.evfd[n], &v, sizeof(v)); 3262 + ASSERT_EQ(ret, sizeof(v)); 3263 + } 3264 + 3265 + /* Busy loop for 1s and wait for all waiters to wake up */ 3266 + ms = msecs(); 3267 + while (count_waiters(&ctx) && msecs() < ms + 1000) 3268 + ; 3269 + 3270 + ASSERT_EQ(count_waiters(&ctx), 0); 3271 + } 3272 + ctx.stopped = 1; 3273 + /* Stop waiters */ 3274 + for (i = 0; i < ARRAY_SIZE(waiters); i++) 3275 + ret = pthread_kill(waiters[i], SIGUSR1); 3276 + for (i = 0; i < ARRAY_SIZE(waiters); i++) 3277 + pthread_join(waiters[i], NULL); 3278 + 3279 + for (i = 0; i < ARRAY_SIZE(waiters); i++) 3280 + close(ctx.evfd[i]); 3281 + close(ctx.epfd); 3138 3282 } 3139 3283 3140 3284 TEST_HARNESS_MAIN