aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2013-05-05 13:13:44 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2013-05-05 13:13:44 -0400
commit802d0db827eaa5a34dd655623c71134ec63d8c3f (patch)
treef5115c13e279f365b94d9508b909a16ef0361abe
parent1aaf6d3d3d1e95f4be07e32dd84aa1c93855fbbd (diff)
parent941b0304a74b240c607ff098401fd4ef70c9d1cc (diff)
Merge branch 'ipc-cleanups'
Merge ipc fixes and cleanups from my IPC branch. The ipc locking has always been pretty ugly, and the scalability fixes to some degree made it even less readable. We had two cases of double unlocks in error paths due to this (one rcu read unlock, one semaphore unlock), and this fixes the bugs I found while trying to clean things up a bit so that we are less likely to have more. * ipc-cleanups: ipc: simplify rcu_read_lock() in semctl_nolock() ipc: simplify semtimedop/semctl_main() common error path handling ipc: move sem_obtain_lock() rcu locking into the only caller ipc: fix double sem unlock in semctl error path ipc: move the rcu_read_lock() from sem_lock_and_putref() into callers ipc: sem_putref() does not need the semaphore lock any more ipc: move rcu_read_unlock() out of sem_unlock() and into callers
-rw-r--r--ipc/sem.c86
1 files changed, 42 insertions, 44 deletions
diff --git a/ipc/sem.c b/ipc/sem.c
index 4734e9c2a98a..899b598b63be 100644
--- a/ipc/sem.c
+++ b/ipc/sem.c
@@ -264,12 +264,13 @@ static inline void sem_unlock(struct sem_array *sma, int locknum)
264 struct sem *sem = sma->sem_base + locknum; 264 struct sem *sem = sma->sem_base + locknum;
265 spin_unlock(&sem->lock); 265 spin_unlock(&sem->lock);
266 } 266 }
267 rcu_read_unlock();
268} 267}
269 268
270/* 269/*
271 * sem_lock_(check_) routines are called in the paths where the rw_mutex 270 * sem_lock_(check_) routines are called in the paths where the rw_mutex
272 * is not held. 271 * is not held.
272 *
273 * The caller holds the RCU read lock.
273 */ 274 */
274static inline struct sem_array *sem_obtain_lock(struct ipc_namespace *ns, 275static inline struct sem_array *sem_obtain_lock(struct ipc_namespace *ns,
275 int id, struct sembuf *sops, int nsops, int *locknum) 276 int id, struct sembuf *sops, int nsops, int *locknum)
@@ -277,12 +278,9 @@ static inline struct sem_array *sem_obtain_lock(struct ipc_namespace *ns,
277 struct kern_ipc_perm *ipcp; 278 struct kern_ipc_perm *ipcp;
278 struct sem_array *sma; 279 struct sem_array *sma;
279 280
280 rcu_read_lock();
281 ipcp = ipc_obtain_object(&sem_ids(ns), id); 281 ipcp = ipc_obtain_object(&sem_ids(ns), id);
282 if (IS_ERR(ipcp)) { 282 if (IS_ERR(ipcp))
283 sma = ERR_CAST(ipcp); 283 return ERR_CAST(ipcp);
284 goto err;
285 }
286 284
287 sma = container_of(ipcp, struct sem_array, sem_perm); 285 sma = container_of(ipcp, struct sem_array, sem_perm);
288 *locknum = sem_lock(sma, sops, nsops); 286 *locknum = sem_lock(sma, sops, nsops);
@@ -294,10 +292,7 @@ static inline struct sem_array *sem_obtain_lock(struct ipc_namespace *ns,
294 return container_of(ipcp, struct sem_array, sem_perm); 292 return container_of(ipcp, struct sem_array, sem_perm);
295 293
296 sem_unlock(sma, *locknum); 294 sem_unlock(sma, *locknum);
297 sma = ERR_PTR(-EINVAL); 295 return ERR_PTR(-EINVAL);
298err:
299 rcu_read_unlock();
300 return sma;
301} 296}
302 297
303static inline struct sem_array *sem_obtain_object(struct ipc_namespace *ns, int id) 298static inline struct sem_array *sem_obtain_object(struct ipc_namespace *ns, int id)
@@ -323,15 +318,13 @@ static inline struct sem_array *sem_obtain_object_check(struct ipc_namespace *ns
323 318
324static inline void sem_lock_and_putref(struct sem_array *sma) 319static inline void sem_lock_and_putref(struct sem_array *sma)
325{ 320{
326 rcu_read_lock();
327 sem_lock(sma, NULL, -1); 321 sem_lock(sma, NULL, -1);
328 ipc_rcu_putref(sma); 322 ipc_rcu_putref(sma);
329} 323}
330 324
331static inline void sem_putref(struct sem_array *sma) 325static inline void sem_putref(struct sem_array *sma)
332{ 326{
333 sem_lock_and_putref(sma); 327 ipc_rcu_putref(sma);
334 sem_unlock(sma, -1);
335} 328}
336 329
337static inline void sem_rmid(struct ipc_namespace *ns, struct sem_array *s) 330static inline void sem_rmid(struct ipc_namespace *ns, struct sem_array *s)
@@ -435,6 +428,7 @@ static int newary(struct ipc_namespace *ns, struct ipc_params *params)
435 sma->sem_nsems = nsems; 428 sma->sem_nsems = nsems;
436 sma->sem_ctime = get_seconds(); 429 sma->sem_ctime = get_seconds();
437 sem_unlock(sma, -1); 430 sem_unlock(sma, -1);
431 rcu_read_unlock();
438 432
439 return sma->sem_perm.id; 433 return sma->sem_perm.id;
440} 434}
@@ -874,6 +868,7 @@ static void freeary(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp)
874 /* Remove the semaphore set from the IDR */ 868 /* Remove the semaphore set from the IDR */
875 sem_rmid(ns, sma); 869 sem_rmid(ns, sma);
876 sem_unlock(sma, -1); 870 sem_unlock(sma, -1);
871 rcu_read_unlock();
877 872
878 wake_up_sem_queue_do(&tasks); 873 wake_up_sem_queue_do(&tasks);
879 ns->used_sems -= sma->sem_nsems; 874 ns->used_sems -= sma->sem_nsems;
@@ -953,8 +948,8 @@ static int semctl_nolock(struct ipc_namespace *ns, int semid,
953 948
954 memset(&tbuf, 0, sizeof(tbuf)); 949 memset(&tbuf, 0, sizeof(tbuf));
955 950
951 rcu_read_lock();
956 if (cmd == SEM_STAT) { 952 if (cmd == SEM_STAT) {
957 rcu_read_lock();
958 sma = sem_obtain_object(ns, semid); 953 sma = sem_obtain_object(ns, semid);
959 if (IS_ERR(sma)) { 954 if (IS_ERR(sma)) {
960 err = PTR_ERR(sma); 955 err = PTR_ERR(sma);
@@ -962,7 +957,6 @@ static int semctl_nolock(struct ipc_namespace *ns, int semid,
962 } 957 }
963 id = sma->sem_perm.id; 958 id = sma->sem_perm.id;
964 } else { 959 } else {
965 rcu_read_lock();
966 sma = sem_obtain_object_check(ns, semid); 960 sma = sem_obtain_object_check(ns, semid);
967 if (IS_ERR(sma)) { 961 if (IS_ERR(sma)) {
968 err = PTR_ERR(sma); 962 err = PTR_ERR(sma);
@@ -1055,6 +1049,7 @@ static int semctl_setval(struct ipc_namespace *ns, int semid, int semnum,
1055 /* maybe some queued-up processes were waiting for this */ 1049 /* maybe some queued-up processes were waiting for this */
1056 do_smart_update(sma, NULL, 0, 0, &tasks); 1050 do_smart_update(sma, NULL, 0, 0, &tasks);
1057 sem_unlock(sma, -1); 1051 sem_unlock(sma, -1);
1052 rcu_read_unlock();
1058 wake_up_sem_queue_do(&tasks); 1053 wake_up_sem_queue_do(&tasks);
1059 return 0; 1054 return 0;
1060} 1055}
@@ -1081,17 +1076,12 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum,
1081 nsems = sma->sem_nsems; 1076 nsems = sma->sem_nsems;
1082 1077
1083 err = -EACCES; 1078 err = -EACCES;
1084 if (ipcperms(ns, &sma->sem_perm, 1079 if (ipcperms(ns, &sma->sem_perm, cmd == SETALL ? S_IWUGO : S_IRUGO))
1085 cmd == SETALL ? S_IWUGO : S_IRUGO)) { 1080 goto out_rcu_wakeup;
1086 rcu_read_unlock();
1087 goto out_wakeup;
1088 }
1089 1081
1090 err = security_sem_semctl(sma, cmd); 1082 err = security_sem_semctl(sma, cmd);
1091 if (err) { 1083 if (err)
1092 rcu_read_unlock(); 1084 goto out_rcu_wakeup;
1093 goto out_wakeup;
1094 }
1095 1085
1096 err = -EACCES; 1086 err = -EACCES;
1097 switch (cmd) { 1087 switch (cmd) {
@@ -1104,19 +1094,23 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum,
1104 if(nsems > SEMMSL_FAST) { 1094 if(nsems > SEMMSL_FAST) {
1105 if (!ipc_rcu_getref(sma)) { 1095 if (!ipc_rcu_getref(sma)) {
1106 sem_unlock(sma, -1); 1096 sem_unlock(sma, -1);
1097 rcu_read_unlock();
1107 err = -EIDRM; 1098 err = -EIDRM;
1108 goto out_free; 1099 goto out_free;
1109 } 1100 }
1110 sem_unlock(sma, -1); 1101 sem_unlock(sma, -1);
1102 rcu_read_unlock();
1111 sem_io = ipc_alloc(sizeof(ushort)*nsems); 1103 sem_io = ipc_alloc(sizeof(ushort)*nsems);
1112 if(sem_io == NULL) { 1104 if(sem_io == NULL) {
1113 sem_putref(sma); 1105 sem_putref(sma);
1114 return -ENOMEM; 1106 return -ENOMEM;
1115 } 1107 }
1116 1108
1109 rcu_read_lock();
1117 sem_lock_and_putref(sma); 1110 sem_lock_and_putref(sma);
1118 if (sma->sem_perm.deleted) { 1111 if (sma->sem_perm.deleted) {
1119 sem_unlock(sma, -1); 1112 sem_unlock(sma, -1);
1113 rcu_read_unlock();
1120 err = -EIDRM; 1114 err = -EIDRM;
1121 goto out_free; 1115 goto out_free;
1122 } 1116 }
@@ -1124,6 +1118,7 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum,
1124 for (i = 0; i < sma->sem_nsems; i++) 1118 for (i = 0; i < sma->sem_nsems; i++)
1125 sem_io[i] = sma->sem_base[i].semval; 1119 sem_io[i] = sma->sem_base[i].semval;
1126 sem_unlock(sma, -1); 1120 sem_unlock(sma, -1);
1121 rcu_read_unlock();
1127 err = 0; 1122 err = 0;
1128 if(copy_to_user(array, sem_io, nsems*sizeof(ushort))) 1123 if(copy_to_user(array, sem_io, nsems*sizeof(ushort)))
1129 err = -EFAULT; 1124 err = -EFAULT;
@@ -1161,9 +1156,11 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum,
1161 goto out_free; 1156 goto out_free;
1162 } 1157 }
1163 } 1158 }
1159 rcu_read_lock();
1164 sem_lock_and_putref(sma); 1160 sem_lock_and_putref(sma);
1165 if (sma->sem_perm.deleted) { 1161 if (sma->sem_perm.deleted) {
1166 sem_unlock(sma, -1); 1162 sem_unlock(sma, -1);
1163 rcu_read_unlock();
1167 err = -EIDRM; 1164 err = -EIDRM;
1168 goto out_free; 1165 goto out_free;
1169 } 1166 }
@@ -1185,10 +1182,8 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum,
1185 /* GETVAL, GETPID, GETNCTN, GETZCNT: fall-through */ 1182 /* GETVAL, GETPID, GETNCTN, GETZCNT: fall-through */
1186 } 1183 }
1187 err = -EINVAL; 1184 err = -EINVAL;
1188 if (semnum < 0 || semnum >= nsems) { 1185 if (semnum < 0 || semnum >= nsems)
1189 rcu_read_unlock(); 1186 goto out_rcu_wakeup;
1190 goto out_wakeup;
1191 }
1192 1187
1193 sem_lock(sma, NULL, -1); 1188 sem_lock(sma, NULL, -1);
1194 curr = &sma->sem_base[semnum]; 1189 curr = &sma->sem_base[semnum];
@@ -1210,7 +1205,8 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum,
1210 1205
1211out_unlock: 1206out_unlock:
1212 sem_unlock(sma, -1); 1207 sem_unlock(sma, -1);
1213out_wakeup: 1208out_rcu_wakeup:
1209 rcu_read_unlock();
1214 wake_up_sem_queue_do(&tasks); 1210 wake_up_sem_queue_do(&tasks);
1215out_free: 1211out_free:
1216 if(sem_io != fast_sem_io) 1212 if(sem_io != fast_sem_io)
@@ -1272,7 +1268,7 @@ static int semctl_down(struct ipc_namespace *ns, int semid,
1272 err = security_sem_semctl(sma, cmd); 1268 err = security_sem_semctl(sma, cmd);
1273 if (err) { 1269 if (err) {
1274 rcu_read_unlock(); 1270 rcu_read_unlock();
1275 goto out_unlock; 1271 goto out_up;
1276 } 1272 }
1277 1273
1278 switch(cmd){ 1274 switch(cmd){
@@ -1295,6 +1291,7 @@ static int semctl_down(struct ipc_namespace *ns, int semid,
1295 1291
1296out_unlock: 1292out_unlock:
1297 sem_unlock(sma, -1); 1293 sem_unlock(sma, -1);
1294 rcu_read_unlock();
1298out_up: 1295out_up:
1299 up_write(&sem_ids(ns).rw_mutex); 1296 up_write(&sem_ids(ns).rw_mutex);
1300 return err; 1297 return err;
@@ -1443,9 +1440,11 @@ static struct sem_undo *find_alloc_undo(struct ipc_namespace *ns, int semid)
1443 } 1440 }
1444 1441
1445 /* step 3: Acquire the lock on semaphore array */ 1442 /* step 3: Acquire the lock on semaphore array */
1443 rcu_read_lock();
1446 sem_lock_and_putref(sma); 1444 sem_lock_and_putref(sma);
1447 if (sma->sem_perm.deleted) { 1445 if (sma->sem_perm.deleted) {
1448 sem_unlock(sma, -1); 1446 sem_unlock(sma, -1);
1447 rcu_read_unlock();
1449 kfree(new); 1448 kfree(new);
1450 un = ERR_PTR(-EIDRM); 1449 un = ERR_PTR(-EIDRM);
1451 goto out; 1450 goto out;
@@ -1472,7 +1471,6 @@ static struct sem_undo *find_alloc_undo(struct ipc_namespace *ns, int semid)
1472 1471
1473success: 1472success:
1474 spin_unlock(&ulp->lock); 1473 spin_unlock(&ulp->lock);
1475 rcu_read_lock();
1476 sem_unlock(sma, -1); 1474 sem_unlock(sma, -1);
1477out: 1475out:
1478 return un; 1476 return un;
@@ -1579,22 +1577,16 @@ SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops,
1579 } 1577 }
1580 1578
1581 error = -EFBIG; 1579 error = -EFBIG;
1582 if (max >= sma->sem_nsems) { 1580 if (max >= sma->sem_nsems)
1583 rcu_read_unlock(); 1581 goto out_rcu_wakeup;
1584 goto out_wakeup;
1585 }
1586 1582
1587 error = -EACCES; 1583 error = -EACCES;
1588 if (ipcperms(ns, &sma->sem_perm, alter ? S_IWUGO : S_IRUGO)) { 1584 if (ipcperms(ns, &sma->sem_perm, alter ? S_IWUGO : S_IRUGO))
1589 rcu_read_unlock(); 1585 goto out_rcu_wakeup;
1590 goto out_wakeup;
1591 }
1592 1586
1593 error = security_sem_semop(sma, sops, nsops, alter); 1587 error = security_sem_semop(sma, sops, nsops, alter);
1594 if (error) { 1588 if (error)
1595 rcu_read_unlock(); 1589 goto out_rcu_wakeup;
1596 goto out_wakeup;
1597 }
1598 1590
1599 /* 1591 /*
1600 * semid identifiers are not unique - find_alloc_undo may have 1592 * semid identifiers are not unique - find_alloc_undo may have
@@ -1648,6 +1640,7 @@ SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops,
1648sleep_again: 1640sleep_again:
1649 current->state = TASK_INTERRUPTIBLE; 1641 current->state = TASK_INTERRUPTIBLE;
1650 sem_unlock(sma, locknum); 1642 sem_unlock(sma, locknum);
1643 rcu_read_unlock();
1651 1644
1652 if (timeout) 1645 if (timeout)
1653 jiffies_left = schedule_timeout(jiffies_left); 1646 jiffies_left = schedule_timeout(jiffies_left);
@@ -1669,6 +1662,7 @@ sleep_again:
1669 goto out_free; 1662 goto out_free;
1670 } 1663 }
1671 1664
1665 rcu_read_lock();
1672 sma = sem_obtain_lock(ns, semid, sops, nsops, &locknum); 1666 sma = sem_obtain_lock(ns, semid, sops, nsops, &locknum);
1673 1667
1674 /* 1668 /*
@@ -1680,6 +1674,7 @@ sleep_again:
1680 * Array removed? If yes, leave without sem_unlock(). 1674 * Array removed? If yes, leave without sem_unlock().
1681 */ 1675 */
1682 if (IS_ERR(sma)) { 1676 if (IS_ERR(sma)) {
1677 rcu_read_unlock();
1683 goto out_free; 1678 goto out_free;
1684 } 1679 }
1685 1680
@@ -1709,7 +1704,8 @@ sleep_again:
1709 1704
1710out_unlock_free: 1705out_unlock_free:
1711 sem_unlock(sma, locknum); 1706 sem_unlock(sma, locknum);
1712out_wakeup: 1707out_rcu_wakeup:
1708 rcu_read_unlock();
1713 wake_up_sem_queue_do(&tasks); 1709 wake_up_sem_queue_do(&tasks);
1714out_free: 1710out_free:
1715 if(sops != fast_sops) 1711 if(sops != fast_sops)
@@ -1801,6 +1797,7 @@ void exit_sem(struct task_struct *tsk)
1801 * exactly the same semid. Nothing to do. 1797 * exactly the same semid. Nothing to do.
1802 */ 1798 */
1803 sem_unlock(sma, -1); 1799 sem_unlock(sma, -1);
1800 rcu_read_unlock();
1804 continue; 1801 continue;
1805 } 1802 }
1806 1803
@@ -1841,6 +1838,7 @@ void exit_sem(struct task_struct *tsk)
1841 INIT_LIST_HEAD(&tasks); 1838 INIT_LIST_HEAD(&tasks);
1842 do_smart_update(sma, NULL, 0, 1, &tasks); 1839 do_smart_update(sma, NULL, 0, 1, &tasks);
1843 sem_unlock(sma, -1); 1840 sem_unlock(sma, -1);
1841 rcu_read_unlock();
1844 wake_up_sem_queue_do(&tasks); 1842 wake_up_sem_queue_do(&tasks);
1845 1843
1846 kfree_rcu(un, rcu); 1844 kfree_rcu(un, rcu);