diff options
Diffstat (limited to 'ipc')
-rw-r--r-- | ipc/msg.c | 19 | ||||
-rw-r--r-- | ipc/sem.c | 34 | ||||
-rw-r--r-- | ipc/shm.c | 17 | ||||
-rw-r--r-- | ipc/util.c | 32 | ||||
-rw-r--r-- | ipc/util.h | 10 |
5 files changed, 64 insertions, 48 deletions
@@ -165,6 +165,15 @@ static inline void msg_rmid(struct ipc_namespace *ns, struct msg_queue *s) | |||
165 | ipc_rmid(&msg_ids(ns), &s->q_perm); | 165 | ipc_rmid(&msg_ids(ns), &s->q_perm); |
166 | } | 166 | } |
167 | 167 | ||
168 | static void msg_rcu_free(struct rcu_head *head) | ||
169 | { | ||
170 | struct ipc_rcu *p = container_of(head, struct ipc_rcu, rcu); | ||
171 | struct msg_queue *msq = ipc_rcu_to_struct(p); | ||
172 | |||
173 | security_msg_queue_free(msq); | ||
174 | ipc_rcu_free(head); | ||
175 | } | ||
176 | |||
168 | /** | 177 | /** |
169 | * newque - Create a new msg queue | 178 | * newque - Create a new msg queue |
170 | * @ns: namespace | 179 | * @ns: namespace |
@@ -189,15 +198,14 @@ static int newque(struct ipc_namespace *ns, struct ipc_params *params) | |||
189 | msq->q_perm.security = NULL; | 198 | msq->q_perm.security = NULL; |
190 | retval = security_msg_queue_alloc(msq); | 199 | retval = security_msg_queue_alloc(msq); |
191 | if (retval) { | 200 | if (retval) { |
192 | ipc_rcu_putref(msq); | 201 | ipc_rcu_putref(msq, ipc_rcu_free); |
193 | return retval; | 202 | return retval; |
194 | } | 203 | } |
195 | 204 | ||
196 | /* ipc_addid() locks msq upon success. */ | 205 | /* ipc_addid() locks msq upon success. */ |
197 | id = ipc_addid(&msg_ids(ns), &msq->q_perm, ns->msg_ctlmni); | 206 | id = ipc_addid(&msg_ids(ns), &msq->q_perm, ns->msg_ctlmni); |
198 | if (id < 0) { | 207 | if (id < 0) { |
199 | security_msg_queue_free(msq); | 208 | ipc_rcu_putref(msq, msg_rcu_free); |
200 | ipc_rcu_putref(msq); | ||
201 | return id; | 209 | return id; |
202 | } | 210 | } |
203 | 211 | ||
@@ -276,8 +284,7 @@ static void freeque(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp) | |||
276 | free_msg(msg); | 284 | free_msg(msg); |
277 | } | 285 | } |
278 | atomic_sub(msq->q_cbytes, &ns->msg_bytes); | 286 | atomic_sub(msq->q_cbytes, &ns->msg_bytes); |
279 | security_msg_queue_free(msq); | 287 | ipc_rcu_putref(msq, msg_rcu_free); |
280 | ipc_rcu_putref(msq); | ||
281 | } | 288 | } |
282 | 289 | ||
283 | /* | 290 | /* |
@@ -717,7 +724,7 @@ long do_msgsnd(int msqid, long mtype, void __user *mtext, | |||
717 | rcu_read_lock(); | 724 | rcu_read_lock(); |
718 | ipc_lock_object(&msq->q_perm); | 725 | ipc_lock_object(&msq->q_perm); |
719 | 726 | ||
720 | ipc_rcu_putref(msq); | 727 | ipc_rcu_putref(msq, ipc_rcu_free); |
721 | if (msq->q_perm.deleted) { | 728 | if (msq->q_perm.deleted) { |
722 | err = -EIDRM; | 729 | err = -EIDRM; |
723 | goto out_unlock0; | 730 | goto out_unlock0; |
@@ -243,6 +243,15 @@ static void merge_queues(struct sem_array *sma) | |||
243 | } | 243 | } |
244 | } | 244 | } |
245 | 245 | ||
246 | static void sem_rcu_free(struct rcu_head *head) | ||
247 | { | ||
248 | struct ipc_rcu *p = container_of(head, struct ipc_rcu, rcu); | ||
249 | struct sem_array *sma = ipc_rcu_to_struct(p); | ||
250 | |||
251 | security_sem_free(sma); | ||
252 | ipc_rcu_free(head); | ||
253 | } | ||
254 | |||
246 | /* | 255 | /* |
247 | * If the request contains only one semaphore operation, and there are | 256 | * If the request contains only one semaphore operation, and there are |
248 | * no complex transactions pending, lock only the semaphore involved. | 257 | * no complex transactions pending, lock only the semaphore involved. |
@@ -374,12 +383,7 @@ static inline struct sem_array *sem_obtain_object_check(struct ipc_namespace *ns | |||
374 | static inline void sem_lock_and_putref(struct sem_array *sma) | 383 | static inline void sem_lock_and_putref(struct sem_array *sma) |
375 | { | 384 | { |
376 | sem_lock(sma, NULL, -1); | 385 | sem_lock(sma, NULL, -1); |
377 | ipc_rcu_putref(sma); | 386 | ipc_rcu_putref(sma, ipc_rcu_free); |
378 | } | ||
379 | |||
380 | static inline void sem_putref(struct sem_array *sma) | ||
381 | { | ||
382 | ipc_rcu_putref(sma); | ||
383 | } | 387 | } |
384 | 388 | ||
385 | static inline void sem_rmid(struct ipc_namespace *ns, struct sem_array *s) | 389 | static inline void sem_rmid(struct ipc_namespace *ns, struct sem_array *s) |
@@ -458,14 +462,13 @@ static int newary(struct ipc_namespace *ns, struct ipc_params *params) | |||
458 | sma->sem_perm.security = NULL; | 462 | sma->sem_perm.security = NULL; |
459 | retval = security_sem_alloc(sma); | 463 | retval = security_sem_alloc(sma); |
460 | if (retval) { | 464 | if (retval) { |
461 | ipc_rcu_putref(sma); | 465 | ipc_rcu_putref(sma, ipc_rcu_free); |
462 | return retval; | 466 | return retval; |
463 | } | 467 | } |
464 | 468 | ||
465 | id = ipc_addid(&sem_ids(ns), &sma->sem_perm, ns->sc_semmni); | 469 | id = ipc_addid(&sem_ids(ns), &sma->sem_perm, ns->sc_semmni); |
466 | if (id < 0) { | 470 | if (id < 0) { |
467 | security_sem_free(sma); | 471 | ipc_rcu_putref(sma, sem_rcu_free); |
468 | ipc_rcu_putref(sma); | ||
469 | return id; | 472 | return id; |
470 | } | 473 | } |
471 | ns->used_sems += nsems; | 474 | ns->used_sems += nsems; |
@@ -1047,8 +1050,7 @@ static void freeary(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp) | |||
1047 | 1050 | ||
1048 | wake_up_sem_queue_do(&tasks); | 1051 | wake_up_sem_queue_do(&tasks); |
1049 | ns->used_sems -= sma->sem_nsems; | 1052 | ns->used_sems -= sma->sem_nsems; |
1050 | security_sem_free(sma); | 1053 | ipc_rcu_putref(sma, sem_rcu_free); |
1051 | ipc_rcu_putref(sma); | ||
1052 | } | 1054 | } |
1053 | 1055 | ||
1054 | static unsigned long copy_semid_to_user(void __user *buf, struct semid64_ds *in, int version) | 1056 | static unsigned long copy_semid_to_user(void __user *buf, struct semid64_ds *in, int version) |
@@ -1292,7 +1294,7 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum, | |||
1292 | rcu_read_unlock(); | 1294 | rcu_read_unlock(); |
1293 | sem_io = ipc_alloc(sizeof(ushort)*nsems); | 1295 | sem_io = ipc_alloc(sizeof(ushort)*nsems); |
1294 | if(sem_io == NULL) { | 1296 | if(sem_io == NULL) { |
1295 | sem_putref(sma); | 1297 | ipc_rcu_putref(sma, ipc_rcu_free); |
1296 | return -ENOMEM; | 1298 | return -ENOMEM; |
1297 | } | 1299 | } |
1298 | 1300 | ||
@@ -1328,20 +1330,20 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum, | |||
1328 | if(nsems > SEMMSL_FAST) { | 1330 | if(nsems > SEMMSL_FAST) { |
1329 | sem_io = ipc_alloc(sizeof(ushort)*nsems); | 1331 | sem_io = ipc_alloc(sizeof(ushort)*nsems); |
1330 | if(sem_io == NULL) { | 1332 | if(sem_io == NULL) { |
1331 | sem_putref(sma); | 1333 | ipc_rcu_putref(sma, ipc_rcu_free); |
1332 | return -ENOMEM; | 1334 | return -ENOMEM; |
1333 | } | 1335 | } |
1334 | } | 1336 | } |
1335 | 1337 | ||
1336 | if (copy_from_user (sem_io, p, nsems*sizeof(ushort))) { | 1338 | if (copy_from_user (sem_io, p, nsems*sizeof(ushort))) { |
1337 | sem_putref(sma); | 1339 | ipc_rcu_putref(sma, ipc_rcu_free); |
1338 | err = -EFAULT; | 1340 | err = -EFAULT; |
1339 | goto out_free; | 1341 | goto out_free; |
1340 | } | 1342 | } |
1341 | 1343 | ||
1342 | for (i = 0; i < nsems; i++) { | 1344 | for (i = 0; i < nsems; i++) { |
1343 | if (sem_io[i] > SEMVMX) { | 1345 | if (sem_io[i] > SEMVMX) { |
1344 | sem_putref(sma); | 1346 | ipc_rcu_putref(sma, ipc_rcu_free); |
1345 | err = -ERANGE; | 1347 | err = -ERANGE; |
1346 | goto out_free; | 1348 | goto out_free; |
1347 | } | 1349 | } |
@@ -1629,7 +1631,7 @@ static struct sem_undo *find_alloc_undo(struct ipc_namespace *ns, int semid) | |||
1629 | /* step 2: allocate new undo structure */ | 1631 | /* step 2: allocate new undo structure */ |
1630 | new = kzalloc(sizeof(struct sem_undo) + sizeof(short)*nsems, GFP_KERNEL); | 1632 | new = kzalloc(sizeof(struct sem_undo) + sizeof(short)*nsems, GFP_KERNEL); |
1631 | if (!new) { | 1633 | if (!new) { |
1632 | sem_putref(sma); | 1634 | ipc_rcu_putref(sma, ipc_rcu_free); |
1633 | return ERR_PTR(-ENOMEM); | 1635 | return ERR_PTR(-ENOMEM); |
1634 | } | 1636 | } |
1635 | 1637 | ||
@@ -167,6 +167,15 @@ static inline void shm_lock_by_ptr(struct shmid_kernel *ipcp) | |||
167 | ipc_lock_object(&ipcp->shm_perm); | 167 | ipc_lock_object(&ipcp->shm_perm); |
168 | } | 168 | } |
169 | 169 | ||
170 | static void shm_rcu_free(struct rcu_head *head) | ||
171 | { | ||
172 | struct ipc_rcu *p = container_of(head, struct ipc_rcu, rcu); | ||
173 | struct shmid_kernel *shp = ipc_rcu_to_struct(p); | ||
174 | |||
175 | security_shm_free(shp); | ||
176 | ipc_rcu_free(head); | ||
177 | } | ||
178 | |||
170 | static inline void shm_rmid(struct ipc_namespace *ns, struct shmid_kernel *s) | 179 | static inline void shm_rmid(struct ipc_namespace *ns, struct shmid_kernel *s) |
171 | { | 180 | { |
172 | ipc_rmid(&shm_ids(ns), &s->shm_perm); | 181 | ipc_rmid(&shm_ids(ns), &s->shm_perm); |
@@ -208,8 +217,7 @@ static void shm_destroy(struct ipc_namespace *ns, struct shmid_kernel *shp) | |||
208 | user_shm_unlock(file_inode(shp->shm_file)->i_size, | 217 | user_shm_unlock(file_inode(shp->shm_file)->i_size, |
209 | shp->mlock_user); | 218 | shp->mlock_user); |
210 | fput (shp->shm_file); | 219 | fput (shp->shm_file); |
211 | security_shm_free(shp); | 220 | ipc_rcu_putref(shp, shm_rcu_free); |
212 | ipc_rcu_putref(shp); | ||
213 | } | 221 | } |
214 | 222 | ||
215 | /* | 223 | /* |
@@ -497,7 +505,7 @@ static int newseg(struct ipc_namespace *ns, struct ipc_params *params) | |||
497 | shp->shm_perm.security = NULL; | 505 | shp->shm_perm.security = NULL; |
498 | error = security_shm_alloc(shp); | 506 | error = security_shm_alloc(shp); |
499 | if (error) { | 507 | if (error) { |
500 | ipc_rcu_putref(shp); | 508 | ipc_rcu_putref(shp, ipc_rcu_free); |
501 | return error; | 509 | return error; |
502 | } | 510 | } |
503 | 511 | ||
@@ -566,8 +574,7 @@ no_id: | |||
566 | user_shm_unlock(size, shp->mlock_user); | 574 | user_shm_unlock(size, shp->mlock_user); |
567 | fput(file); | 575 | fput(file); |
568 | no_file: | 576 | no_file: |
569 | security_shm_free(shp); | 577 | ipc_rcu_putref(shp, shm_rcu_free); |
570 | ipc_rcu_putref(shp); | ||
571 | return error; | 578 | return error; |
572 | } | 579 | } |
573 | 580 | ||
diff --git a/ipc/util.c b/ipc/util.c index e829da9ed01f..fdb8ae740775 100644 --- a/ipc/util.c +++ b/ipc/util.c | |||
@@ -474,11 +474,6 @@ void ipc_free(void* ptr, int size) | |||
474 | kfree(ptr); | 474 | kfree(ptr); |
475 | } | 475 | } |
476 | 476 | ||
477 | struct ipc_rcu { | ||
478 | struct rcu_head rcu; | ||
479 | atomic_t refcount; | ||
480 | } ____cacheline_aligned_in_smp; | ||
481 | |||
482 | /** | 477 | /** |
483 | * ipc_rcu_alloc - allocate ipc and rcu space | 478 | * ipc_rcu_alloc - allocate ipc and rcu space |
484 | * @size: size desired | 479 | * @size: size desired |
@@ -505,27 +500,24 @@ int ipc_rcu_getref(void *ptr) | |||
505 | return atomic_inc_not_zero(&p->refcount); | 500 | return atomic_inc_not_zero(&p->refcount); |
506 | } | 501 | } |
507 | 502 | ||
508 | /** | 503 | void ipc_rcu_putref(void *ptr, void (*func)(struct rcu_head *head)) |
509 | * ipc_schedule_free - free ipc + rcu space | ||
510 | * @head: RCU callback structure for queued work | ||
511 | */ | ||
512 | static void ipc_schedule_free(struct rcu_head *head) | ||
513 | { | ||
514 | vfree(container_of(head, struct ipc_rcu, rcu)); | ||
515 | } | ||
516 | |||
517 | void ipc_rcu_putref(void *ptr) | ||
518 | { | 504 | { |
519 | struct ipc_rcu *p = ((struct ipc_rcu *)ptr) - 1; | 505 | struct ipc_rcu *p = ((struct ipc_rcu *)ptr) - 1; |
520 | 506 | ||
521 | if (!atomic_dec_and_test(&p->refcount)) | 507 | if (!atomic_dec_and_test(&p->refcount)) |
522 | return; | 508 | return; |
523 | 509 | ||
524 | if (is_vmalloc_addr(ptr)) { | 510 | call_rcu(&p->rcu, func); |
525 | call_rcu(&p->rcu, ipc_schedule_free); | 511 | } |
526 | } else { | 512 | |
527 | kfree_rcu(p, rcu); | 513 | void ipc_rcu_free(struct rcu_head *head) |
528 | } | 514 | { |
515 | struct ipc_rcu *p = container_of(head, struct ipc_rcu, rcu); | ||
516 | |||
517 | if (is_vmalloc_addr(p)) | ||
518 | vfree(p); | ||
519 | else | ||
520 | kfree(p); | ||
529 | } | 521 | } |
530 | 522 | ||
531 | /** | 523 | /** |
diff --git a/ipc/util.h b/ipc/util.h index c5f3338ba1fa..f2f5036f2eed 100644 --- a/ipc/util.h +++ b/ipc/util.h | |||
@@ -47,6 +47,13 @@ static inline void msg_exit_ns(struct ipc_namespace *ns) { } | |||
47 | static inline void shm_exit_ns(struct ipc_namespace *ns) { } | 47 | static inline void shm_exit_ns(struct ipc_namespace *ns) { } |
48 | #endif | 48 | #endif |
49 | 49 | ||
50 | struct ipc_rcu { | ||
51 | struct rcu_head rcu; | ||
52 | atomic_t refcount; | ||
53 | } ____cacheline_aligned_in_smp; | ||
54 | |||
55 | #define ipc_rcu_to_struct(p) ((void *)(p+1)) | ||
56 | |||
50 | /* | 57 | /* |
51 | * Structure that holds the parameters needed by the ipc operations | 58 | * Structure that holds the parameters needed by the ipc operations |
52 | * (see after) | 59 | * (see after) |
@@ -120,7 +127,8 @@ void ipc_free(void* ptr, int size); | |||
120 | */ | 127 | */ |
121 | void* ipc_rcu_alloc(int size); | 128 | void* ipc_rcu_alloc(int size); |
122 | int ipc_rcu_getref(void *ptr); | 129 | int ipc_rcu_getref(void *ptr); |
123 | void ipc_rcu_putref(void *ptr); | 130 | void ipc_rcu_putref(void *ptr, void (*func)(struct rcu_head *head)); |
131 | void ipc_rcu_free(struct rcu_head *head); | ||
124 | 132 | ||
125 | struct kern_ipc_perm *ipc_lock(struct ipc_ids *, int); | 133 | struct kern_ipc_perm *ipc_lock(struct ipc_ids *, int); |
126 | struct kern_ipc_perm *ipc_obtain_object(struct ipc_ids *ids, int id); | 134 | struct kern_ipc_perm *ipc_obtain_object(struct ipc_ids *ids, int id); |