Skip to content

Commit

Permalink
vmbus: use rcu for per-cpu channel list
Browse files Browse the repository at this point in the history
The per-cpu channel list is now referred to in the interrupt
routine. This is mostly safe since the host will not normally generate
an interrupt when channel is being deleted but if it did then there
would be a use after free problem.

To solve, this use RCU protection on ther per-cpu list.

Fixes: 631e63a ("vmbus: change to per channel tasklet")

Signed-off-by: Stephen Hemminger <sthemmin@microsoft.com>
Signed-off-by: K. Y. Srinivasan <kys@microsoft.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
  • Loading branch information
Stephen Hemminger authored and Greg Kroah-Hartman committed Mar 16, 2017
1 parent c6240ca commit 8200f20
Show file tree
Hide file tree
Showing 3 changed files with 16 additions and 4 deletions.
7 changes: 4 additions & 3 deletions drivers/hv/channel_mgmt.c
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,8 @@ static struct vmbus_channel *alloc_channel(void)
static void free_channel(struct vmbus_channel *channel)
{
tasklet_kill(&channel->callback_event);
kfree(channel);

kfree_rcu(channel, rcu);
}

static void percpu_channel_enq(void *arg)
Expand All @@ -359,14 +360,14 @@ static void percpu_channel_enq(void *arg)
struct hv_per_cpu_context *hv_cpu
= this_cpu_ptr(hv_context.cpu_context);

list_add_tail(&channel->percpu_list, &hv_cpu->chan_list);
list_add_tail_rcu(&channel->percpu_list, &hv_cpu->chan_list);
}

static void percpu_channel_deq(void *arg)
{
struct vmbus_channel *channel = arg;

list_del(&channel->percpu_list);
list_del_rcu(&channel->percpu_list);
}


Expand Down
6 changes: 5 additions & 1 deletion drivers/hv/vmbus_drv.c
Original file line number Diff line number Diff line change
Expand Up @@ -939,8 +939,10 @@ static void vmbus_chan_sched(struct hv_per_cpu_context *hv_cpu)
if (relid == 0)
continue;

rcu_read_lock();

/* Find channel based on relid */
list_for_each_entry(channel, &hv_cpu->chan_list, percpu_list) {
list_for_each_entry_rcu(channel, &hv_cpu->chan_list, percpu_list) {
if (channel->offermsg.child_relid != relid)
continue;

Expand All @@ -956,6 +958,8 @@ static void vmbus_chan_sched(struct hv_per_cpu_context *hv_cpu)
tasklet_schedule(&channel->callback_event);
}
}

rcu_read_unlock();
}
}

Expand Down
7 changes: 7 additions & 0 deletions include/linux/hyperv.h
Original file line number Diff line number Diff line change
Expand Up @@ -845,6 +845,13 @@ struct vmbus_channel {
* link up channels based on their CPU affinity.
*/
struct list_head percpu_list;

/*
* Defer freeing channel until after all cpu's have
* gone through grace period.
*/
struct rcu_head rcu;

/*
* For performance critical channels (storage, networking
* etc,), Hyper-V has a mechanism to enhance the throughput
Expand Down

0 comments on commit 8200f20

Please sign in to comment.