Sat Sep 15 17:15:01 2012 UTC ()
In the "interlock" case (where the scheduler lock is used as the condvar
lock), we need to take the CPU interlock before releasing the CPU.
Otherwise other threads can be scheduled before we get the interlock,
leading to e.g. missed condvar wakeups.  This affected only "locks_up.c"
locking (nomen est omen?).

Also, remove various __predicts since they don't have a positive
performance impact in any setup.


(pooka)
diff -r1.28 -r1.29 src/sys/rump/librump/rumpkern/scheduler.c

cvs diff -r1.28 -r1.29 src/sys/rump/librump/rumpkern/scheduler.c (expand / switch to unified diff)

--- src/sys/rump/librump/rumpkern/scheduler.c 2012/06/22 12:45:43 1.28
+++ src/sys/rump/librump/rumpkern/scheduler.c 2012/09/15 17:15:01 1.29
@@ -1,14 +1,14 @@ @@ -1,14 +1,14 @@
1/* $NetBSD: scheduler.c,v 1.28 2012/06/22 12:45:43 rmind Exp $ */ 1/* $NetBSD: scheduler.c,v 1.29 2012/09/15 17:15:01 pooka Exp $ */
2 2
3/* 3/*
4 * Copyright (c) 2010, 2011 Antti Kantee. All Rights Reserved. 4 * Copyright (c) 2010, 2011 Antti Kantee. All Rights Reserved.
5 * 5 *
6 * Redistribution and use in source and binary forms, with or without 6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions 7 * modification, are permitted provided that the following conditions
8 * are met: 8 * are met:
9 * 1. Redistributions of source code must retain the above copyright 9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer. 10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright 11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the 12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution. 13 * documentation and/or other materials provided with the distribution.
14 * 14 *
@@ -16,27 +16,27 @@ @@ -16,27 +16,27 @@
16 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE. 25 * SUCH DAMAGE.
26 */ 26 */
27 27
28#include <sys/cdefs.h> 28#include <sys/cdefs.h>
29__KERNEL_RCSID(0, "$NetBSD: scheduler.c,v 1.28 2012/06/22 12:45:43 rmind Exp $"); 29__KERNEL_RCSID(0, "$NetBSD: scheduler.c,v 1.29 2012/09/15 17:15:01 pooka Exp $");
30 30
31#include <sys/param.h> 31#include <sys/param.h>
32#include <sys/atomic.h> 32#include <sys/atomic.h>
33#include <sys/cpu.h> 33#include <sys/cpu.h>
34#include <sys/kmem.h> 34#include <sys/kmem.h>
35#include <sys/mutex.h> 35#include <sys/mutex.h>
36#include <sys/namei.h> 36#include <sys/namei.h>
37#include <sys/queue.h> 37#include <sys/queue.h>
38#include <sys/select.h> 38#include <sys/select.h>
39#include <sys/systm.h> 39#include <sys/systm.h>
40 40
41#include <rump/rumpuser.h> 41#include <rump/rumpuser.h>
42 42
@@ -290,44 +290,44 @@ rump_schedule_cpu_interlock(struct lwp * @@ -290,44 +290,44 @@ rump_schedule_cpu_interlock(struct lwp *
290 /* 290 /*
291 * First, try fastpath: if we were the previous user of the 291 * First, try fastpath: if we were the previous user of the
292 * CPU, everything is in order cachewise and we can just 292 * CPU, everything is in order cachewise and we can just
293 * proceed to use it. 293 * proceed to use it.
294 * 294 *
295 * If we are a different thread (i.e. CAS fails), we must go 295 * If we are a different thread (i.e. CAS fails), we must go
296 * through a memory barrier to ensure we get a truthful 296 * through a memory barrier to ensure we get a truthful
297 * view of the world. 297 * view of the world.
298 */ 298 */
299 299
300 KASSERT(l->l_target_cpu != NULL); 300 KASSERT(l->l_target_cpu != NULL);
301 rcpu = &rcpu_storage[l->l_target_cpu-&rump_cpus[0]]; 301 rcpu = &rcpu_storage[l->l_target_cpu-&rump_cpus[0]];
302 if (atomic_cas_ptr(&rcpu->rcpu_prevlwp, l, RCPULWP_BUSY) == l) { 302 if (atomic_cas_ptr(&rcpu->rcpu_prevlwp, l, RCPULWP_BUSY) == l) {
303 if (__predict_true(interlock == rcpu->rcpu_mtx)) 303 if (interlock == rcpu->rcpu_mtx)
304 rumpuser_mutex_exit(rcpu->rcpu_mtx); 304 rumpuser_mutex_exit(rcpu->rcpu_mtx);
305 SCHED_FASTPATH(rcpu); 305 SCHED_FASTPATH(rcpu);
306 /* jones, you're the man */ 306 /* jones, you're the man */
307 goto fastlane; 307 goto fastlane;
308 } 308 }
309 309
310 /* 310 /*
311 * Else, it's the slowpath for us. First, determine if we 311 * Else, it's the slowpath for us. First, determine if we
312 * can migrate. 312 * can migrate.
313 */ 313 */
314 if (ncpu == 1) 314 if (ncpu == 1)
315 domigrate = false; 315 domigrate = false;
316 else 316 else
317 domigrate = true; 317 domigrate = true;
318 318
319 /* Take lock. This acts as a load barrier too. */ 319 /* Take lock. This acts as a load barrier too. */
320 if (__predict_true(interlock != rcpu->rcpu_mtx)) 320 if (interlock != rcpu->rcpu_mtx)
321 rumpuser_mutex_enter_nowrap(rcpu->rcpu_mtx); 321 rumpuser_mutex_enter_nowrap(rcpu->rcpu_mtx);
322 322
323 for (;;) { 323 for (;;) {
324 SCHED_SLOWPATH(rcpu); 324 SCHED_SLOWPATH(rcpu);
325 old = atomic_swap_ptr(&rcpu->rcpu_prevlwp, RCPULWP_WANTED); 325 old = atomic_swap_ptr(&rcpu->rcpu_prevlwp, RCPULWP_WANTED);
326 326
327 /* CPU is free? */ 327 /* CPU is free? */
328 if (old != RCPULWP_BUSY && old != RCPULWP_WANTED) { 328 if (old != RCPULWP_BUSY && old != RCPULWP_WANTED) {
329 if (atomic_cas_ptr(&rcpu->rcpu_prevlwp, 329 if (atomic_cas_ptr(&rcpu->rcpu_prevlwp,
330 RCPULWP_WANTED, RCPULWP_BUSY) == RCPULWP_WANTED) { 330 RCPULWP_WANTED, RCPULWP_BUSY) == RCPULWP_WANTED) {
331 break; 331 break;
332 } 332 }
333 } 333 }
@@ -432,53 +432,59 @@ rump_unschedule_cpu1(struct lwp *l, void @@ -432,53 +432,59 @@ rump_unschedule_cpu1(struct lwp *l, void
432 void *old; 432 void *old;
433 433
434 ci = l->l_cpu; 434 ci = l->l_cpu;
435 ci->ci_curlwp = NULL; 435 ci->ci_curlwp = NULL;
436 rcpu = &rcpu_storage[ci-&rump_cpus[0]]; 436 rcpu = &rcpu_storage[ci-&rump_cpus[0]];
437 437
438 KASSERT(rcpu->rcpu_ci == ci); 438 KASSERT(rcpu->rcpu_ci == ci);
439 439
440 /* 440 /*
441 * Make sure all stores are seen before the CPU release. This 441 * Make sure all stores are seen before the CPU release. This
442 * is relevant only in the non-fastpath scheduling case, but 442 * is relevant only in the non-fastpath scheduling case, but
443 * we don't know here if that's going to happen, so need to 443 * we don't know here if that's going to happen, so need to
444 * expect the worst. 444 * expect the worst.
 445 *
 446 * If the scheduler interlock was requested by the caller, we
 447 * need to obtain it before we release the CPU. Otherwise, we risk a
 448 * race condition where another thread is scheduled onto the
 449 * rump kernel CPU before our current thread can
 450 * grab the interlock.
445 */ 451 */
446 membar_exit(); 452 if (interlock == rcpu->rcpu_mtx)
 453 rumpuser_mutex_enter_nowrap(rcpu->rcpu_mtx);
 454 else
 455 membar_exit();
447 456
448 /* Release the CPU. */ 457 /* Release the CPU. */
449 old = atomic_swap_ptr(&rcpu->rcpu_prevlwp, l); 458 old = atomic_swap_ptr(&rcpu->rcpu_prevlwp, l);
450 459
451 /* No waiters? No problems. We're outta here. */ 460 /* No waiters? No problems. We're outta here. */
452 if (old == RCPULWP_BUSY) { 461 if (old == RCPULWP_BUSY) {
453 /* Was the scheduler interlock requested? */ 
454 if (__predict_false(interlock == rcpu->rcpu_mtx)) 
455 rumpuser_mutex_enter_nowrap(rcpu->rcpu_mtx); 
456 return; 462 return;
457 } 463 }
458 464
459 KASSERT(old == RCPULWP_WANTED); 465 KASSERT(old == RCPULWP_WANTED);
460 466
461 /* 467 /*
462 * Ok, things weren't so snappy. 468 * Ok, things weren't so snappy.
463 * 469 *
464 * Snailpath: take lock and signal anyone waiting for this CPU. 470 * Snailpath: take lock and signal anyone waiting for this CPU.
465 */ 471 */
466 472
467 rumpuser_mutex_enter_nowrap(rcpu->rcpu_mtx); 473 if (interlock != rcpu->rcpu_mtx)
 474 rumpuser_mutex_enter_nowrap(rcpu->rcpu_mtx);
468 if (rcpu->rcpu_wanted) 475 if (rcpu->rcpu_wanted)
469 rumpuser_cv_broadcast(rcpu->rcpu_cv); 476 rumpuser_cv_broadcast(rcpu->rcpu_cv);
470 477 if (interlock != rcpu->rcpu_mtx)
471 if (__predict_true(interlock != rcpu->rcpu_mtx)) 
472 rumpuser_mutex_exit(rcpu->rcpu_mtx); 478 rumpuser_mutex_exit(rcpu->rcpu_mtx);
473} 479}
474 480
475/* Give up and retake CPU (perhaps a different one) */ 481/* Give up and retake CPU (perhaps a different one) */
476void 482void
477yield() 483yield()
478{ 484{
479 struct lwp *l = curlwp; 485 struct lwp *l = curlwp;
480 int nlocks; 486 int nlocks;
481 487
482 KERNEL_UNLOCK_ALL(l, &nlocks); 488 KERNEL_UNLOCK_ALL(l, &nlocks);
483 rump_unschedule_cpu(l); 489 rump_unschedule_cpu(l);
484 rump_schedule_cpu(l); 490 rump_schedule_cpu(l);