ipc_schedule_free() can do vfree() directly now
authorAl Viro <viro@zeniv.linux.org.uk>
Mon, 29 Apr 2013 16:42:09 +0000 (12:42 -0400)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 2 May 2013 15:03:33 +0000 (08:03 -0700)
Commit 32fcfd40715e ("make vfree() safe to call from interrupt
contexts") made it safe to do vfree directly from the RCU callback,
which allows us to simplify ipc/util.c a lot by getting rid of the
differences between vmalloc/kmalloc memory.

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
ipc/util.c

index abfc13e8677f58ad667ce0c32a9e82c9c25ef247..809ec5ec812295f7ef652b365ff0b35c4a6060d1 100644 (file)
@@ -466,51 +466,13 @@ void ipc_free(void* ptr, int size)
                kfree(ptr);
 }
 
-/*
- * rcu allocations:
- * There are three headers that are prepended to the actual allocation:
- * - during use: ipc_rcu_hdr.
- * - during the rcu grace period: ipc_rcu_grace.
- * - [only if vmalloc]: ipc_rcu_sched.
- * Their lifetime doesn't overlap, thus the headers share the same memory.
- * Unlike a normal union, they are right-aligned, thus some container_of
- * forward/backward casting is necessary:
- */
-struct ipc_rcu_hdr
-{
-       atomic_t refcount;
-       int is_vmalloc;
-       void *data[0];
-};
-
-
-struct ipc_rcu_grace
-{
+struct ipc_rcu {
        struct rcu_head rcu;
+       atomic_t refcount;
        /* "void *" makes sure alignment of following data is sane. */
        void *data[0];
 };
 
-struct ipc_rcu_sched
-{
-       struct work_struct work;
-       /* "void *" makes sure alignment of following data is sane. */
-       void *data[0];
-};
-
-#define HDRLEN_KMALLOC         (sizeof(struct ipc_rcu_grace) > sizeof(struct ipc_rcu_hdr) ? \
-                                       sizeof(struct ipc_rcu_grace) : sizeof(struct ipc_rcu_hdr))
-#define HDRLEN_VMALLOC         (sizeof(struct ipc_rcu_sched) > HDRLEN_KMALLOC ? \
-                                       sizeof(struct ipc_rcu_sched) : HDRLEN_KMALLOC)
-
-static inline int rcu_use_vmalloc(int size)
-{
-       /* Too big for a single page? */
-       if (HDRLEN_KMALLOC + size > PAGE_SIZE)
-               return 1;
-       return 0;
-}
-
 /**
  *     ipc_rcu_alloc   -       allocate ipc and rcu space 
  *     @size: size desired
@@ -520,74 +482,41 @@ static inline int rcu_use_vmalloc(int size)
  */
 void *ipc_rcu_alloc(int size)
 {
-       void *out;
-
        /*
-        * We prepend the allocation with the rcu struct, and
-        * workqueue if necessary (for vmalloc).
+        * We prepend the allocation with the rcu struct
         */
-       if (rcu_use_vmalloc(size)) {
-               out = vmalloc(HDRLEN_VMALLOC + size);
-               if (!out)
-                       goto done;
-
-               out += HDRLEN_VMALLOC;
-               container_of(out, struct ipc_rcu_hdr, data)->is_vmalloc = 1;
-       } else {
-               out = kmalloc(HDRLEN_KMALLOC + size, GFP_KERNEL);
-               if (!out)
-                       goto done;
-
-               out += HDRLEN_KMALLOC;
-               container_of(out, struct ipc_rcu_hdr, data)->is_vmalloc = 0;
-       }
-
-       /* set reference counter no matter what kind of allocation was done */
-       atomic_set(&container_of(out, struct ipc_rcu_hdr, data)->refcount, 1);
-done:
-       return out;
+       struct ipc_rcu *out = ipc_alloc(sizeof(struct ipc_rcu) + size);
+       if (unlikely(!out))
+               return NULL;
+       atomic_set(&out->refcount, 1);
+       return out->data;
 }
 
 int ipc_rcu_getref(void *ptr)
 {
-       return atomic_inc_not_zero(&container_of(ptr, struct ipc_rcu_hdr, data)->refcount);
-}
-
-static void ipc_do_vfree(struct work_struct *work)
-{
-       vfree(container_of(work, struct ipc_rcu_sched, work));
+       return atomic_inc_not_zero(&container_of(ptr, struct ipc_rcu, data)->refcount);
 }
 
 /**
  * ipc_schedule_free - free ipc + rcu space
  * @head: RCU callback structure for queued work
- * 
- * Since RCU callback function is called in bh,
- * we need to defer the vfree to schedule_work().
  */
 static void ipc_schedule_free(struct rcu_head *head)
 {
-       struct ipc_rcu_grace *grace;
-       struct ipc_rcu_sched *sched;
-
-       grace = container_of(head, struct ipc_rcu_grace, rcu);
-       sched = container_of(&(grace->data[0]), struct ipc_rcu_sched,
-                               data[0]);
-
-       INIT_WORK(&sched->work, ipc_do_vfree);
-       schedule_work(&sched->work);
+       vfree(container_of(head, struct ipc_rcu, rcu));
 }
 
 void ipc_rcu_putref(void *ptr)
 {
-       if (!atomic_dec_and_test(&container_of(ptr, struct ipc_rcu_hdr, data)->refcount))
+       struct ipc_rcu *p = container_of(ptr, struct ipc_rcu, data);
+
+       if (!atomic_dec_and_test(&p->refcount))
                return;
 
-       if (container_of(ptr, struct ipc_rcu_hdr, data)->is_vmalloc) {
-               call_rcu(&container_of(ptr, struct ipc_rcu_grace, data)->rcu,
-                               ipc_schedule_free);
+       if (is_vmalloc_addr(ptr)) {
+               call_rcu(&p->rcu, ipc_schedule_free);
        } else {
-               kfree_rcu(container_of(ptr, struct ipc_rcu_grace, data), rcu);
+               kfree_rcu(p, rcu);
        }
 }