diff options
| -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); |
