Skip to content

Commit

Permalink
ipc_schedule_free() can do vfree() directly now
Browse files Browse the repository at this point in the history
Commit 32fcfd4 ("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 <[email protected]>
Signed-off-by: Linus Torvalds <[email protected]>
  • Loading branch information
Al Viro authored and torvalds committed May 2, 2013
1 parent 4ada8db commit 600fe97
Showing 1 changed file with 16 additions and 87 deletions.
103 changes: 16 additions & 87 deletions ipc/util.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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);
}
}

Expand Down

0 comments on commit 600fe97

Please sign in to comment.