Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 370141
b: refs/heads/master
c: 600fe97
h: refs/heads/master
i:
  370139: f38905e
v: v3
  • Loading branch information
Al Viro authored and Linus Torvalds committed May 2, 2013
1 parent f532796 commit ffbd8d8
Show file tree
Hide file tree
Showing 2 changed files with 17 additions and 88 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 4ada8db38a44654446fe35ceb20a1972220e0f69
refs/heads/master: 600fe9751aeb6f6b72de84076a05c5b8c04152c0
103 changes: 16 additions & 87 deletions trunk/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 ffbd8d8

Please sign in to comment.