diff options
author | K. Y. Srinivasan <kys@microsoft.com> | 2014-04-08 21:45:54 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2014-05-03 19:24:26 -0400 |
commit | 3a28fa35d6658703cd26f9c16aaea0eae06afd40 (patch) | |
tree | bcc8f07bbd9365e0a270c85af0e50ce3e1682987 /drivers/hv | |
parent | d3ba720dd58cdf6630fee4b89482c465d5ad0d0f (diff) |
Drivers: hv: vmbus: Implement per-CPU mapping of relid to channel
Currently the mapping of the relID to channel is done under the protection of a
single spin lock. Starting with ws2012, each channel is bound to a specific VCPU
in the guest. Use this binding to eliminate the spin lock by setting up
per-cpu state for mapping relId to the channel.
Signed-off-by: K. Y. Srinivasan <kys@microsoft.com>
Reviewed-by: Haiyang Zhang <haiyangz@microsoft.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/hv')
-rw-r--r-- | drivers/hv/channel_mgmt.c | 41 | ||||
-rw-r--r-- | drivers/hv/connection.c | 24 | ||||
-rw-r--r-- | drivers/hv/hv.c | 2 | ||||
-rw-r--r-- | drivers/hv/hyperv_vmbus.h | 5 |
4 files changed, 70 insertions, 2 deletions
diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c index 6f7fdd9a7e77..6c8b032cacba 100644 --- a/drivers/hv/channel_mgmt.c +++ b/drivers/hv/channel_mgmt.c | |||
@@ -149,6 +149,7 @@ static struct vmbus_channel *alloc_channel(void) | |||
149 | spin_lock_init(&channel->sc_lock); | 149 | spin_lock_init(&channel->sc_lock); |
150 | 150 | ||
151 | INIT_LIST_HEAD(&channel->sc_list); | 151 | INIT_LIST_HEAD(&channel->sc_list); |
152 | INIT_LIST_HEAD(&channel->percpu_list); | ||
152 | 153 | ||
153 | channel->controlwq = create_workqueue("hv_vmbus_ctl"); | 154 | channel->controlwq = create_workqueue("hv_vmbus_ctl"); |
154 | if (!channel->controlwq) { | 155 | if (!channel->controlwq) { |
@@ -188,7 +189,20 @@ static void free_channel(struct vmbus_channel *channel) | |||
188 | queue_work(vmbus_connection.work_queue, &channel->work); | 189 | queue_work(vmbus_connection.work_queue, &channel->work); |
189 | } | 190 | } |
190 | 191 | ||
192 | static void percpu_channel_enq(void *arg) | ||
193 | { | ||
194 | struct vmbus_channel *channel = arg; | ||
195 | int cpu = smp_processor_id(); | ||
196 | |||
197 | list_add_tail(&channel->percpu_list, &hv_context.percpu_list[cpu]); | ||
198 | } | ||
191 | 199 | ||
200 | static void percpu_channel_deq(void *arg) | ||
201 | { | ||
202 | struct vmbus_channel *channel = arg; | ||
203 | |||
204 | list_del(&channel->percpu_list); | ||
205 | } | ||
192 | 206 | ||
193 | /* | 207 | /* |
194 | * vmbus_process_rescind_offer - | 208 | * vmbus_process_rescind_offer - |
@@ -210,6 +224,12 @@ static void vmbus_process_rescind_offer(struct work_struct *work) | |||
210 | msg.header.msgtype = CHANNELMSG_RELID_RELEASED; | 224 | msg.header.msgtype = CHANNELMSG_RELID_RELEASED; |
211 | vmbus_post_msg(&msg, sizeof(struct vmbus_channel_relid_released)); | 225 | vmbus_post_msg(&msg, sizeof(struct vmbus_channel_relid_released)); |
212 | 226 | ||
227 | if (channel->target_cpu != smp_processor_id()) | ||
228 | smp_call_function_single(channel->target_cpu, | ||
229 | percpu_channel_deq, channel, true); | ||
230 | else | ||
231 | percpu_channel_deq(channel); | ||
232 | |||
213 | if (channel->primary_channel == NULL) { | 233 | if (channel->primary_channel == NULL) { |
214 | spin_lock_irqsave(&vmbus_connection.channel_lock, flags); | 234 | spin_lock_irqsave(&vmbus_connection.channel_lock, flags); |
215 | list_del(&channel->listentry); | 235 | list_del(&channel->listentry); |
@@ -245,6 +265,7 @@ static void vmbus_process_offer(struct work_struct *work) | |||
245 | work); | 265 | work); |
246 | struct vmbus_channel *channel; | 266 | struct vmbus_channel *channel; |
247 | bool fnew = true; | 267 | bool fnew = true; |
268 | bool enq = false; | ||
248 | int ret; | 269 | int ret; |
249 | unsigned long flags; | 270 | unsigned long flags; |
250 | 271 | ||
@@ -264,12 +285,22 @@ static void vmbus_process_offer(struct work_struct *work) | |||
264 | } | 285 | } |
265 | } | 286 | } |
266 | 287 | ||
267 | if (fnew) | 288 | if (fnew) { |
268 | list_add_tail(&newchannel->listentry, | 289 | list_add_tail(&newchannel->listentry, |
269 | &vmbus_connection.chn_list); | 290 | &vmbus_connection.chn_list); |
291 | enq = true; | ||
292 | } | ||
270 | 293 | ||
271 | spin_unlock_irqrestore(&vmbus_connection.channel_lock, flags); | 294 | spin_unlock_irqrestore(&vmbus_connection.channel_lock, flags); |
272 | 295 | ||
296 | if (enq) { | ||
297 | if (newchannel->target_cpu != smp_processor_id()) | ||
298 | smp_call_function_single(newchannel->target_cpu, | ||
299 | percpu_channel_enq, | ||
300 | newchannel, true); | ||
301 | else | ||
302 | percpu_channel_enq(newchannel); | ||
303 | } | ||
273 | if (!fnew) { | 304 | if (!fnew) { |
274 | /* | 305 | /* |
275 | * Check to see if this is a sub-channel. | 306 | * Check to see if this is a sub-channel. |
@@ -282,6 +313,14 @@ static void vmbus_process_offer(struct work_struct *work) | |||
282 | spin_lock_irqsave(&channel->sc_lock, flags); | 313 | spin_lock_irqsave(&channel->sc_lock, flags); |
283 | list_add_tail(&newchannel->sc_list, &channel->sc_list); | 314 | list_add_tail(&newchannel->sc_list, &channel->sc_list); |
284 | spin_unlock_irqrestore(&channel->sc_lock, flags); | 315 | spin_unlock_irqrestore(&channel->sc_lock, flags); |
316 | |||
317 | if (newchannel->target_cpu != smp_processor_id()) | ||
318 | smp_call_function_single(newchannel->target_cpu, | ||
319 | percpu_channel_enq, | ||
320 | newchannel, true); | ||
321 | else | ||
322 | percpu_channel_enq(newchannel); | ||
323 | |||
285 | newchannel->state = CHANNEL_OPEN_STATE; | 324 | newchannel->state = CHANNEL_OPEN_STATE; |
286 | if (channel->sc_creation_callback != NULL) | 325 | if (channel->sc_creation_callback != NULL) |
287 | channel->sc_creation_callback(newchannel); | 326 | channel->sc_creation_callback(newchannel); |
diff --git a/drivers/hv/connection.c b/drivers/hv/connection.c index df2363ea017f..7f10c151632a 100644 --- a/drivers/hv/connection.c +++ b/drivers/hv/connection.c | |||
@@ -234,6 +234,28 @@ cleanup: | |||
234 | return ret; | 234 | return ret; |
235 | } | 235 | } |
236 | 236 | ||
237 | /* | ||
238 | * Map the given relid to the corresponding channel based on the | ||
239 | * per-cpu list of channels that have been affinitized to this CPU. | ||
240 | * This will be used in the channel callback path as we can do this | ||
241 | * mapping in a lock-free fashion. | ||
242 | */ | ||
243 | static struct vmbus_channel *pcpu_relid2channel(u32 relid) | ||
244 | { | ||
245 | struct vmbus_channel *channel; | ||
246 | struct vmbus_channel *found_channel = NULL; | ||
247 | int cpu = smp_processor_id(); | ||
248 | struct list_head *pcpu_head = &hv_context.percpu_list[cpu]; | ||
249 | |||
250 | list_for_each_entry(channel, pcpu_head, percpu_list) { | ||
251 | if (channel->offermsg.child_relid == relid) { | ||
252 | found_channel = channel; | ||
253 | break; | ||
254 | } | ||
255 | } | ||
256 | |||
257 | return found_channel; | ||
258 | } | ||
237 | 259 | ||
238 | /* | 260 | /* |
239 | * relid2channel - Get the channel object given its | 261 | * relid2channel - Get the channel object given its |
@@ -285,7 +307,7 @@ static void process_chn_event(u32 relid) | |||
285 | * Find the channel based on this relid and invokes the | 307 | * Find the channel based on this relid and invokes the |
286 | * channel callback to process the event | 308 | * channel callback to process the event |
287 | */ | 309 | */ |
288 | channel = relid2channel(relid); | 310 | channel = pcpu_relid2channel(relid); |
289 | 311 | ||
290 | if (!channel) { | 312 | if (!channel) { |
291 | pr_err("channel not found for relid - %u\n", relid); | 313 | pr_err("channel not found for relid - %u\n", relid); |
diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c index bcb49502c3bf..edfc8488cb03 100644 --- a/drivers/hv/hv.c +++ b/drivers/hv/hv.c | |||
@@ -383,6 +383,8 @@ void hv_synic_init(void *arg) | |||
383 | */ | 383 | */ |
384 | rdmsrl(HV_X64_MSR_VP_INDEX, vp_index); | 384 | rdmsrl(HV_X64_MSR_VP_INDEX, vp_index); |
385 | hv_context.vp_index[cpu] = (u32)vp_index; | 385 | hv_context.vp_index[cpu] = (u32)vp_index; |
386 | |||
387 | INIT_LIST_HEAD(&hv_context.percpu_list[cpu]); | ||
386 | return; | 388 | return; |
387 | } | 389 | } |
388 | 390 | ||
diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h index 860134da8039..18d1a8404cbc 100644 --- a/drivers/hv/hyperv_vmbus.h +++ b/drivers/hv/hyperv_vmbus.h | |||
@@ -510,6 +510,11 @@ struct hv_context { | |||
510 | * basis. | 510 | * basis. |
511 | */ | 511 | */ |
512 | struct tasklet_struct *event_dpc[NR_CPUS]; | 512 | struct tasklet_struct *event_dpc[NR_CPUS]; |
513 | /* | ||
514 | * To optimize the mapping of relid to channel, maintain | ||
515 | * per-cpu list of the channels based on their CPU affinity. | ||
516 | */ | ||
517 | struct list_head percpu_list[NR_CPUS]; | ||
513 | }; | 518 | }; |
514 | 519 | ||
515 | extern struct hv_context hv_context; | 520 | extern struct hv_context hv_context; |