aboutsummaryrefslogtreecommitdiffstats
path: root/ipc
diff options
context:
space:
mode:
authorManfred Spraul <manfred@colorfullife.com>2010-07-20 16:24:23 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2010-07-20 19:25:40 -0400
commitc61284e99191b2284fb74dae6961d4d09e4e59e8 (patch)
tree6b49a9e2e5ade9f395c66f6453ff60b952a6df38 /ipc
parent19f0f0af097ba3138ae2780b471180512763a78f (diff)
ipc/sem.c: bugfix for semop() not reporting successful operation
The last change to improve the scalability moved the actual wake-up out of the section that is protected by spin_lock(sma->sem_perm.lock). This means that IN_WAKEUP can be in queue.status even when the spinlock is acquired by the current task. Thus the same loop that is performed when queue.status is read without the spinlock acquired must be performed when the spinlock is acquired. Thanks to kamezawa.hiroyu@jp.fujitsu.com for noticing lack of the memory barrier. Addresses https://bugzilla.kernel.org/show_bug.cgi?id=16255 [akpm@linux-foundation.org: clean up kerneldoc, checkpatch warning and whitespace] Signed-off-by: Manfred Spraul <manfred@colorfullife.com> Reported-by: Luca Tettamanti <kronos.it@gmail.com> Tested-by: Luca Tettamanti <kronos.it@gmail.com> Reported-by: Christoph Lameter <cl@linux-foundation.org> Cc: Maciej Rutecki <maciej.rutecki@gmail.com> Cc: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'ipc')
-rw-r--r--ipc/sem.c46
1 files changed, 39 insertions, 7 deletions
diff --git a/ipc/sem.c b/ipc/sem.c
index 506c8491a8d1..40a8f462a822 100644
--- a/ipc/sem.c
+++ b/ipc/sem.c
@@ -1256,6 +1256,33 @@ out:
1256 return un; 1256 return un;
1257} 1257}
1258 1258
1259
1260/**
1261 * get_queue_result - Retrieve the result code from sem_queue
1262 * @q: Pointer to queue structure
1263 *
1264 * Retrieve the return code from the pending queue. If IN_WAKEUP is found in
1265 * q->status, then we must loop until the value is replaced with the final
1266 * value: This may happen if a task is woken up by an unrelated event (e.g.
1267 * signal) and in parallel the task is woken up by another task because it got
1268 * the requested semaphores.
1269 *
1270 * The function can be called with or without holding the semaphore spinlock.
1271 */
1272static int get_queue_result(struct sem_queue *q)
1273{
1274 int error;
1275
1276 error = q->status;
1277 while (unlikely(error == IN_WAKEUP)) {
1278 cpu_relax();
1279 error = q->status;
1280 }
1281
1282 return error;
1283}
1284
1285
1259SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops, 1286SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops,
1260 unsigned, nsops, const struct timespec __user *, timeout) 1287 unsigned, nsops, const struct timespec __user *, timeout)
1261{ 1288{
@@ -1409,15 +1436,18 @@ SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops,
1409 else 1436 else
1410 schedule(); 1437 schedule();
1411 1438
1412 error = queue.status; 1439 error = get_queue_result(&queue);
1413 while(unlikely(error == IN_WAKEUP)) {
1414 cpu_relax();
1415 error = queue.status;
1416 }
1417 1440
1418 if (error != -EINTR) { 1441 if (error != -EINTR) {
1419 /* fast path: update_queue already obtained all requested 1442 /* fast path: update_queue already obtained all requested
1420 * resources */ 1443 * resources.
1444 * Perform a smp_mb(): User space could assume that semop()
1445 * is a memory barrier: Without the mb(), the cpu could
1446 * speculatively read in user space stale data that was
1447 * overwritten by the previous owner of the semaphore.
1448 */
1449 smp_mb();
1450
1421 goto out_free; 1451 goto out_free;
1422 } 1452 }
1423 1453
@@ -1427,10 +1457,12 @@ SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops,
1427 goto out_free; 1457 goto out_free;
1428 } 1458 }
1429 1459
1460 error = get_queue_result(&queue);
1461
1430 /* 1462 /*
1431 * If queue.status != -EINTR we are woken up by another process 1463 * If queue.status != -EINTR we are woken up by another process
1432 */ 1464 */
1433 error = queue.status; 1465
1434 if (error != -EINTR) { 1466 if (error != -EINTR) {
1435 goto out_unlock_free; 1467 goto out_unlock_free;
1436 } 1468 }