diff options
Diffstat (limited to 'ipc/sem.c')
-rw-r--r-- | ipc/sem.c | 46 |
1 files changed, 39 insertions, 7 deletions
@@ -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 | */ | ||
1272 | static 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 | |||
1259 | SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops, | 1286 | SYSCALL_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 | } |