diff options
author | Haiyang Zhang <haiyangz@microsoft.com> | 2010-05-28 19:22:44 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2010-06-30 11:18:14 -0400 |
commit | 8b5d6d3bd3e34e4cc67d875c8c88007c1c9aa960 (patch) | |
tree | 2f923d8299574a31799c14bfd9ecc10520ed9b02 | |
parent | 6c2fd308045ba902fbe9f4408daa7b949fa8f5a1 (diff) |
staging: hv: Fix race condition on vmbus channel initialization
There is a possible race condition when hv_utils starts to load immediately
after hv_vmbus is loading - null pointer error could happen.
This patch added wait/completion to ensure all channels are ready before
vmbus loading completes. So another module won't have any uninitialized channel.
Signed-off-by: Haiyang Zhang <haiyangz@microsoft.com>
Signed-off-by: Hank Janssen <hjanssen@microsoft.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r-- | drivers/staging/hv/channel_mgmt.c | 41 | ||||
-rw-r--r-- | drivers/staging/hv/vmbus.h | 2 | ||||
-rw-r--r-- | drivers/staging/hv/vmbus_drv.c | 3 |
3 files changed, 35 insertions, 11 deletions
diff --git a/drivers/staging/hv/channel_mgmt.c b/drivers/staging/hv/channel_mgmt.c index 3f53b4d1e4cf..12db555a3a5d 100644 --- a/drivers/staging/hv/channel_mgmt.c +++ b/drivers/staging/hv/channel_mgmt.c | |||
@@ -23,6 +23,7 @@ | |||
23 | #include <linux/slab.h> | 23 | #include <linux/slab.h> |
24 | #include <linux/list.h> | 24 | #include <linux/list.h> |
25 | #include <linux/module.h> | 25 | #include <linux/module.h> |
26 | #include <linux/completion.h> | ||
26 | #include "osd.h" | 27 | #include "osd.h" |
27 | #include "logging.h" | 28 | #include "logging.h" |
28 | #include "vmbus_private.h" | 29 | #include "vmbus_private.h" |
@@ -293,6 +294,25 @@ void FreeVmbusChannel(struct vmbus_channel *Channel) | |||
293 | Channel); | 294 | Channel); |
294 | } | 295 | } |
295 | 296 | ||
297 | |||
298 | DECLARE_COMPLETION(hv_channel_ready); | ||
299 | |||
300 | /* | ||
301 | * Count initialized channels, and ensure all channels are ready when hv_vmbus | ||
302 | * module loading completes. | ||
303 | */ | ||
304 | static void count_hv_channel(void) | ||
305 | { | ||
306 | static int counter; | ||
307 | unsigned long flags; | ||
308 | |||
309 | spin_lock_irqsave(&gVmbusConnection.channel_lock, flags); | ||
310 | if (++counter == MAX_MSG_TYPES) | ||
311 | complete(&hv_channel_ready); | ||
312 | spin_unlock_irqrestore(&gVmbusConnection.channel_lock, flags); | ||
313 | } | ||
314 | |||
315 | |||
296 | /* | 316 | /* |
297 | * VmbusChannelProcessOffer - Process the offer by creating a channel/device | 317 | * VmbusChannelProcessOffer - Process the offer by creating a channel/device |
298 | * associated with this offer | 318 | * associated with this offer |
@@ -373,22 +393,21 @@ static void VmbusChannelProcessOffer(void *context) | |||
373 | * can cleanup properly | 393 | * can cleanup properly |
374 | */ | 394 | */ |
375 | newChannel->State = CHANNEL_OPEN_STATE; | 395 | newChannel->State = CHANNEL_OPEN_STATE; |
376 | cnt = 0; | ||
377 | 396 | ||
378 | while (cnt != MAX_MSG_TYPES) { | 397 | /* Open IC channels */ |
398 | for (cnt = 0; cnt < MAX_MSG_TYPES; cnt++) { | ||
379 | if (memcmp(&newChannel->OfferMsg.Offer.InterfaceType, | 399 | if (memcmp(&newChannel->OfferMsg.Offer.InterfaceType, |
380 | &hv_cb_utils[cnt].data, | 400 | &hv_cb_utils[cnt].data, |
381 | sizeof(struct hv_guid)) == 0) { | 401 | sizeof(struct hv_guid)) == 0 && |
402 | VmbusChannelOpen(newChannel, 2 * PAGE_SIZE, | ||
403 | 2 * PAGE_SIZE, NULL, 0, | ||
404 | hv_cb_utils[cnt].callback, | ||
405 | newChannel) == 0) { | ||
406 | hv_cb_utils[cnt].channel = newChannel; | ||
382 | DPRINT_INFO(VMBUS, "%s", | 407 | DPRINT_INFO(VMBUS, "%s", |
383 | hv_cb_utils[cnt].log_msg); | 408 | hv_cb_utils[cnt].log_msg); |
384 | 409 | count_hv_channel(); | |
385 | if (VmbusChannelOpen(newChannel, 2 * PAGE_SIZE, | ||
386 | 2 * PAGE_SIZE, NULL, 0, | ||
387 | hv_cb_utils[cnt].callback, | ||
388 | newChannel) == 0) | ||
389 | hv_cb_utils[cnt].channel = newChannel; | ||
390 | } | 410 | } |
391 | cnt++; | ||
392 | } | 411 | } |
393 | } | 412 | } |
394 | DPRINT_EXIT(VMBUS); | 413 | DPRINT_EXIT(VMBUS); |
diff --git a/drivers/staging/hv/vmbus.h b/drivers/staging/hv/vmbus.h index 0c6ee0f487f3..3c14b2926e00 100644 --- a/drivers/staging/hv/vmbus.h +++ b/drivers/staging/hv/vmbus.h | |||
@@ -74,4 +74,6 @@ int vmbus_child_driver_register(struct driver_context *driver_ctx); | |||
74 | void vmbus_child_driver_unregister(struct driver_context *driver_ctx); | 74 | void vmbus_child_driver_unregister(struct driver_context *driver_ctx); |
75 | void vmbus_get_interface(struct vmbus_channel_interface *interface); | 75 | void vmbus_get_interface(struct vmbus_channel_interface *interface); |
76 | 76 | ||
77 | extern struct completion hv_channel_ready; | ||
78 | |||
77 | #endif /* _VMBUS_H_ */ | 79 | #endif /* _VMBUS_H_ */ |
diff --git a/drivers/staging/hv/vmbus_drv.c b/drivers/staging/hv/vmbus_drv.c index c21731a12ca7..22c80ece6388 100644 --- a/drivers/staging/hv/vmbus_drv.c +++ b/drivers/staging/hv/vmbus_drv.c | |||
@@ -27,6 +27,7 @@ | |||
27 | #include <linux/pci.h> | 27 | #include <linux/pci.h> |
28 | #include <linux/dmi.h> | 28 | #include <linux/dmi.h> |
29 | #include <linux/slab.h> | 29 | #include <linux/slab.h> |
30 | #include <linux/completion.h> | ||
30 | #include "version_info.h" | 31 | #include "version_info.h" |
31 | #include "osd.h" | 32 | #include "osd.h" |
32 | #include "logging.h" | 33 | #include "logging.h" |
@@ -356,6 +357,8 @@ static int vmbus_bus_init(int (*drv_init)(struct hv_driver *drv)) | |||
356 | 357 | ||
357 | vmbus_drv_obj->GetChannelOffers(); | 358 | vmbus_drv_obj->GetChannelOffers(); |
358 | 359 | ||
360 | wait_for_completion(&hv_channel_ready); | ||
361 | |||
359 | cleanup: | 362 | cleanup: |
360 | DPRINT_EXIT(VMBUS_DRV); | 363 | DPRINT_EXIT(VMBUS_DRV); |
361 | 364 | ||