Further cleanup of the hv drivers: 1) Cleanup the reference counting mess for both stor and net devices. 2) Handle all block devices using the storvsc driver. 3) Accomodate some host side scsi emulation bugs. 4) In case of scsi errors off-line the device. Regads, K. Y
K. Y. Srinivasan
2011-Jun-29 14:38 UTC
[PATCH 01/40] Staging: hv: storvsc: Do not aquire an unnecessary reference on stor_device
On entry into storvsc_on_io_completion() we have already acquired a reference on the stor_device; there is no need to acquire an additional reference here. Signed-off-by: K. Y. Srinivasan <kys at microsoft.com> Signed-off-by: Haiyang Zhang <haiyangz at microsoft.com> Signed-off-by: Abhishek Kane <v-abkane at microsoft.com> Signed-off-by: Hank Janssen <hjanssen at microsoft.com> --- drivers/staging/hv/storvsc.c | 5 +---- 1 files changed, 1 insertions(+), 4 deletions(-) diff --git a/drivers/staging/hv/storvsc.c b/drivers/staging/hv/storvsc.c index 8c62829..cd38cd6 100644 --- a/drivers/staging/hv/storvsc.c +++ b/drivers/staging/hv/storvsc.c @@ -232,9 +232,7 @@ static void storvsc_on_io_completion(struct hv_device *device, struct storvsc_device *stor_device; struct vstor_packet *stor_pkt; - stor_device = must_get_stor_device(device); - if (!stor_device) - return; + stor_device = (struct storvsc_device *)device->ext; stor_pkt = &request->vstor_packet; @@ -279,7 +277,6 @@ static void storvsc_on_io_completion(struct hv_device *device, wake_up(&stor_device->waiting_to_drain); - put_stor_device(device); } static void storvsc_on_receive(struct hv_device *device, -- 1.7.4.1
K. Y. Srinivasan
2011-Jun-29 14:38 UTC
[PATCH 02/40] Staging: hv: storvsc: Rename must_get_stor_device()
In preparation for cleaning up how we manage reference counts on the stor device, clearly distinguish why we are attempting to acquire a reference. Signed-off-by: K. Y. Srinivasan <kys at microsoft.com> Signed-off-by: Haiyang Zhang <haiyangz at microsoft.com> Signed-off-by: Abhishek Kane <v-abkane at microsoft.com> Signed-off-by: Hank Janssen <hjanssen at microsoft.com> --- drivers/staging/hv/storvsc.c | 6 +++--- 1 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/hv/storvsc.c b/drivers/staging/hv/storvsc.c index cd38cd6..89708b1 100644 --- a/drivers/staging/hv/storvsc.c +++ b/drivers/staging/hv/storvsc.c @@ -41,7 +41,7 @@ static inline struct storvsc_device *alloc_stor_device(struct hv_device *device) return NULL; /* Set to 2 to allow both inbound and outbound traffics */ - /* (ie get_stor_device() and must_get_stor_device()) to proceed. */ + /* (ie get_stor_device() and get_in_stor_device()) to proceed. */ atomic_cmpxchg(&stor_device->ref_count, 0, 2); init_waitqueue_head(&stor_device->waiting_to_drain); @@ -53,7 +53,7 @@ static inline struct storvsc_device *alloc_stor_device(struct hv_device *device) /* Get the stordevice object iff exists and its refcount > 0 */ -static inline struct storvsc_device *must_get_stor_device( +static inline struct storvsc_device *get_in_stor_device( struct hv_device *device) { struct storvsc_device *stor_device; @@ -305,7 +305,7 @@ static void storvsc_on_channel_callback(void *context) int ret; - stor_device = must_get_stor_device(device); + stor_device = get_in_stor_device(device); if (!stor_device) return; -- 1.7.4.1
K. Y. Srinivasan
2011-Jun-29 14:39 UTC
[PATCH 03/40] Staging: hv: storvsc: Rename get_stor_device()
In preparation for cleaning up how we manage reference counts on the stor device, clearly distinguish why we are attempting to acquire a reference. Signed-off-by: K. Y. Srinivasan <kys at microsoft.com> Signed-off-by: Haiyang Zhang <haiyangz at microsoft.com> Signed-off-by: Abhishek Kane <v-abkane at microsoft.com> Signed-off-by: Hank Janssen <hjanssen at microsoft.com> --- drivers/staging/hv/hyperv_storage.h | 3 ++- drivers/staging/hv/storvsc.c | 8 ++++---- drivers/staging/hv/storvsc_drv.c | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/staging/hv/hyperv_storage.h b/drivers/staging/hv/hyperv_storage.h index a01f9a0..a224413 100644 --- a/drivers/staging/hv/hyperv_storage.h +++ b/drivers/staging/hv/hyperv_storage.h @@ -288,7 +288,8 @@ struct storvsc_device { /* Get the stordevice object iff exists and its refcount > 1 */ -static inline struct storvsc_device *get_stor_device(struct hv_device *device) +static inline struct storvsc_device *get_out_stor_device( + struct hv_device *device) { struct storvsc_device *stor_device; diff --git a/drivers/staging/hv/storvsc.c b/drivers/staging/hv/storvsc.c index 89708b1..313a3f8 100644 --- a/drivers/staging/hv/storvsc.c +++ b/drivers/staging/hv/storvsc.c @@ -41,7 +41,7 @@ static inline struct storvsc_device *alloc_stor_device(struct hv_device *device) return NULL; /* Set to 2 to allow both inbound and outbound traffics */ - /* (ie get_stor_device() and get_in_stor_device()) to proceed. */ + /* (ie get_out_stor_device() and get_in_stor_device()) to proceed. */ atomic_cmpxchg(&stor_device->ref_count, 0, 2); init_waitqueue_head(&stor_device->waiting_to_drain); @@ -67,7 +67,7 @@ static inline struct storvsc_device *get_in_stor_device( return stor_device; } -/* Drop ref count to 1 to effectively disable get_stor_device() */ +/* Drop ref count to 1 to effectively disable get_out_stor_device() */ static inline struct storvsc_device *release_stor_device( struct hv_device *device) { @@ -105,7 +105,7 @@ static int storvsc_channel_init(struct hv_device *device) struct vstor_packet *vstor_packet; int ret, t; - stor_device = get_stor_device(device); + stor_device = get_out_stor_device(device); if (!stor_device) return -ENODEV; @@ -427,7 +427,7 @@ int storvsc_do_io(struct hv_device *device, int ret = 0; vstor_packet = &request->vstor_packet; - stor_device = get_stor_device(device); + stor_device = get_out_stor_device(device); if (!stor_device) return -ENODEV; diff --git a/drivers/staging/hv/storvsc_drv.c b/drivers/staging/hv/storvsc_drv.c index 7db5246..8d5be51 100644 --- a/drivers/staging/hv/storvsc_drv.c +++ b/drivers/staging/hv/storvsc_drv.c @@ -346,7 +346,7 @@ static int storvsc_host_reset(struct hv_device *device) int ret, t; - stor_device = get_stor_device(device); + stor_device = get_out_stor_device(device); if (!stor_device) return -ENODEV; -- 1.7.4.1
K. Y. Srinivasan
2011-Jun-29 14:39 UTC
[PATCH 04/40] Staging: hv: storvsc: Cleanup alloc_stor_device()
Cleanup alloc_stor_device(), we can set the ref_count directly. Signed-off-by: K. Y. Srinivasan <kys at microsoft.com> Signed-off-by: Haiyang Zhang <haiyangz at microsoft.com> Signed-off-by: Abhishek Kane <v-abkane at microsoft.com> Signed-off-by: Hank Janssen <hjanssen at microsoft.com> --- drivers/staging/hv/storvsc.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/drivers/staging/hv/storvsc.c b/drivers/staging/hv/storvsc.c index 313a3f8..48bd8da 100644 --- a/drivers/staging/hv/storvsc.c +++ b/drivers/staging/hv/storvsc.c @@ -42,7 +42,7 @@ static inline struct storvsc_device *alloc_stor_device(struct hv_device *device) /* Set to 2 to allow both inbound and outbound traffics */ /* (ie get_out_stor_device() and get_in_stor_device()) to proceed. */ - atomic_cmpxchg(&stor_device->ref_count, 0, 2); + atomic_set(&stor_device->ref_count, 2); init_waitqueue_head(&stor_device->waiting_to_drain); stor_device->device = device; -- 1.7.4.1
K. Y. Srinivasan
2011-Jun-29 14:39 UTC
[PATCH 05/40] Staging: hv: storvsc: Introduce state to manage the lifecycle of stor device
Introduce state to manage the lifecycle of stor device. This would be the basis for managing the references on the stor object. Signed-off-by: K. Y. Srinivasan <kys at microsoft.com> Signed-off-by: Haiyang Zhang <haiyangz at microsoft.com> Signed-off-by: Abhishek Kane <v-abkane at microsoft.com> Signed-off-by: Hank Janssen <hjanssen at microsoft.com> --- drivers/staging/hv/hyperv_storage.h | 2 +- drivers/staging/hv/storvsc.c | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/staging/hv/hyperv_storage.h b/drivers/staging/hv/hyperv_storage.h index a224413..d93bf93 100644 --- a/drivers/staging/hv/hyperv_storage.h +++ b/drivers/staging/hv/hyperv_storage.h @@ -266,7 +266,7 @@ struct storvsc_device { /* 0 indicates the device is being destroyed */ atomic_t ref_count; - + bool destroy; bool drain_notify; atomic_t num_outstanding_req; diff --git a/drivers/staging/hv/storvsc.c b/drivers/staging/hv/storvsc.c index 48bd8da..357b08a 100644 --- a/drivers/staging/hv/storvsc.c +++ b/drivers/staging/hv/storvsc.c @@ -43,7 +43,7 @@ static inline struct storvsc_device *alloc_stor_device(struct hv_device *device) /* Set to 2 to allow both inbound and outbound traffics */ /* (ie get_out_stor_device() and get_in_stor_device()) to proceed. */ atomic_set(&stor_device->ref_count, 2); - + stor_device->destroy = false; init_waitqueue_head(&stor_device->waiting_to_drain); stor_device->device = device; device->ext = stor_device; @@ -401,6 +401,7 @@ int storvsc_dev_remove(struct hv_device *device) struct storvsc_device *stor_device; stor_device = release_stor_device(device); + stor_device->destroy = true; /* * At this point, all outbound traffic should be disable. We -- 1.7.4.1
K. Y. Srinivasan
2011-Jun-29 14:39 UTC
[PATCH 06/40] Staging: hv: vmbus: Introduce a lock to protect the ext field in hv_device
The current mechanism for handling references in broken. Introduce a lock to protect the ext field in hv_device. Signed-off-by: K. Y. Srinivasan <kys at microsoft.com> Signed-off-by: Haiyang Zhang <haiyangz at microsoft.com> Signed-off-by: Abhishek Kane <v-abkane at microsoft.com> Signed-off-by: Hank Janssen <hjanssen at microsoft.com> --- drivers/staging/hv/hyperv.h | 3 +++ drivers/staging/hv/vmbus_drv.c | 1 + 2 files changed, 4 insertions(+), 0 deletions(-) diff --git a/drivers/staging/hv/hyperv.h b/drivers/staging/hv/hyperv.h index 370b096..ff3c69d 100644 --- a/drivers/staging/hv/hyperv.h +++ b/drivers/staging/hv/hyperv.h @@ -830,6 +830,9 @@ struct hv_device { struct vmbus_channel *channel; + /* This lock protects the device extension field */ + spinlock_t ext_lock; + /* Device extension; */ void *ext; }; diff --git a/drivers/staging/hv/vmbus_drv.c b/drivers/staging/hv/vmbus_drv.c index 2e6bfc1..903362e 100644 --- a/drivers/staging/hv/vmbus_drv.c +++ b/drivers/staging/hv/vmbus_drv.c @@ -557,6 +557,7 @@ struct hv_device *vmbus_child_device_create(struct hv_guid *type, return NULL; } + spin_lock_init(&child_device_obj->ext_lock); child_device_obj->channel = channel; /* * Get the human readable device type name and stash it away. -- 1.7.4.1
K. Y. Srinivasan
2011-Jun-29 14:39 UTC
[PATCH 07/40] Staging: hv: storvsc: Use the newly introduced lock in accessing ext field
Use the newly introduced lock in accessing ext field. Signed-off-by: K. Y. Srinivasan <kys at microsoft.com> Signed-off-by: Haiyang Zhang <haiyangz at microsoft.com> Signed-off-by: Abhishek Kane <v-abkane at microsoft.com> Signed-off-by: Hank Janssen <hjanssen at microsoft.com> --- drivers/staging/hv/hyperv_storage.h | 6 ++++++ drivers/staging/hv/storvsc.c | 6 ++++++ 2 files changed, 12 insertions(+), 0 deletions(-) diff --git a/drivers/staging/hv/hyperv_storage.h b/drivers/staging/hv/hyperv_storage.h index d93bf93..6b20f1d 100644 --- a/drivers/staging/hv/hyperv_storage.h +++ b/drivers/staging/hv/hyperv_storage.h @@ -292,12 +292,15 @@ static inline struct storvsc_device *get_out_stor_device( struct hv_device *device) { struct storvsc_device *stor_device; + unsigned long flags; + spin_lock_irqsave(&device->ext_lock, flags); stor_device = (struct storvsc_device *)device->ext; if (stor_device && atomic_read(&stor_device->ref_count) > 1) atomic_inc(&stor_device->ref_count); else stor_device = NULL; + spin_unlock_irqrestore(&device->ext_lock, flags); return stor_device; } @@ -306,10 +309,13 @@ static inline struct storvsc_device *get_out_stor_device( static inline void put_stor_device(struct hv_device *device) { struct storvsc_device *stor_device; + unsigned long flags; + spin_lock_irqsave(&device->ext_lock, flags); stor_device = (struct storvsc_device *)device->ext; atomic_dec(&stor_device->ref_count); + spin_unlock_irqrestore(&device->ext_lock, flags); } static inline void storvsc_wait_to_drain(struct storvsc_device *dev) diff --git a/drivers/staging/hv/storvsc.c b/drivers/staging/hv/storvsc.c index 357b08a..d1b6c4e 100644 --- a/drivers/staging/hv/storvsc.c +++ b/drivers/staging/hv/storvsc.c @@ -57,12 +57,15 @@ static inline struct storvsc_device *get_in_stor_device( struct hv_device *device) { struct storvsc_device *stor_device; + unsigned long flags; + spin_lock_irqsave(&device->ext_lock, flags); stor_device = (struct storvsc_device *)device->ext; if (stor_device && atomic_read(&stor_device->ref_count)) atomic_inc(&stor_device->ref_count); else stor_device = NULL; + spin_unlock_irqrestore(&device->ext_lock, flags); return stor_device; } @@ -87,6 +90,7 @@ static inline struct storvsc_device *final_release_stor_device( struct hv_device *device) { struct storvsc_device *stor_device; + unsigned long flags; stor_device = (struct storvsc_device *)device->ext; @@ -94,7 +98,9 @@ static inline struct storvsc_device *final_release_stor_device( while (atomic_cmpxchg(&stor_device->ref_count, 1, 0) != 1) udelay(100); + spin_lock_irqsave(&device->ext_lock, flags); device->ext = NULL; + spin_unlock_irqrestore(&device->ext_lock, flags); return stor_device; } -- 1.7.4.1
K. Y. Srinivasan
2011-Jun-29 14:39 UTC
[PATCH 08/40] Staging: hv: storvsc: Prevent outgoing traffic when stor dev is destroyed
Prevent outgoing traffic when stor dev is destroyed. Signed-off-by: K. Y. Srinivasan <kys at microsoft.com> Signed-off-by: Haiyang Zhang <haiyangz at microsoft.com> Signed-off-by: Abhishek Kane <v-abkane at microsoft.com> Signed-off-by: Hank Janssen <hjanssen at microsoft.com> --- drivers/staging/hv/hyperv_storage.h | 3 ++- 1 files changed, 2 insertions(+), 1 deletions(-) diff --git a/drivers/staging/hv/hyperv_storage.h b/drivers/staging/hv/hyperv_storage.h index 6b20f1d..53b65be 100644 --- a/drivers/staging/hv/hyperv_storage.h +++ b/drivers/staging/hv/hyperv_storage.h @@ -296,7 +296,8 @@ static inline struct storvsc_device *get_out_stor_device( spin_lock_irqsave(&device->ext_lock, flags); stor_device = (struct storvsc_device *)device->ext; - if (stor_device && atomic_read(&stor_device->ref_count) > 1) + if (stor_device && (atomic_read(&stor_device->ref_count) > 1) && + !stor_device->destroy) atomic_inc(&stor_device->ref_count); else stor_device = NULL; -- 1.7.4.1
K. Y. Srinivasan
2011-Jun-29 14:39 UTC
[PATCH 09/40] Staging: hv: storvsc: Get rid of release_stor_device() by inlining the code
Get rid of release_stor_device() by inlining the code. Signed-off-by: K. Y. Srinivasan <kys at microsoft.com> Signed-off-by: Haiyang Zhang <haiyangz at microsoft.com> Signed-off-by: Abhishek Kane <v-abkane at microsoft.com> Signed-off-by: Hank Janssen <hjanssen at microsoft.com> --- drivers/staging/hv/storvsc.c | 23 +++++++---------------- 1 files changed, 7 insertions(+), 16 deletions(-) diff --git a/drivers/staging/hv/storvsc.c b/drivers/staging/hv/storvsc.c index d1b6c4e..f52e610 100644 --- a/drivers/staging/hv/storvsc.c +++ b/drivers/staging/hv/storvsc.c @@ -70,21 +70,6 @@ static inline struct storvsc_device *get_in_stor_device( return stor_device; } -/* Drop ref count to 1 to effectively disable get_out_stor_device() */ -static inline struct storvsc_device *release_stor_device( - struct hv_device *device) -{ - struct storvsc_device *stor_device; - - stor_device = (struct storvsc_device *)device->ext; - - /* Busy wait until the ref drop to 2, then set it to 1 */ - while (atomic_cmpxchg(&stor_device->ref_count, 2, 1) != 2) - udelay(100); - - return stor_device; -} - /* Drop ref count to 0. No one can use stor_device object. */ static inline struct storvsc_device *final_release_stor_device( struct hv_device *device) @@ -406,7 +391,13 @@ int storvsc_dev_remove(struct hv_device *device) { struct storvsc_device *stor_device; - stor_device = release_stor_device(device); + /* + * Since we currently hold a reference on the stor + * device, it is safe to dereference the ext + * pointer. + */ + stor_device = (struct storvsc_device *)device->ext; + atomic_dec(&stor_device->ref_count); stor_device->destroy = true; /* -- 1.7.4.1
K. Y. Srinivasan
2011-Jun-29 14:39 UTC
[PATCH 10/40] Staging: hv: storvsc: Get rid of final_release_stor_device() by inlining code
Get rid of final_release_stor_device() by inlining code. Signed-off-by: K. Y. Srinivasan <kys at microsoft.com> Signed-off-by: Haiyang Zhang <haiyangz at microsoft.com> Signed-off-by: Abhishek Kane <v-abkane at microsoft.com> Signed-off-by: Hank Janssen <hjanssen at microsoft.com> --- drivers/staging/hv/storvsc.c | 25 +++++-------------------- 1 files changed, 5 insertions(+), 20 deletions(-) diff --git a/drivers/staging/hv/storvsc.c b/drivers/staging/hv/storvsc.c index f52e610..c4cb170 100644 --- a/drivers/staging/hv/storvsc.c +++ b/drivers/staging/hv/storvsc.c @@ -70,25 +70,6 @@ static inline struct storvsc_device *get_in_stor_device( return stor_device; } -/* Drop ref count to 0. No one can use stor_device object. */ -static inline struct storvsc_device *final_release_stor_device( - struct hv_device *device) -{ - struct storvsc_device *stor_device; - unsigned long flags; - - stor_device = (struct storvsc_device *)device->ext; - - /* Busy wait until the ref drop to 1, then set it to 0 */ - while (atomic_cmpxchg(&stor_device->ref_count, 1, 0) != 1) - udelay(100); - - spin_lock_irqsave(&device->ext_lock, flags); - device->ext = NULL; - spin_unlock_irqrestore(&device->ext_lock, flags); - return stor_device; -} - static int storvsc_channel_init(struct hv_device *device) { struct storvsc_device *stor_device; @@ -390,6 +371,7 @@ int storvsc_dev_add(struct hv_device *device, int storvsc_dev_remove(struct hv_device *device) { struct storvsc_device *stor_device; + unsigned long flags; /* * Since we currently hold a reference on the stor @@ -408,7 +390,10 @@ int storvsc_dev_remove(struct hv_device *device) storvsc_wait_to_drain(stor_device); - stor_device = final_release_stor_device(device); + spin_lock_irqsave(&device->ext_lock, flags); + atomic_set(&stor_device->ref_count, 0); + device->ext = NULL; + spin_unlock_irqrestore(&device->ext_lock, flags); /* Close the channel */ vmbus_close(device->channel); -- 1.7.4.1
K. Y. Srinivasan
2011-Jun-29 14:39 UTC
[PATCH 11/40] Staging: hv: storvsc: Leverage the spinlock to manage ref_cnt
Now that we have a spin lock protecting access to the stor device pointer, use it manage the reference count as well. Signed-off-by: K. Y. Srinivasan <kys at microsoft.com> Signed-off-by: Haiyang Zhang <haiyangz at microsoft.com> Signed-off-by: Abhishek Kane <v-abkane at microsoft.com> Signed-off-by: Hank Janssen <hjanssen at microsoft.com> --- drivers/staging/hv/hyperv_storage.h | 8 ++++---- drivers/staging/hv/storvsc.c | 10 +++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/staging/hv/hyperv_storage.h b/drivers/staging/hv/hyperv_storage.h index 53b65be..d946211 100644 --- a/drivers/staging/hv/hyperv_storage.h +++ b/drivers/staging/hv/hyperv_storage.h @@ -265,7 +265,7 @@ struct storvsc_device { struct hv_device *device; /* 0 indicates the device is being destroyed */ - atomic_t ref_count; + int ref_count; bool destroy; bool drain_notify; atomic_t num_outstanding_req; @@ -296,9 +296,9 @@ static inline struct storvsc_device *get_out_stor_device( spin_lock_irqsave(&device->ext_lock, flags); stor_device = (struct storvsc_device *)device->ext; - if (stor_device && (atomic_read(&stor_device->ref_count) > 1) && + if (stor_device && (stor_device->ref_count > 1) && !stor_device->destroy) - atomic_inc(&stor_device->ref_count); + stor_device->ref_count++; else stor_device = NULL; spin_unlock_irqrestore(&device->ext_lock, flags); @@ -315,7 +315,7 @@ static inline void put_stor_device(struct hv_device *device) spin_lock_irqsave(&device->ext_lock, flags); stor_device = (struct storvsc_device *)device->ext; - atomic_dec(&stor_device->ref_count); + stor_device->ref_count--; spin_unlock_irqrestore(&device->ext_lock, flags); } diff --git a/drivers/staging/hv/storvsc.c b/drivers/staging/hv/storvsc.c index c4cb170..a41be2a 100644 --- a/drivers/staging/hv/storvsc.c +++ b/drivers/staging/hv/storvsc.c @@ -42,7 +42,7 @@ static inline struct storvsc_device *alloc_stor_device(struct hv_device *device) /* Set to 2 to allow both inbound and outbound traffics */ /* (ie get_out_stor_device() and get_in_stor_device()) to proceed. */ - atomic_set(&stor_device->ref_count, 2); + stor_device->ref_count = 2; stor_device->destroy = false; init_waitqueue_head(&stor_device->waiting_to_drain); stor_device->device = device; @@ -61,8 +61,8 @@ static inline struct storvsc_device *get_in_stor_device( spin_lock_irqsave(&device->ext_lock, flags); stor_device = (struct storvsc_device *)device->ext; - if (stor_device && atomic_read(&stor_device->ref_count)) - atomic_inc(&stor_device->ref_count); + if (stor_device && stor_device->ref_count) + stor_device->ref_count++; else stor_device = NULL; spin_unlock_irqrestore(&device->ext_lock, flags); @@ -379,7 +379,7 @@ int storvsc_dev_remove(struct hv_device *device) * pointer. */ stor_device = (struct storvsc_device *)device->ext; - atomic_dec(&stor_device->ref_count); + stor_device->destroy = true; /* @@ -391,7 +391,7 @@ int storvsc_dev_remove(struct hv_device *device) storvsc_wait_to_drain(stor_device); spin_lock_irqsave(&device->ext_lock, flags); - atomic_set(&stor_device->ref_count, 0); + stor_device->ref_count = 0; device->ext = NULL; spin_unlock_irqrestore(&device->ext_lock, flags); -- 1.7.4.1
K. Y. Srinivasan
2011-Jun-29 14:39 UTC
[PATCH 12/40] Staging: hv: storvsc: Further cleanup reference counting of stor_device
Further cleanup reference counting of stor_device - when the device is being destroyed, we will permit incoming traffic only to drain outstanding requests. Signed-off-by: K. Y. Srinivasan <kys at microsoft.com> Signed-off-by: Haiyang Zhang <haiyangz at microsoft.com> Signed-off-by: Abhishek Kane <v-abkane at microsoft.com> Signed-off-by: Hank Janssen <hjanssen at microsoft.com> --- drivers/staging/hv/hyperv_storage.h | 3 +-- drivers/staging/hv/storvsc.c | 22 +++++++++++++--------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/drivers/staging/hv/hyperv_storage.h b/drivers/staging/hv/hyperv_storage.h index d946211..a1f3e27 100644 --- a/drivers/staging/hv/hyperv_storage.h +++ b/drivers/staging/hv/hyperv_storage.h @@ -287,7 +287,6 @@ struct storvsc_device { }; -/* Get the stordevice object iff exists and its refcount > 1 */ static inline struct storvsc_device *get_out_stor_device( struct hv_device *device) { @@ -296,7 +295,7 @@ static inline struct storvsc_device *get_out_stor_device( spin_lock_irqsave(&device->ext_lock, flags); stor_device = (struct storvsc_device *)device->ext; - if (stor_device && (stor_device->ref_count > 1) && + if (stor_device && (stor_device->ref_count) && !stor_device->destroy) stor_device->ref_count++; else diff --git a/drivers/staging/hv/storvsc.c b/drivers/staging/hv/storvsc.c index a41be2a..4d13044 100644 --- a/drivers/staging/hv/storvsc.c +++ b/drivers/staging/hv/storvsc.c @@ -40,9 +40,7 @@ static inline struct storvsc_device *alloc_stor_device(struct hv_device *device) if (!stor_device) return NULL; - /* Set to 2 to allow both inbound and outbound traffics */ - /* (ie get_out_stor_device() and get_in_stor_device()) to proceed. */ - stor_device->ref_count = 2; + stor_device->ref_count = 1; stor_device->destroy = false; init_waitqueue_head(&stor_device->waiting_to_drain); stor_device->device = device; @@ -51,8 +49,6 @@ static inline struct storvsc_device *alloc_stor_device(struct hv_device *device) return stor_device; } - -/* Get the stordevice object iff exists and its refcount > 0 */ static inline struct storvsc_device *get_in_stor_device( struct hv_device *device) { @@ -61,10 +57,18 @@ static inline struct storvsc_device *get_in_stor_device( spin_lock_irqsave(&device->ext_lock, flags); stor_device = (struct storvsc_device *)device->ext; - if (stor_device && stor_device->ref_count) - stor_device->ref_count++; - else - stor_device = NULL; + if (!stor_device) + goto cleanup; + + /* + * If the device is being destroyed; allow incoming + * traffic only to cleanup outstanding requests. + */ + if (stor_device->destroy && + (atomic_read(&stor_device->num_outstanding_req) == 0)) + goto cleanup; + stor_device->ref_count++; +cleanup: spin_unlock_irqrestore(&device->ext_lock, flags); return stor_device; -- 1.7.4.1
K. Y. Srinivasan
2011-Jun-29 14:39 UTC
[PATCH 13/40] Staging: hv: netvsc: Introduce code to properly manage outstanding sends
Introduce code to properly manage outstanding sends. Signed-off-by: K. Y. Srinivasan <kys at microsoft.com> Signed-off-by: Haiyang Zhang <haiyangz at microsoft.com> Signed-off-by: Abhishek Kane <v-abkane at microsoft.com> Signed-off-by: Hank Janssen <hjanssen at microsoft.com> --- drivers/staging/hv/hyperv_net.h | 10 ++++++++++ drivers/staging/hv/netvsc.c | 6 +++++- 2 files changed, 15 insertions(+), 1 deletions(-) diff --git a/drivers/staging/hv/hyperv_net.h b/drivers/staging/hv/hyperv_net.h index 5782fea..b5bee9e 100644 --- a/drivers/staging/hv/hyperv_net.h +++ b/drivers/staging/hv/hyperv_net.h @@ -371,6 +371,8 @@ struct netvsc_device { atomic_t refcnt; atomic_t num_outstanding_sends; + bool drain_notify; + wait_queue_head_t waiting_to_drain; /* * List of free preallocated hv_netvsc_packet to represent receive * packet @@ -1051,6 +1053,14 @@ struct rndis_filter_packet { #define NDIS_PACKET_TYPE_FUNCTIONAL 0x00000400 #define NDIS_PACKET_TYPE_MAC_FRAME 0x00000800 +static inline void netvsc_wait_to_drain(struct netvsc_device *dev) +{ + dev->drain_notify = true; + wait_event(dev->waiting_to_drain, + atomic_read(&dev->num_outstanding_sends) == 0); + dev->drain_notify = false; +} + #endif /* _HYPERV_NET_H */ diff --git a/drivers/staging/hv/netvsc.c b/drivers/staging/hv/netvsc.c index 551537a..bdd5c2b 100644 --- a/drivers/staging/hv/netvsc.c +++ b/drivers/staging/hv/netvsc.c @@ -42,6 +42,8 @@ static struct netvsc_device *alloc_net_device(struct hv_device *device) /* Set to 2 to allow both inbound and outbound traffic */ atomic_cmpxchg(&net_device->refcnt, 0, 2); + net_device->drain_notify = false; + init_waitqueue_head(&net_device->waiting_to_drain); net_device->dev = device; device->ext = net_device; @@ -483,7 +485,9 @@ static void netvsc_send_completion(struct hv_device *device, nvsc_packet->completion.send.send_completion( nvsc_packet->completion.send.send_completion_ctx); - atomic_dec(&net_device->num_outstanding_sends); + if (atomic_dec_and_test(&net_device->num_outstanding_sends) && + net_device->drain_notify) + wake_up(&net_device->waiting_to_drain); } else { netdev_err(ndev, "Unknown send completion packet type- " "%d received!!\n", nvsp_packet->hdr.msg_type); -- 1.7.4.1
K. Y. Srinivasan
2011-Jun-29 14:39 UTC
[PATCH 14/40] Staging: hv: netvsc: Inline the code for free_net_device()
Inline the code for free_net_device(). Signed-off-by: K. Y. Srinivasan <kys at microsoft.com> Signed-off-by: Haiyang Zhang <haiyangz at microsoft.com> Signed-off-by: Abhishek Kane <v-abkane at microsoft.com> Signed-off-by: Hank Janssen <hjanssen at microsoft.com> --- drivers/staging/hv/netvsc.c | 12 ++---------- 1 files changed, 2 insertions(+), 10 deletions(-) diff --git a/drivers/staging/hv/netvsc.c b/drivers/staging/hv/netvsc.c index bdd5c2b..c3c934c 100644 --- a/drivers/staging/hv/netvsc.c +++ b/drivers/staging/hv/netvsc.c @@ -51,14 +51,6 @@ static struct netvsc_device *alloc_net_device(struct hv_device *device) return net_device; } -static void free_net_device(struct netvsc_device *device) -{ - WARN_ON(atomic_read(&device->refcnt) != 0); - device->dev->ext = NULL; - kfree(device); -} - - /* Get the net device object iff exists and its refcount > 1 */ static struct netvsc_device *get_outbound_net_device(struct hv_device *device) { @@ -444,7 +436,7 @@ int netvsc_device_remove(struct hv_device *device) kfree(netvsc_packet); } - free_net_device(net_device); + kfree(net_device); return 0; } @@ -994,7 +986,7 @@ cleanup: release_outbound_net_device(device); release_inbound_net_device(device); - free_net_device(net_device); + kfree(net_device); } return ret; -- 1.7.4.1
K. Y. Srinivasan
2011-Jun-29 14:39 UTC
[PATCH 15/40] Staging: hv: netvsc: Cleanup alloc_net_device()
Cleanup alloc_net_device(); we can directly set the refcnt. Signed-off-by: K. Y. Srinivasan <kys at microsoft.com> Signed-off-by: Haiyang Zhang <haiyangz at microsoft.com> Signed-off-by: Abhishek Kane <v-abkane at microsoft.com> Signed-off-by: Hank Janssen <hjanssen at microsoft.com> --- drivers/staging/hv/netvsc.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/drivers/staging/hv/netvsc.c b/drivers/staging/hv/netvsc.c index c3c934c..56749ca 100644 --- a/drivers/staging/hv/netvsc.c +++ b/drivers/staging/hv/netvsc.c @@ -41,7 +41,7 @@ static struct netvsc_device *alloc_net_device(struct hv_device *device) return NULL; /* Set to 2 to allow both inbound and outbound traffic */ - atomic_cmpxchg(&net_device->refcnt, 0, 2); + atomic_set(&net_device->refcnt, 2); net_device->drain_notify = false; init_waitqueue_head(&net_device->waiting_to_drain); -- 1.7.4.1
K. Y. Srinivasan
2011-Jun-29 14:39 UTC
[PATCH 16/40] Staging: hv: netvsc: Introduce state to manage the lifecycle of net device
In preparation for cleaning up reference counts, introduce state to manage the lifecycle of net device. Signed-off-by: K. Y. Srinivasan <kys at microsoft.com> Signed-off-by: Haiyang Zhang <haiyangz at microsoft.com> Signed-off-by: Abhishek Kane <v-abkane at microsoft.com> Signed-off-by: Hank Janssen <hjanssen at microsoft.com> --- drivers/staging/hv/hyperv_net.h | 1 + drivers/staging/hv/netvsc.c | 2 ++ 2 files changed, 3 insertions(+), 0 deletions(-) diff --git a/drivers/staging/hv/hyperv_net.h b/drivers/staging/hv/hyperv_net.h index b5bee9e..c6836be 100644 --- a/drivers/staging/hv/hyperv_net.h +++ b/drivers/staging/hv/hyperv_net.h @@ -371,6 +371,7 @@ struct netvsc_device { atomic_t refcnt; atomic_t num_outstanding_sends; + bool destroy; bool drain_notify; wait_queue_head_t waiting_to_drain; /* diff --git a/drivers/staging/hv/netvsc.c b/drivers/staging/hv/netvsc.c index 56749ca..f03018c 100644 --- a/drivers/staging/hv/netvsc.c +++ b/drivers/staging/hv/netvsc.c @@ -43,6 +43,7 @@ static struct netvsc_device *alloc_net_device(struct hv_device *device) /* Set to 2 to allow both inbound and outbound traffic */ atomic_set(&net_device->refcnt, 2); net_device->drain_notify = false; + net_device->destroy = false; init_waitqueue_head(&net_device->waiting_to_drain); net_device->dev = device; @@ -409,6 +410,7 @@ int netvsc_device_remove(struct hv_device *device) netdev_err(ndev, "No net device present!!\n"); return -ENODEV; } + net_device->destroy = true; /* Wait for all send completions */ while (atomic_read(&net_device->num_outstanding_sends)) { -- 1.7.4.1
K. Y. Srinivasan
2011-Jun-29 14:39 UTC
[PATCH 17/40] Staging: hv: netvsc: Use the newly introduced lock in accessing ext field
The current reference counting mechanism is broken; use the newly introduced lock to access the net_device pointer in struct hv_device. Signed-off-by: K. Y. Srinivasan <kys at microsoft.com> Signed-off-by: Haiyang Zhang <haiyangz at microsoft.com> Signed-off-by: Abhishek Kane <v-abkane at microsoft.com> Signed-off-by: Hank Janssen <hjanssen at microsoft.com> --- drivers/staging/hv/netvsc.c | 24 ++++++++++++++++++++++-- 1 files changed, 22 insertions(+), 2 deletions(-) diff --git a/drivers/staging/hv/netvsc.c b/drivers/staging/hv/netvsc.c index f03018c..531de63 100644 --- a/drivers/staging/hv/netvsc.c +++ b/drivers/staging/hv/netvsc.c @@ -56,12 +56,15 @@ static struct netvsc_device *alloc_net_device(struct hv_device *device) static struct netvsc_device *get_outbound_net_device(struct hv_device *device) { struct netvsc_device *net_device; + unsigned long flags; + spin_lock_irqsave(&device->ext_lock, flags); net_device = device->ext; if (net_device && atomic_read(&net_device->refcnt) > 1) atomic_inc(&net_device->refcnt); else net_device = NULL; + spin_unlock_irqrestore(&device->ext_lock, flags); return net_device; } @@ -70,38 +73,50 @@ static struct netvsc_device *get_outbound_net_device(struct hv_device *device) static struct netvsc_device *get_inbound_net_device(struct hv_device *device) { struct netvsc_device *net_device; + unsigned long flags; + spin_lock_irqsave(&device->ext_lock, flags); net_device = device->ext; if (net_device && atomic_read(&net_device->refcnt)) atomic_inc(&net_device->refcnt); else net_device = NULL; + spin_unlock_irqrestore(&device->ext_lock, flags); return net_device; } static void put_net_device(struct hv_device *device) { struct netvsc_device *net_device; + unsigned long flags; + + spin_lock_irqsave(&device->ext_lock, flags); net_device = device->ext; atomic_dec(&net_device->refcnt); + spin_unlock_irqrestore(&device->ext_lock, flags); } static struct netvsc_device *release_outbound_net_device( struct hv_device *device) { struct netvsc_device *net_device; + unsigned long flags; + spin_lock_irqsave(&device->ext_lock, flags); net_device = device->ext; - if (net_device == NULL) + if (net_device == NULL) { + spin_unlock_irqrestore(&device->ext_lock, flags); return NULL; + } /* Busy wait until the ref drop to 2, then set it to 1 */ while (atomic_cmpxchg(&net_device->refcnt, 2, 1) != 2) udelay(100); + spin_unlock_irqrestore(&device->ext_lock, flags); return net_device; } @@ -109,16 +124,21 @@ static struct netvsc_device *release_inbound_net_device( struct hv_device *device) { struct netvsc_device *net_device; + unsigned long flags; + spin_lock_irqsave(&device->ext_lock, flags); net_device = device->ext; - if (net_device == NULL) + if (net_device == NULL) { + spin_unlock_irqrestore(&device->ext_lock, flags); return NULL; + } /* Busy wait until the ref drop to 1, then set it to 0 */ while (atomic_cmpxchg(&net_device->refcnt, 1, 0) != 1) udelay(100); device->ext = NULL; + spin_unlock_irqrestore(&device->ext_lock, flags); return net_device; } -- 1.7.4.1
K. Y. Srinivasan
2011-Jun-29 14:39 UTC
[PATCH 18/40] Staging: hv: netvsc: Prevent outgoing traffic when netvsc dev is destroyed
Prevent outgoing traffic when netvsc dev is destroyed. Signed-off-by: K. Y. Srinivasan <kys at microsoft.com> Signed-off-by: Haiyang Zhang <haiyangz at microsoft.com> Signed-off-by: Abhishek Kane <v-abkane at microsoft.com> Signed-off-by: Hank Janssen <hjanssen at microsoft.com> --- drivers/staging/hv/netvsc.c | 3 ++- 1 files changed, 2 insertions(+), 1 deletions(-) diff --git a/drivers/staging/hv/netvsc.c b/drivers/staging/hv/netvsc.c index 531de63..3ac0e17 100644 --- a/drivers/staging/hv/netvsc.c +++ b/drivers/staging/hv/netvsc.c @@ -60,7 +60,8 @@ static struct netvsc_device *get_outbound_net_device(struct hv_device *device) spin_lock_irqsave(&device->ext_lock, flags); net_device = device->ext; - if (net_device && atomic_read(&net_device->refcnt) > 1) + if (net_device && (atomic_read(&net_device->refcnt) > 1) && + !net_device->destroy) atomic_inc(&net_device->refcnt); else net_device = NULL; -- 1.7.4.1
K. Y. Srinivasan
2011-Jun-29 14:39 UTC
[PATCH 19/40] Staging: hv: netvsc: Get rid of release_outbound_net_device() by inlining the code
Get rid of release_outbound_net_device() by inlining the code. Signed-off-by: K. Y. Srinivasan <kys at microsoft.com> Signed-off-by: Haiyang Zhang <haiyangz at microsoft.com> Signed-off-by: Abhishek Kane <v-abkane at microsoft.com> Signed-off-by: Hank Janssen <hjanssen at microsoft.com> --- drivers/staging/hv/netvsc.c | 35 +++++++---------------------------- 1 files changed, 7 insertions(+), 28 deletions(-) diff --git a/drivers/staging/hv/netvsc.c b/drivers/staging/hv/netvsc.c index 3ac0e17..7761370 100644 --- a/drivers/staging/hv/netvsc.c +++ b/drivers/staging/hv/netvsc.c @@ -100,27 +100,6 @@ static void put_net_device(struct hv_device *device) spin_unlock_irqrestore(&device->ext_lock, flags); } -static struct netvsc_device *release_outbound_net_device( - struct hv_device *device) -{ - struct netvsc_device *net_device; - unsigned long flags; - - spin_lock_irqsave(&device->ext_lock, flags); - net_device = device->ext; - if (net_device == NULL) { - spin_unlock_irqrestore(&device->ext_lock, flags); - return NULL; - } - - /* Busy wait until the ref drop to 2, then set it to 1 */ - while (atomic_cmpxchg(&net_device->refcnt, 2, 1) != 2) - udelay(100); - - spin_unlock_irqrestore(&device->ext_lock, flags); - return net_device; -} - static struct netvsc_device *release_inbound_net_device( struct hv_device *device) { @@ -423,14 +402,15 @@ int netvsc_device_remove(struct hv_device *device) { struct netvsc_device *net_device; struct hv_netvsc_packet *netvsc_packet, *pos; - struct net_device *ndev = dev_get_drvdata(&device->device); /* Stop outbound traffic ie sends and receives completions */ - net_device = release_outbound_net_device(device); - if (!net_device) { - netdev_err(ndev, "No net device present!!\n"); - return -ENODEV; - } + + /* + * Since we currently hold a reference on the net_device, + * it is safe to dereference the ext pointer. + */ + net_device = (struct netvsc_device *)device->ext; + atomic_dec(&net_device->refcnt); net_device->destroy = true; /* Wait for all send completions */ @@ -1006,7 +986,6 @@ cleanup: kfree(packet); } - release_outbound_net_device(device); release_inbound_net_device(device); kfree(net_device); -- 1.7.4.1
K. Y. Srinivasan
2011-Jun-29 14:39 UTC
[PATCH 20/40] Staging: hv: netvsc: Get rid of release_inbound_net_device() by inlining the code
Get rid of release_inbound_net_device() by inlining the code. Signed-off-by: K. Y. Srinivasan <kys at microsoft.com> Signed-off-by: Haiyang Zhang <haiyangz at microsoft.com> Signed-off-by: Abhishek Kane <v-abkane at microsoft.com> Signed-off-by: Hank Janssen <hjanssen at microsoft.com> --- drivers/staging/hv/netvsc.c | 44 +++++++++--------------------------------- 1 files changed, 10 insertions(+), 34 deletions(-) diff --git a/drivers/staging/hv/netvsc.c b/drivers/staging/hv/netvsc.c index 7761370..f930b9e 100644 --- a/drivers/staging/hv/netvsc.c +++ b/drivers/staging/hv/netvsc.c @@ -100,28 +100,6 @@ static void put_net_device(struct hv_device *device) spin_unlock_irqrestore(&device->ext_lock, flags); } -static struct netvsc_device *release_inbound_net_device( - struct hv_device *device) -{ - struct netvsc_device *net_device; - unsigned long flags; - - spin_lock_irqsave(&device->ext_lock, flags); - net_device = device->ext; - if (net_device == NULL) { - spin_unlock_irqrestore(&device->ext_lock, flags); - return NULL; - } - - /* Busy wait until the ref drop to 1, then set it to 0 */ - while (atomic_cmpxchg(&net_device->refcnt, 1, 0) != 1) - udelay(100); - - device->ext = NULL; - spin_unlock_irqrestore(&device->ext_lock, flags); - return net_device; -} - static int netvsc_destroy_recv_buf(struct netvsc_device *net_device) { struct nvsp_message *revoke_packet; @@ -402,6 +380,7 @@ int netvsc_device_remove(struct hv_device *device) { struct netvsc_device *net_device; struct hv_netvsc_packet *netvsc_packet, *pos; + unsigned long flags; /* Stop outbound traffic ie sends and receives completions */ @@ -413,18 +392,17 @@ int netvsc_device_remove(struct hv_device *device) atomic_dec(&net_device->refcnt); net_device->destroy = true; - /* Wait for all send completions */ - while (atomic_read(&net_device->num_outstanding_sends)) { - dev_info(&device->device, - "waiting for %d requests to complete...\n", - atomic_read(&net_device->num_outstanding_sends)); - udelay(100); - } - + /* + * At this point, all outbound traffic is disabled. Wait + * for outstanding sends to drain out. + */ + netvsc_wait_to_drain(net_device); netvsc_disconnect_vsp(net_device); - /* Stop inbound traffic ie receives and sends completions */ - net_device = release_inbound_net_device(device); + spin_lock_irqsave(&device->ext_lock, flags); + atomic_set(&net_device->refcnt, 0); + device->ext = NULL; + spin_unlock_irqrestore(&device->ext_lock, flags); /* At this point, no one should be accessing netDevice except in here */ dev_notice(&device->device, "net device safe to remove\n"); @@ -986,8 +964,6 @@ cleanup: kfree(packet); } - release_inbound_net_device(device); - kfree(net_device); } -- 1.7.4.1
K. Y. Srinivasan
2011-Jun-29 14:39 UTC
[PATCH 21/40] Staging: hv: netvsc: Leverage the spinlock to manage ref_cnt
We are using the lock to access the net_device; use this lock to manage the ref_cnt as well. Signed-off-by: K. Y. Srinivasan <kys at microsoft.com> Signed-off-by: Haiyang Zhang <haiyangz at microsoft.com> Signed-off-by: Abhishek Kane <v-abkane at microsoft.com> Signed-off-by: Hank Janssen <hjanssen at microsoft.com> --- drivers/staging/hv/hyperv_net.h | 2 +- drivers/staging/hv/netvsc.c | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/staging/hv/hyperv_net.h b/drivers/staging/hv/hyperv_net.h index c6836be..058ca79 100644 --- a/drivers/staging/hv/hyperv_net.h +++ b/drivers/staging/hv/hyperv_net.h @@ -369,7 +369,7 @@ struct nvsp_message { struct netvsc_device { struct hv_device *dev; - atomic_t refcnt; + int refcnt; atomic_t num_outstanding_sends; bool destroy; bool drain_notify; diff --git a/drivers/staging/hv/netvsc.c b/drivers/staging/hv/netvsc.c index f930b9e..9ee264c 100644 --- a/drivers/staging/hv/netvsc.c +++ b/drivers/staging/hv/netvsc.c @@ -41,7 +41,7 @@ static struct netvsc_device *alloc_net_device(struct hv_device *device) return NULL; /* Set to 2 to allow both inbound and outbound traffic */ - atomic_set(&net_device->refcnt, 2); + net_device->refcnt = 2; net_device->drain_notify = false; net_device->destroy = false; init_waitqueue_head(&net_device->waiting_to_drain); @@ -60,9 +60,9 @@ static struct netvsc_device *get_outbound_net_device(struct hv_device *device) spin_lock_irqsave(&device->ext_lock, flags); net_device = device->ext; - if (net_device && (atomic_read(&net_device->refcnt) > 1) && + if (net_device && (net_device->refcnt > 1) && !net_device->destroy) - atomic_inc(&net_device->refcnt); + net_device->refcnt++; else net_device = NULL; spin_unlock_irqrestore(&device->ext_lock, flags); @@ -78,8 +78,8 @@ static struct netvsc_device *get_inbound_net_device(struct hv_device *device) spin_lock_irqsave(&device->ext_lock, flags); net_device = device->ext; - if (net_device && atomic_read(&net_device->refcnt)) - atomic_inc(&net_device->refcnt); + if (net_device && net_device->refcnt) + net_device->refcnt++; else net_device = NULL; @@ -96,7 +96,7 @@ static void put_net_device(struct hv_device *device) net_device = device->ext; - atomic_dec(&net_device->refcnt); + net_device->refcnt--; spin_unlock_irqrestore(&device->ext_lock, flags); } @@ -389,7 +389,7 @@ int netvsc_device_remove(struct hv_device *device) * it is safe to dereference the ext pointer. */ net_device = (struct netvsc_device *)device->ext; - atomic_dec(&net_device->refcnt); + net_device->refcnt--; net_device->destroy = true; /* @@ -400,7 +400,7 @@ int netvsc_device_remove(struct hv_device *device) netvsc_disconnect_vsp(net_device); spin_lock_irqsave(&device->ext_lock, flags); - atomic_set(&net_device->refcnt, 0); + net_device->refcnt = 0; device->ext = NULL; spin_unlock_irqrestore(&device->ext_lock, flags); -- 1.7.4.1
K. Y. Srinivasan
2011-Jun-29 14:39 UTC
[PATCH 22/40] Staging: hv: netvsc: Further cleanup reference counting of netvsc_device
Signed-off-by: K. Y. Srinivasan <kys at microsoft.com> Signed-off-by: Haiyang Zhang <haiyangz at microsoft.com> Signed-off-by: Abhishek Kane <v-abkane at microsoft.com> Signed-off-by: Hank Janssen <hjanssen at microsoft.com> --- drivers/staging/hv/netvsc.c | 25 ++++++++++++++++--------- 1 files changed, 16 insertions(+), 9 deletions(-) diff --git a/drivers/staging/hv/netvsc.c b/drivers/staging/hv/netvsc.c index 9ee264c..caca82e 100644 --- a/drivers/staging/hv/netvsc.c +++ b/drivers/staging/hv/netvsc.c @@ -40,8 +40,7 @@ static struct netvsc_device *alloc_net_device(struct hv_device *device) if (!net_device) return NULL; - /* Set to 2 to allow both inbound and outbound traffic */ - net_device->refcnt = 2; + net_device->refcnt = 1; net_device->drain_notify = false; net_device->destroy = false; init_waitqueue_head(&net_device->waiting_to_drain); @@ -52,7 +51,6 @@ static struct netvsc_device *alloc_net_device(struct hv_device *device) return net_device; } -/* Get the net device object iff exists and its refcount > 1 */ static struct netvsc_device *get_outbound_net_device(struct hv_device *device) { struct netvsc_device *net_device; @@ -60,7 +58,7 @@ static struct netvsc_device *get_outbound_net_device(struct hv_device *device) spin_lock_irqsave(&device->ext_lock, flags); net_device = device->ext; - if (net_device && (net_device->refcnt > 1) && + if (net_device && (net_device->refcnt) && !net_device->destroy) net_device->refcnt++; else @@ -70,7 +68,6 @@ static struct netvsc_device *get_outbound_net_device(struct hv_device *device) return net_device; } -/* Get the net device object iff exists and its refcount > 0 */ static struct netvsc_device *get_inbound_net_device(struct hv_device *device) { struct netvsc_device *net_device; @@ -78,11 +75,21 @@ static struct netvsc_device *get_inbound_net_device(struct hv_device *device) spin_lock_irqsave(&device->ext_lock, flags); net_device = device->ext; - if (net_device && net_device->refcnt) - net_device->refcnt++; - else - net_device = NULL; + if (!net_device) + goto cleanup; + + /* + * If the device is being destroyed; allow incoming + * traffic only to cleanup outstanding requests. + */ + if (net_device->destroy && + (atomic_read(&net_device->num_outstanding_sends) == 0)) + goto cleanup; + + net_device->refcnt++; + +cleanup: spin_unlock_irqrestore(&device->ext_lock, flags); return net_device; } -- 1.7.4.1
K. Y. Srinivasan
2011-Jun-29 14:39 UTC
[PATCH 23/40] Staging: hv: storvsc: Introduce code to manage IDE devices using storvsc HBA
Introduce code to manage the (potentially four) IDE devices suning the storvsc driver. To do so, we assign distinct channels to each of these devices as well as add code to probe an IDE device. Signed-off-by: K. Y. Srinivasan <kys at microsoft.com> Signed-off-by: Haiyang Zhang <haiyangz at microsoft.com> Signed-off-by: Abhishek Kane <v-abkane at microsoft.com> Signed-off-by: Hank Janssen <hjanssen at microsoft.com> --- drivers/staging/hv/hyperv_storage.h | 13 +++++++++ drivers/staging/hv/storvsc_drv.c | 50 +++++++++++++++++++++++++++++++++- 2 files changed, 61 insertions(+), 2 deletions(-) diff --git a/drivers/staging/hv/hyperv_storage.h b/drivers/staging/hv/hyperv_storage.h index a1f3e27..a15a53b 100644 --- a/drivers/staging/hv/hyperv_storage.h +++ b/drivers/staging/hv/hyperv_storage.h @@ -25,6 +25,19 @@ #ifndef _HYPERV_STORAGE_H #define _HYPERV_STORAGE_H +/* + * We want to manage the IDE devices using standard Linux SCSI drivers + * using the storvsc driver. + * Define special channels to support this. + */ + +#define HV_MAX_IDE_DEVICES 4 +#define HV_IDE_BASE_CHANNEL 10 +#define HV_IDE0_DEV1 HV_IDE_BASE_CHANNEL +#define HV_IDE0_DEV2 (HV_IDE_BASE_CHANNEL + 1) +#define HV_IDE1_DEV1 (HV_IDE_BASE_CHANNEL + 2) +#define HV_IDE1_DEV2 (HV_IDE_BASE_CHANNEL + 3) + /* vstorage.w revision number. This is used in the case of a version match, */ /* to alert the user that structure sizes may be mismatched even though the */ diff --git a/drivers/staging/hv/storvsc_drv.c b/drivers/staging/hv/storvsc_drv.c index 8d5be51..cf659d7 100644 --- a/drivers/staging/hv/storvsc_drv.c +++ b/drivers/staging/hv/storvsc_drv.c @@ -61,6 +61,54 @@ struct storvsc_cmd_request { struct hv_storvsc_request request; }; +static struct Scsi_Host *storvsc_host; + +/* + * State to manage IDE devices that register with the storvsc driver. + * + */ +static struct hv_device *ide_devices[HV_MAX_IDE_DEVICES]; + +static void storvsc_get_ide_info(struct hv_device *dev, int *target, int *path) +{ + *target + dev->dev_instance.data[5] << 8 | dev->dev_instance.data[4]; + + *path + dev->dev_instance.data[3] << 24 | dev->dev_instance.data[2] << 16 | + dev->dev_instance.data[1] << 8 | dev->dev_instance.data[0]; +} + +int storvsc_ide_probe(struct hv_device *dev) +{ + struct storvsc_device_info dev_info; + int target, path, channel; + int ret; + + dev_info.ring_buffer_size = BLKVSC_RING_BUFFER_SIZE; + ret = storvsc_dev_add(dev, &dev_info); + + if (ret) + return ret; + + storvsc_get_ide_info(dev, &target, &path); + + if (path) + /* IDE controller 1. */ + if (target) + channel = HV_IDE1_DEV2; + else + channel = HV_IDE1_DEV1; + else + /* IDE controller 0 */ + if (target) + channel = HV_IDE0_DEV2; + else + channel = HV_IDE0_DEV1; + + ide_devices[channel - HV_IDE_BASE_CHANNEL] = dev; + return scsi_add_device(storvsc_host, channel, target, 0); +} static int storvsc_device_alloc(struct scsi_device *sdevice) { @@ -469,7 +517,6 @@ static int storvsc_queuecommand_lck(struct scsi_cmnd *scmnd, unsigned int sg_count = 0; struct vmscsi_request *vm_srb; - /* If retrying, no need to prep the cmd */ if (scmnd->host_scribble) { @@ -707,7 +754,6 @@ static int storvsc_probe(struct hv_device *device) scsi_host_put(host); return -ENODEV; } - scsi_scan_host(host); return ret; } -- 1.7.4.1
K. Y. Srinivasan
2011-Jun-29 14:39 UTC
[PATCH 24/40] Staging: hv: storvsc: On I/O get the correct IDE device
We use the channel number to distinguish an IDE device managed by the storvsc driver from scsi devices. Add code to get the correct device pointer based on the channel number. Signed-off-by: K. Y. Srinivasan <kys at microsoft.com> Signed-off-by: Haiyang Zhang <haiyangz at microsoft.com> Signed-off-by: Abhishek Kane <v-abkane at microsoft.com> Signed-off-by: Hank Janssen <hjanssen at microsoft.com> --- drivers/staging/hv/storvsc_drv.c | 10 ++++++++++ 1 files changed, 10 insertions(+), 0 deletions(-) diff --git a/drivers/staging/hv/storvsc_drv.c b/drivers/staging/hv/storvsc_drv.c index cf659d7..fcc3f5d 100644 --- a/drivers/staging/hv/storvsc_drv.c +++ b/drivers/staging/hv/storvsc_drv.c @@ -517,6 +517,16 @@ static int storvsc_queuecommand_lck(struct scsi_cmnd *scmnd, unsigned int sg_count = 0; struct vmscsi_request *vm_srb; + if (scmnd->device->channel >= HV_IDE_BASE_CHANNEL) { + int channel = scmnd->device->channel; + + /* + * This is an IDE device; get the right dev. + */ + + dev = ide_devices[channel - HV_IDE_BASE_CHANNEL]; + } + /* If retrying, no need to prep the cmd */ if (scmnd->host_scribble) { -- 1.7.4.1
K. Y. Srinivasan
2011-Jun-29 14:39 UTC
[PATCH 25/40] Staging: hv: storvsc: Add state to manage the lifecycle of emulated HBA
We setup a single emulated HBA for managing all IDE devices. To properly deal with unloading of the driver, establish state to track who should cleanup the emulated HBA. Signed-off-by: K. Y. Srinivasan <kys at microsoft.com> Signed-off-by: Haiyang Zhang <haiyangz at microsoft.com> Signed-off-by: Abhishek Kane <v-abkane at microsoft.com> Signed-off-by: Hank Janssen <hjanssen at microsoft.com> --- drivers/staging/hv/hyperv_storage.h | 1 + drivers/staging/hv/storvsc.c | 1 + drivers/staging/hv/storvsc_drv.c | 9 ++++++++- 3 files changed, 10 insertions(+), 1 deletions(-) diff --git a/drivers/staging/hv/hyperv_storage.h b/drivers/staging/hv/hyperv_storage.h index a15a53b..865ede1 100644 --- a/drivers/staging/hv/hyperv_storage.h +++ b/drivers/staging/hv/hyperv_storage.h @@ -281,6 +281,7 @@ struct storvsc_device { int ref_count; bool destroy; bool drain_notify; + bool hba_owner; atomic_t num_outstanding_req; wait_queue_head_t waiting_to_drain; diff --git a/drivers/staging/hv/storvsc.c b/drivers/staging/hv/storvsc.c index 4d13044..c06f750 100644 --- a/drivers/staging/hv/storvsc.c +++ b/drivers/staging/hv/storvsc.c @@ -42,6 +42,7 @@ static inline struct storvsc_device *alloc_stor_device(struct hv_device *device) stor_device->ref_count = 1; stor_device->destroy = false; + stor_device->hba_owner = false; init_waitqueue_head(&stor_device->waiting_to_drain); stor_device->device = device; device->ext = stor_device; diff --git a/drivers/staging/hv/storvsc_drv.c b/drivers/staging/hv/storvsc_drv.c index fcc3f5d..898a311 100644 --- a/drivers/staging/hv/storvsc_drv.c +++ b/drivers/staging/hv/storvsc_drv.c @@ -349,6 +349,10 @@ static int storvsc_remove(struct hv_device *dev) struct Scsi_Host *host = dev_get_drvdata(&dev->device); struct hv_host_device *host_dev (struct hv_host_device *)host->hostdata; + struct storvsc_device *stor_dev = dev->ext; + + if (!stor_dev->hba_owner) + return 0; scsi_remove_host(host); @@ -743,7 +747,10 @@ static int storvsc_probe(struct hv_device *device) scsi_host_put(host); return -ENODEV; } - + /* + * This stor device owns the HBA; capture that state. + */ + ((struct storvsc_device *)device->ext)->hba_owner = true; host_dev->path = device_info.path_id; host_dev->target = device_info.target_id; -- 1.7.4.1
K. Y. Srinivasan
2011-Jun-29 14:39 UTC
[PATCH 26/40] Staging: hv: storvsc: Handle probing IDE devices
Add code is storvsc_probe() to handle IDE devices. We currently boot off of the first IDE disk. The emulated HBA assigned to manage the IDE disks will be owned by this (boot) device. Subsequent IDE devices that may be discovered will be managed by this HBA. Signed-off-by: K. Y. Srinivasan <kys at microsoft.com> Signed-off-by: Haiyang Zhang <haiyangz at microsoft.com> Signed-off-by: Abhishek Kane <v-abkane at microsoft.com> Signed-off-by: Hank Janssen <hjanssen at microsoft.com> --- drivers/staging/hv/storvsc_drv.c | 23 ++++++++++++++++++++--- 1 files changed, 20 insertions(+), 3 deletions(-) diff --git a/drivers/staging/hv/storvsc_drv.c b/drivers/staging/hv/storvsc_drv.c index 898a311..c0d6e16 100644 --- a/drivers/staging/hv/storvsc_drv.c +++ b/drivers/staging/hv/storvsc_drv.c @@ -79,7 +79,7 @@ static void storvsc_get_ide_info(struct hv_device *dev, int *target, int *path) dev->dev_instance.data[1] << 8 | dev->dev_instance.data[0]; } -int storvsc_ide_probe(struct hv_device *dev) +static int storvsc_ide_probe(struct hv_device *dev) { struct storvsc_device_info dev_info; int target, path, channel; @@ -713,6 +713,15 @@ static int storvsc_probe(struct hv_device *device) struct Scsi_Host *host; struct hv_host_device *host_dev; struct storvsc_device_info device_info; + bool dev_is_ide; + int path, target; + + if (!strcmp(device->device_type, "hv_block")) { + dev_is_ide = true; + if (storvsc_host) + return storvsc_ide_probe(device); + } else + dev_is_ide = false; host = scsi_host_alloc(&scsi_driver, sizeof(struct hv_host_device)); @@ -751,8 +760,15 @@ static int storvsc_probe(struct hv_device *device) * This stor device owns the HBA; capture that state. */ ((struct storvsc_device *)device->ext)->hba_owner = true; - host_dev->path = device_info.path_id; - host_dev->target = device_info.target_id; + + if (dev_is_ide) { + storvsc_get_ide_info(device, &target, &path); + host_dev->path = path; + host_dev->target = target; + } else { + host_dev->path = device_info.path_id; + host_dev->target = device_info.target_id; + } /* max # of devices per target */ host->max_lun = STORVSC_MAX_LUNS_PER_TARGET; @@ -772,6 +788,7 @@ static int storvsc_probe(struct hv_device *device) return -ENODEV; } scsi_scan_host(host); + storvsc_host = host; return ret; } -- 1.7.4.1
K. Y. Srinivasan
2011-Jun-29 14:39 UTC
[PATCH 29/40] Staging: hv: storvsc: Optimize bounce buffer handling for the "write" case
Optimize bounce buffer handling for the "write" case; we need to copy from bounce buffer only when we are performing a "read" operation. Signed-off-by: K. Y. Srinivasan <kys at microsoft.com> Signed-off-by: Haiyang Zhang <haiyangz at microsoft.com> Signed-off-by: Abhishek Kane <v-abkane at microsoft.com> Signed-off-by: Hank Janssen <hjanssen at microsoft.com> --- drivers/staging/hv/storvsc_drv.c | 11 ++++++----- 1 files changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/staging/hv/storvsc_drv.c b/drivers/staging/hv/storvsc_drv.c index fc53bf6..c6838fd 100644 --- a/drivers/staging/hv/storvsc_drv.c +++ b/drivers/staging/hv/storvsc_drv.c @@ -478,17 +478,18 @@ static void storvsc_commmand_completion(struct hv_storvsc_request *request) struct scsi_sense_hdr sense_hdr; struct vmscsi_request *vm_srb; + vm_srb = &request->vstor_packet.vm_srb; if (cmd_request->bounce_sgl_count) { - /* FIXME: We can optimize on writes by just skipping this */ - copy_from_bounce_buffer(scsi_sglist(scmnd), + if (vm_srb->data_in == READ_TYPE) { + copy_from_bounce_buffer(scsi_sglist(scmnd), cmd_request->bounce_sgl, scsi_sg_count(scmnd)); - destroy_bounce_buffer(cmd_request->bounce_sgl, - cmd_request->bounce_sgl_count); + destroy_bounce_buffer(cmd_request->bounce_sgl, + cmd_request->bounce_sgl_count); + } } - vm_srb = &request->vstor_packet.vm_srb; scmnd->result = vm_srb->scsi_status; if (scmnd->result) { -- 1.7.4.1
K. Y. Srinivasan
2011-Jun-29 14:39 UTC
[PATCH 30/40] Staging: hv: storvsc: Optimize the bounce buffer handling in the "read" case
Optimize the bounce buffer handling in the "read" case. Copy to bounce buffers (if needed) when we initiate an I/O, only if we are performing a "write" operation. Signed-off-by: K. Y. Srinivasan <kys at microsoft.com> Signed-off-by: Haiyang Zhang <haiyangz at microsoft.com> Signed-off-by: Abhishek Kane <v-abkane at microsoft.com> Signed-off-by: Hank Janssen <hjanssen at microsoft.com> --- drivers/staging/hv/storvsc_drv.c | 10 ++++------ 1 files changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/staging/hv/storvsc_drv.c b/drivers/staging/hv/storvsc_drv.c index c6838fd..23334ad 100644 --- a/drivers/staging/hv/storvsc_drv.c +++ b/drivers/staging/hv/storvsc_drv.c @@ -623,12 +623,10 @@ static int storvsc_queuecommand_lck(struct scsi_cmnd *scmnd, ALIGN(scsi_bufflen(scmnd), PAGE_SIZE) >> PAGE_SHIFT; - /* - * FIXME: We can optimize on reads by just skipping - * this - */ - copy_to_bounce_buffer(sgl, cmd_request->bounce_sgl, - scsi_sg_count(scmnd)); + if (vm_srb->data_in == WRITE_TYPE) + copy_to_bounce_buffer(sgl, + cmd_request->bounce_sgl, + scsi_sg_count(scmnd)); sgl = cmd_request->bounce_sgl; sg_count = cmd_request->bounce_sgl_count; -- 1.7.4.1
K. Y. Srinivasan
2011-Jun-29 14:39 UTC
[PATCH 32/40] Staging: hv: storvsc: Include storvsc.c in storvsc_drv.c
As part of further cleanup of our storage drivers, include the content of storvsc.c into storvsc_drv.c. Signed-off-by: K. Y. Srinivasan <kys at microsoft.com> Signed-off-by: Haiyang Zhang <haiyangz at microsoft.com> Signed-off-by: Abhishek Kane <v-abkane at microsoft.com> Signed-off-by: Hank Janssen <hjanssen at microsoft.com> --- drivers/staging/hv/Makefile | 2 +- drivers/staging/hv/storvsc.c | 527 ------------------------------------- drivers/staging/hv/storvsc_drv.c | 528 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 529 insertions(+), 528 deletions(-) delete mode 100644 drivers/staging/hv/storvsc.c diff --git a/drivers/staging/hv/Makefile b/drivers/staging/hv/Makefile index bb89437..bd176b1 100644 --- a/drivers/staging/hv/Makefile +++ b/drivers/staging/hv/Makefile @@ -7,6 +7,6 @@ obj-$(CONFIG_HYPERV_MOUSE) += hv_mouse.o hv_vmbus-y := vmbus_drv.o \ hv.o connection.o channel.o \ channel_mgmt.o ring_buffer.o -hv_storvsc-y := storvsc_drv.o storvsc.o +hv_storvsc-y := storvsc_drv.o hv_netvsc-y := netvsc_drv.o netvsc.o rndis_filter.o hv_utils-y := hv_util.o hv_kvp.o diff --git a/drivers/staging/hv/storvsc.c b/drivers/staging/hv/storvsc.c deleted file mode 100644 index c06f750..0000000 --- a/drivers/staging/hv/storvsc.c +++ /dev/null @@ -1,527 +0,0 @@ -/* - * Copyright (c) 2009, Microsoft Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307 USA. - * - * Authors: - * Haiyang Zhang <haiyangz at microsoft.com> - * Hank Janssen <hjanssen at microsoft.com> - * K. Y. Srinivasan <kys at microsoft.com> - * - */ -#include <linux/kernel.h> -#include <linux/sched.h> -#include <linux/completion.h> -#include <linux/string.h> -#include <linux/slab.h> -#include <linux/mm.h> -#include <linux/delay.h> - -#include "hyperv.h" -#include "hyperv_storage.h" - - -static inline struct storvsc_device *alloc_stor_device(struct hv_device *device) -{ - struct storvsc_device *stor_device; - - stor_device = kzalloc(sizeof(struct storvsc_device), GFP_KERNEL); - if (!stor_device) - return NULL; - - stor_device->ref_count = 1; - stor_device->destroy = false; - stor_device->hba_owner = false; - init_waitqueue_head(&stor_device->waiting_to_drain); - stor_device->device = device; - device->ext = stor_device; - - return stor_device; -} - -static inline struct storvsc_device *get_in_stor_device( - struct hv_device *device) -{ - struct storvsc_device *stor_device; - unsigned long flags; - - spin_lock_irqsave(&device->ext_lock, flags); - stor_device = (struct storvsc_device *)device->ext; - if (!stor_device) - goto cleanup; - - /* - * If the device is being destroyed; allow incoming - * traffic only to cleanup outstanding requests. - */ - if (stor_device->destroy && - (atomic_read(&stor_device->num_outstanding_req) == 0)) - goto cleanup; - stor_device->ref_count++; -cleanup: - spin_unlock_irqrestore(&device->ext_lock, flags); - - return stor_device; -} - -static int storvsc_channel_init(struct hv_device *device) -{ - struct storvsc_device *stor_device; - struct hv_storvsc_request *request; - struct vstor_packet *vstor_packet; - int ret, t; - - stor_device = get_out_stor_device(device); - if (!stor_device) - return -ENODEV; - - request = &stor_device->init_request; - vstor_packet = &request->vstor_packet; - - /* - * Now, initiate the vsc/vsp initialization protocol on the open - * channel - */ - memset(request, 0, sizeof(struct hv_storvsc_request)); - init_completion(&request->wait_event); - vstor_packet->operation = VSTOR_OPERATION_BEGIN_INITIALIZATION; - vstor_packet->flags = REQUEST_COMPLETION_FLAG; - - ret = vmbus_sendpacket(device->channel, vstor_packet, - sizeof(struct vstor_packet), - (unsigned long)request, - VM_PKT_DATA_INBAND, - VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); - if (ret != 0) - goto cleanup; - - t = wait_for_completion_timeout(&request->wait_event, 5*HZ); - if (t == 0) { - ret = -ETIMEDOUT; - goto cleanup; - } - - if (vstor_packet->operation != VSTOR_OPERATION_COMPLETE_IO || - vstor_packet->status != 0) - goto cleanup; - - - /* reuse the packet for version range supported */ - memset(vstor_packet, 0, sizeof(struct vstor_packet)); - vstor_packet->operation = VSTOR_OPERATION_QUERY_PROTOCOL_VERSION; - vstor_packet->flags = REQUEST_COMPLETION_FLAG; - - vstor_packet->version.major_minor = VMSTOR_PROTOCOL_VERSION_CURRENT; - FILL_VMSTOR_REVISION(vstor_packet->version.revision); - - ret = vmbus_sendpacket(device->channel, vstor_packet, - sizeof(struct vstor_packet), - (unsigned long)request, - VM_PKT_DATA_INBAND, - VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); - if (ret != 0) - goto cleanup; - - t = wait_for_completion_timeout(&request->wait_event, 5*HZ); - if (t == 0) { - ret = -ETIMEDOUT; - goto cleanup; - } - - if (vstor_packet->operation != VSTOR_OPERATION_COMPLETE_IO || - vstor_packet->status != 0) - goto cleanup; - - - memset(vstor_packet, 0, sizeof(struct vstor_packet)); - vstor_packet->operation = VSTOR_OPERATION_QUERY_PROPERTIES; - vstor_packet->flags = REQUEST_COMPLETION_FLAG; - vstor_packet->storage_channel_properties.port_number - stor_device->port_number; - - ret = vmbus_sendpacket(device->channel, vstor_packet, - sizeof(struct vstor_packet), - (unsigned long)request, - VM_PKT_DATA_INBAND, - VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); - - if (ret != 0) - goto cleanup; - - t = wait_for_completion_timeout(&request->wait_event, 5*HZ); - if (t == 0) { - ret = -ETIMEDOUT; - goto cleanup; - } - - if (vstor_packet->operation != VSTOR_OPERATION_COMPLETE_IO || - vstor_packet->status != 0) - goto cleanup; - - stor_device->path_id = vstor_packet->storage_channel_properties.path_id; - stor_device->target_id - = vstor_packet->storage_channel_properties.target_id; - - memset(vstor_packet, 0, sizeof(struct vstor_packet)); - vstor_packet->operation = VSTOR_OPERATION_END_INITIALIZATION; - vstor_packet->flags = REQUEST_COMPLETION_FLAG; - - ret = vmbus_sendpacket(device->channel, vstor_packet, - sizeof(struct vstor_packet), - (unsigned long)request, - VM_PKT_DATA_INBAND, - VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); - - if (ret != 0) - goto cleanup; - - t = wait_for_completion_timeout(&request->wait_event, 5*HZ); - if (t == 0) { - ret = -ETIMEDOUT; - goto cleanup; - } - - if (vstor_packet->operation != VSTOR_OPERATION_COMPLETE_IO || - vstor_packet->status != 0) - goto cleanup; - - -cleanup: - put_stor_device(device); - return ret; -} - -static void storvsc_on_io_completion(struct hv_device *device, - struct vstor_packet *vstor_packet, - struct hv_storvsc_request *request) -{ - struct storvsc_device *stor_device; - struct vstor_packet *stor_pkt; - - stor_device = (struct storvsc_device *)device->ext; - - stor_pkt = &request->vstor_packet; - - - /* Copy over the status...etc */ - stor_pkt->vm_srb.scsi_status = vstor_packet->vm_srb.scsi_status; - stor_pkt->vm_srb.srb_status = vstor_packet->vm_srb.srb_status; - stor_pkt->vm_srb.sense_info_length - vstor_packet->vm_srb.sense_info_length; - - if (vstor_packet->vm_srb.scsi_status != 0 || - vstor_packet->vm_srb.srb_status != 1){ - DPRINT_WARN(STORVSC, - "cmd 0x%x scsi status 0x%x srb status 0x%x\n", - stor_pkt->vm_srb.cdb[0], - vstor_packet->vm_srb.scsi_status, - vstor_packet->vm_srb.srb_status); - } - - if ((vstor_packet->vm_srb.scsi_status & 0xFF) == 0x02) { - /* CHECK_CONDITION */ - if (vstor_packet->vm_srb.srb_status & 0x80) { - /* autosense data available */ - DPRINT_WARN(STORVSC, "storvsc pkt %p autosense data " - "valid - len %d\n", request, - vstor_packet->vm_srb.sense_info_length); - - memcpy(request->sense_buffer, - vstor_packet->vm_srb.sense_data, - vstor_packet->vm_srb.sense_info_length); - - } - } - - stor_pkt->vm_srb.data_transfer_length - vstor_packet->vm_srb.data_transfer_length; - - request->on_io_completion(request); - - if (atomic_dec_and_test(&stor_device->num_outstanding_req) && - stor_device->drain_notify) - wake_up(&stor_device->waiting_to_drain); - - -} - -static void storvsc_on_receive(struct hv_device *device, - struct vstor_packet *vstor_packet, - struct hv_storvsc_request *request) -{ - switch (vstor_packet->operation) { - case VSTOR_OPERATION_COMPLETE_IO: - storvsc_on_io_completion(device, vstor_packet, request); - break; - case VSTOR_OPERATION_REMOVE_DEVICE: - - default: - break; - } -} - -static void storvsc_on_channel_callback(void *context) -{ - struct hv_device *device = (struct hv_device *)context; - struct storvsc_device *stor_device; - u32 bytes_recvd; - u64 request_id; - unsigned char packet[ALIGN(sizeof(struct vstor_packet), 8)]; - struct hv_storvsc_request *request; - int ret; - - - stor_device = get_in_stor_device(device); - if (!stor_device) - return; - - do { - ret = vmbus_recvpacket(device->channel, packet, - ALIGN(sizeof(struct vstor_packet), 8), - &bytes_recvd, &request_id); - if (ret == 0 && bytes_recvd > 0) { - - request = (struct hv_storvsc_request *) - (unsigned long)request_id; - - if ((request == &stor_device->init_request) || - (request == &stor_device->reset_request)) { - - memcpy(&request->vstor_packet, packet, - sizeof(struct vstor_packet)); - complete(&request->wait_event); - } else { - storvsc_on_receive(device, - (struct vstor_packet *)packet, - request); - } - } else { - break; - } - } while (1); - - put_stor_device(device); - return; -} - -static int storvsc_connect_to_vsp(struct hv_device *device, u32 ring_size) -{ - struct vmstorage_channel_properties props; - int ret; - - memset(&props, 0, sizeof(struct vmstorage_channel_properties)); - - /* Open the channel */ - ret = vmbus_open(device->channel, - ring_size, - ring_size, - (void *)&props, - sizeof(struct vmstorage_channel_properties), - storvsc_on_channel_callback, device); - - if (ret != 0) - return ret; - - ret = storvsc_channel_init(device); - - return ret; -} - -int storvsc_dev_add(struct hv_device *device, - void *additional_info) -{ - struct storvsc_device *stor_device; - struct storvsc_device_info *device_info; - int ret = 0; - - device_info = (struct storvsc_device_info *)additional_info; - stor_device = alloc_stor_device(device); - if (!stor_device) - return -ENOMEM; - - /* Save the channel properties to our storvsc channel */ - - /* - * If we support more than 1 scsi channel, we need to set the - * port number here to the scsi channel but how do we get the - * scsi channel prior to the bus scan. - * - * The host does not support this. - */ - - stor_device->port_number = device_info->port_number; - /* Send it back up */ - ret = storvsc_connect_to_vsp(device, device_info->ring_buffer_size); - if (ret) { - kfree(stor_device); - return ret; - } - device_info->path_id = stor_device->path_id; - device_info->target_id = stor_device->target_id; - - return ret; -} - -int storvsc_dev_remove(struct hv_device *device) -{ - struct storvsc_device *stor_device; - unsigned long flags; - - /* - * Since we currently hold a reference on the stor - * device, it is safe to dereference the ext - * pointer. - */ - stor_device = (struct storvsc_device *)device->ext; - - stor_device->destroy = true; - - /* - * At this point, all outbound traffic should be disable. We - * only allow inbound traffic (responses) to proceed so that - * outstanding requests can be completed. - */ - - storvsc_wait_to_drain(stor_device); - - spin_lock_irqsave(&device->ext_lock, flags); - stor_device->ref_count = 0; - device->ext = NULL; - spin_unlock_irqrestore(&device->ext_lock, flags); - - /* Close the channel */ - vmbus_close(device->channel); - - kfree(stor_device); - return 0; -} - -int storvsc_do_io(struct hv_device *device, - struct hv_storvsc_request *request) -{ - struct storvsc_device *stor_device; - struct vstor_packet *vstor_packet; - int ret = 0; - - vstor_packet = &request->vstor_packet; - stor_device = get_out_stor_device(device); - - if (!stor_device) - return -ENODEV; - - - request->device = device; - - - vstor_packet->flags |= REQUEST_COMPLETION_FLAG; - - vstor_packet->vm_srb.length = sizeof(struct vmscsi_request); - - - vstor_packet->vm_srb.sense_info_length = SENSE_BUFFER_SIZE; - - - vstor_packet->vm_srb.data_transfer_length - request->data_buffer.len; - - vstor_packet->operation = VSTOR_OPERATION_EXECUTE_SRB; - - if (request->data_buffer.len) { - ret = vmbus_sendpacket_multipagebuffer(device->channel, - &request->data_buffer, - vstor_packet, - sizeof(struct vstor_packet), - (unsigned long)request); - } else { - ret = vmbus_sendpacket(device->channel, vstor_packet, - sizeof(struct vstor_packet), - (unsigned long)request, - VM_PKT_DATA_INBAND, - VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); - } - - if (ret != 0) - return ret; - - atomic_inc(&stor_device->num_outstanding_req); - - put_stor_device(device); - return ret; -} - -/* - * The channel properties uniquely specify how the device is to be - * presented to the guest. Map this information for use by the block - * driver. For Linux guests on Hyper-V, we emulate a scsi HBA in the guest - * (storvsc_drv) and so scsi devices in the guest are handled by - * native upper level Linux drivers. Consequently, Hyper-V - * block driver, while being a generic block driver, presently does not - * deal with anything other than devices that would need to be presented - * to the guest as an IDE disk. - * - * This function maps the channel properties as embedded in the input - * parameter device_info onto information necessary to register the - * corresponding block device. - * - * Currently, there is no way to stop the emulation of the block device - * on the host side. And so, to prevent the native IDE drivers in Linux - * from taking over these devices (to be managedby Hyper-V block - * driver), we will take over if need be the major of the IDE controllers. - * - */ - -int storvsc_get_major_info(struct storvsc_device_info *device_info, - struct storvsc_major_info *major_info) -{ - static bool ide0_registered; - static bool ide1_registered; - - /* - * For now we only support IDE disks. - */ - major_info->devname = "ide"; - major_info->diskname = "hd"; - - if (device_info->path_id) { - major_info->major = 22; - if (!ide1_registered) { - major_info->do_register = true; - ide1_registered = true; - } else - major_info->do_register = false; - - if (device_info->target_id) - major_info->index = 3; - else - major_info->index = 2; - - return 0; - } else { - major_info->major = 3; - if (!ide0_registered) { - major_info->do_register = true; - ide0_registered = true; - } else - major_info->do_register = false; - - if (device_info->target_id) - major_info->index = 1; - else - major_info->index = 0; - - return 0; - } - - return -ENODEV; -} diff --git a/drivers/staging/hv/storvsc_drv.c b/drivers/staging/hv/storvsc_drv.c index 23334ad..f460741 100644 --- a/drivers/staging/hv/storvsc_drv.c +++ b/drivers/staging/hv/storvsc_drv.c @@ -41,6 +41,534 @@ static int storvsc_ringbuffer_size = STORVSC_RING_BUFFER_SIZE; module_param(storvsc_ringbuffer_size, int, S_IRUGO); MODULE_PARM_DESC(storvsc_ringbuffer_size, "Ring buffer size (bytes)"); +/* + * Copyright (c) 2009, Microsoft Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307 USA. + * + * Authors: + * Haiyang Zhang <haiyangz at microsoft.com> + * Hank Janssen <hjanssen at microsoft.com> + * K. Y. Srinivasan <kys at microsoft.com> + * + */ +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/completion.h> +#include <linux/string.h> +#include <linux/slab.h> +#include <linux/mm.h> +#include <linux/delay.h> + +#include "hyperv.h" +#include "hyperv_storage.h" + + +static inline struct storvsc_device *alloc_stor_device(struct hv_device *device) +{ + struct storvsc_device *stor_device; + + stor_device = kzalloc(sizeof(struct storvsc_device), GFP_KERNEL); + if (!stor_device) + return NULL; + + stor_device->ref_count = 1; + stor_device->destroy = false; + stor_device->hba_owner = false; + init_waitqueue_head(&stor_device->waiting_to_drain); + stor_device->device = device; + device->ext = stor_device; + + return stor_device; +} + +static inline struct storvsc_device *get_in_stor_device( + struct hv_device *device) +{ + struct storvsc_device *stor_device; + unsigned long flags; + + spin_lock_irqsave(&device->ext_lock, flags); + stor_device = (struct storvsc_device *)device->ext; + if (!stor_device) + goto cleanup; + + /* + * If the device is being destroyed; allow incoming + * traffic only to cleanup outstanding requests. + */ + if (stor_device->destroy && + (atomic_read(&stor_device->num_outstanding_req) == 0)) + goto cleanup; + stor_device->ref_count++; +cleanup: + spin_unlock_irqrestore(&device->ext_lock, flags); + + return stor_device; +} + +static int storvsc_channel_init(struct hv_device *device) +{ + struct storvsc_device *stor_device; + struct hv_storvsc_request *request; + struct vstor_packet *vstor_packet; + int ret, t; + + stor_device = get_out_stor_device(device); + if (!stor_device) + return -ENODEV; + + request = &stor_device->init_request; + vstor_packet = &request->vstor_packet; + + /* + * Now, initiate the vsc/vsp initialization protocol on the open + * channel + */ + memset(request, 0, sizeof(struct hv_storvsc_request)); + init_completion(&request->wait_event); + vstor_packet->operation = VSTOR_OPERATION_BEGIN_INITIALIZATION; + vstor_packet->flags = REQUEST_COMPLETION_FLAG; + + ret = vmbus_sendpacket(device->channel, vstor_packet, + sizeof(struct vstor_packet), + (unsigned long)request, + VM_PKT_DATA_INBAND, + VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); + if (ret != 0) + goto cleanup; + + t = wait_for_completion_timeout(&request->wait_event, 5*HZ); + if (t == 0) { + ret = -ETIMEDOUT; + goto cleanup; + } + + if (vstor_packet->operation != VSTOR_OPERATION_COMPLETE_IO || + vstor_packet->status != 0) + goto cleanup; + + + /* reuse the packet for version range supported */ + memset(vstor_packet, 0, sizeof(struct vstor_packet)); + vstor_packet->operation = VSTOR_OPERATION_QUERY_PROTOCOL_VERSION; + vstor_packet->flags = REQUEST_COMPLETION_FLAG; + + vstor_packet->version.major_minor = VMSTOR_PROTOCOL_VERSION_CURRENT; + FILL_VMSTOR_REVISION(vstor_packet->version.revision); + + ret = vmbus_sendpacket(device->channel, vstor_packet, + sizeof(struct vstor_packet), + (unsigned long)request, + VM_PKT_DATA_INBAND, + VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); + if (ret != 0) + goto cleanup; + + t = wait_for_completion_timeout(&request->wait_event, 5*HZ); + if (t == 0) { + ret = -ETIMEDOUT; + goto cleanup; + } + + if (vstor_packet->operation != VSTOR_OPERATION_COMPLETE_IO || + vstor_packet->status != 0) + goto cleanup; + + + memset(vstor_packet, 0, sizeof(struct vstor_packet)); + vstor_packet->operation = VSTOR_OPERATION_QUERY_PROPERTIES; + vstor_packet->flags = REQUEST_COMPLETION_FLAG; + vstor_packet->storage_channel_properties.port_number + stor_device->port_number; + + ret = vmbus_sendpacket(device->channel, vstor_packet, + sizeof(struct vstor_packet), + (unsigned long)request, + VM_PKT_DATA_INBAND, + VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); + + if (ret != 0) + goto cleanup; + + t = wait_for_completion_timeout(&request->wait_event, 5*HZ); + if (t == 0) { + ret = -ETIMEDOUT; + goto cleanup; + } + + if (vstor_packet->operation != VSTOR_OPERATION_COMPLETE_IO || + vstor_packet->status != 0) + goto cleanup; + + stor_device->path_id = vstor_packet->storage_channel_properties.path_id; + stor_device->target_id + = vstor_packet->storage_channel_properties.target_id; + + memset(vstor_packet, 0, sizeof(struct vstor_packet)); + vstor_packet->operation = VSTOR_OPERATION_END_INITIALIZATION; + vstor_packet->flags = REQUEST_COMPLETION_FLAG; + + ret = vmbus_sendpacket(device->channel, vstor_packet, + sizeof(struct vstor_packet), + (unsigned long)request, + VM_PKT_DATA_INBAND, + VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); + + if (ret != 0) + goto cleanup; + + t = wait_for_completion_timeout(&request->wait_event, 5*HZ); + if (t == 0) { + ret = -ETIMEDOUT; + goto cleanup; + } + + if (vstor_packet->operation != VSTOR_OPERATION_COMPLETE_IO || + vstor_packet->status != 0) + goto cleanup; + + +cleanup: + put_stor_device(device); + return ret; +} + +static void storvsc_on_io_completion(struct hv_device *device, + struct vstor_packet *vstor_packet, + struct hv_storvsc_request *request) +{ + struct storvsc_device *stor_device; + struct vstor_packet *stor_pkt; + + stor_device = (struct storvsc_device *)device->ext; + + stor_pkt = &request->vstor_packet; + + + /* Copy over the status...etc */ + stor_pkt->vm_srb.scsi_status = vstor_packet->vm_srb.scsi_status; + stor_pkt->vm_srb.srb_status = vstor_packet->vm_srb.srb_status; + stor_pkt->vm_srb.sense_info_length + vstor_packet->vm_srb.sense_info_length; + + if (vstor_packet->vm_srb.scsi_status != 0 || + vstor_packet->vm_srb.srb_status != 1){ + DPRINT_WARN(STORVSC, + "cmd 0x%x scsi status 0x%x srb status 0x%x\n", + stor_pkt->vm_srb.cdb[0], + vstor_packet->vm_srb.scsi_status, + vstor_packet->vm_srb.srb_status); + } + + if ((vstor_packet->vm_srb.scsi_status & 0xFF) == 0x02) { + /* CHECK_CONDITION */ + if (vstor_packet->vm_srb.srb_status & 0x80) { + /* autosense data available */ + DPRINT_WARN(STORVSC, "storvsc pkt %p autosense data " + "valid - len %d\n", request, + vstor_packet->vm_srb.sense_info_length); + + memcpy(request->sense_buffer, + vstor_packet->vm_srb.sense_data, + vstor_packet->vm_srb.sense_info_length); + + } + } + + stor_pkt->vm_srb.data_transfer_length + vstor_packet->vm_srb.data_transfer_length; + + request->on_io_completion(request); + + if (atomic_dec_and_test(&stor_device->num_outstanding_req) && + stor_device->drain_notify) + wake_up(&stor_device->waiting_to_drain); + + +} + +static void storvsc_on_receive(struct hv_device *device, + struct vstor_packet *vstor_packet, + struct hv_storvsc_request *request) +{ + switch (vstor_packet->operation) { + case VSTOR_OPERATION_COMPLETE_IO: + storvsc_on_io_completion(device, vstor_packet, request); + break; + case VSTOR_OPERATION_REMOVE_DEVICE: + + default: + break; + } +} + +static void storvsc_on_channel_callback(void *context) +{ + struct hv_device *device = (struct hv_device *)context; + struct storvsc_device *stor_device; + u32 bytes_recvd; + u64 request_id; + unsigned char packet[ALIGN(sizeof(struct vstor_packet), 8)]; + struct hv_storvsc_request *request; + int ret; + + + stor_device = get_in_stor_device(device); + if (!stor_device) + return; + + do { + ret = vmbus_recvpacket(device->channel, packet, + ALIGN(sizeof(struct vstor_packet), 8), + &bytes_recvd, &request_id); + if (ret == 0 && bytes_recvd > 0) { + + request = (struct hv_storvsc_request *) + (unsigned long)request_id; + + if ((request == &stor_device->init_request) || + (request == &stor_device->reset_request)) { + + memcpy(&request->vstor_packet, packet, + sizeof(struct vstor_packet)); + complete(&request->wait_event); + } else { + storvsc_on_receive(device, + (struct vstor_packet *)packet, + request); + } + } else { + break; + } + } while (1); + + put_stor_device(device); + return; +} + +static int storvsc_connect_to_vsp(struct hv_device *device, u32 ring_size) +{ + struct vmstorage_channel_properties props; + int ret; + + memset(&props, 0, sizeof(struct vmstorage_channel_properties)); + + /* Open the channel */ + ret = vmbus_open(device->channel, + ring_size, + ring_size, + (void *)&props, + sizeof(struct vmstorage_channel_properties), + storvsc_on_channel_callback, device); + + if (ret != 0) + return ret; + + ret = storvsc_channel_init(device); + + return ret; +} + +int storvsc_dev_add(struct hv_device *device, + void *additional_info) +{ + struct storvsc_device *stor_device; + struct storvsc_device_info *device_info; + int ret = 0; + + device_info = (struct storvsc_device_info *)additional_info; + stor_device = alloc_stor_device(device); + if (!stor_device) + return -ENOMEM; + + /* Save the channel properties to our storvsc channel */ + + /* + * If we support more than 1 scsi channel, we need to set the + * port number here to the scsi channel but how do we get the + * scsi channel prior to the bus scan. + * + * The host does not support this. + */ + + stor_device->port_number = device_info->port_number; + /* Send it back up */ + ret = storvsc_connect_to_vsp(device, device_info->ring_buffer_size); + if (ret) { + kfree(stor_device); + return ret; + } + device_info->path_id = stor_device->path_id; + device_info->target_id = stor_device->target_id; + + return ret; +} + +int storvsc_dev_remove(struct hv_device *device) +{ + struct storvsc_device *stor_device; + unsigned long flags; + + /* + * Since we currently hold a reference on the stor + * device, it is safe to dereference the ext + * pointer. + */ + stor_device = (struct storvsc_device *)device->ext; + + stor_device->destroy = true; + + /* + * At this point, all outbound traffic should be disable. We + * only allow inbound traffic (responses) to proceed so that + * outstanding requests can be completed. + */ + + storvsc_wait_to_drain(stor_device); + + spin_lock_irqsave(&device->ext_lock, flags); + stor_device->ref_count = 0; + device->ext = NULL; + spin_unlock_irqrestore(&device->ext_lock, flags); + + /* Close the channel */ + vmbus_close(device->channel); + + kfree(stor_device); + return 0; +} + +int storvsc_do_io(struct hv_device *device, + struct hv_storvsc_request *request) +{ + struct storvsc_device *stor_device; + struct vstor_packet *vstor_packet; + int ret = 0; + + vstor_packet = &request->vstor_packet; + stor_device = get_out_stor_device(device); + + if (!stor_device) + return -ENODEV; + + + request->device = device; + + + vstor_packet->flags |= REQUEST_COMPLETION_FLAG; + + vstor_packet->vm_srb.length = sizeof(struct vmscsi_request); + + + vstor_packet->vm_srb.sense_info_length = SENSE_BUFFER_SIZE; + + + vstor_packet->vm_srb.data_transfer_length + request->data_buffer.len; + + vstor_packet->operation = VSTOR_OPERATION_EXECUTE_SRB; + + if (request->data_buffer.len) { + ret = vmbus_sendpacket_multipagebuffer(device->channel, + &request->data_buffer, + vstor_packet, + sizeof(struct vstor_packet), + (unsigned long)request); + } else { + ret + vmbus_sendpacket(device->channel, vstor_packet, + sizeof(struct vstor_packet), + (unsigned long)request, + VM_PKT_DATA_INBAND, + VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); + } + + if (ret != 0) + return ret; + + atomic_inc(&stor_device->num_outstanding_req); + + put_stor_device(device); + return ret; +} + +/* + * The channel properties uniquely specify how the device is to be + * presented to the guest. Map this information for use by the block + * driver. For Linux guests on Hyper-V, we emulate a scsi HBA in the guest + * (storvsc_drv) and so scsi devices in the guest are handled by + * native upper level Linux drivers. Consequently, Hyper-V + * block driver, while being a generic block driver, presently does not + * deal with anything other than devices that would need to be presented + * to the guest as an IDE disk. + * + * This function maps the channel properties as embedded in the input + * parameter device_info onto information necessary to register the + * corresponding block device. + * + * Currently, there is no way to stop the emulation of the block device + * on the host side. And so, to prevent the native IDE drivers in Linux + * from taking over these devices (to be managedby Hyper-V block + * driver), we will take over if need be the major of the IDE controllers. + * + */ + +int storvsc_get_major_info(struct storvsc_device_info *device_info, + struct storvsc_major_info *major_info) +{ + static bool ide0_registered; + static bool ide1_registered; + + /* + * For now we only support IDE disks. + */ + major_info->devname = "ide"; + major_info->diskname = "hd"; + + if (device_info->path_id) { + major_info->major = 22; + if (!ide1_registered) { + major_info->do_register = true; + ide1_registered = true; + } else + major_info->do_register = false; + + if (device_info->target_id) + major_info->index = 3; + else + major_info->index = 2; + + return 0; + } else { + major_info->major = 3; + if (!ide0_registered) { + major_info->do_register = true; + ide0_registered = true; + } else + major_info->do_register = false; + + if (device_info->target_id) + major_info->index = 1; + else + major_info->index = 0; + + return 0; + } + + return -ENODEV; +} static const char *driver_name = "storvsc"; struct hv_host_device { -- 1.7.4.1
K. Y. Srinivasan
2011-Jun-29 14:39 UTC
[PATCH 33/40] Staging: hv: storvsc: Cleanup storvsc_drv.c after adding the contents of storvsc.c
Now, cleanup storvsc_drv.c after adding the contents of storvsc.c Signed-off-by: K. Y. Srinivasan <kys at microsoft.com> Signed-off-by: Haiyang Zhang <haiyangz at microsoft.com> Signed-off-by: Abhishek Kane <v-abkane at microsoft.com> Signed-off-by: Hank Janssen <hjanssen at microsoft.com> --- drivers/staging/hv/storvsc_drv.c | 105 +++----------------------------------- 1 files changed, 7 insertions(+), 98 deletions(-) diff --git a/drivers/staging/hv/storvsc_drv.c b/drivers/staging/hv/storvsc_drv.c index f460741..0702dc0 100644 --- a/drivers/staging/hv/storvsc_drv.c +++ b/drivers/staging/hv/storvsc_drv.c @@ -19,8 +19,15 @@ * Hank Janssen <hjanssen at microsoft.com> * K. Y. Srinivasan <kys at microsoft.com> */ + +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/completion.h> +#include <linux/string.h> #include <linux/init.h> #include <linux/slab.h> +#include <linux/mm.h> +#include <linux/delay.h> #include <linux/module.h> #include <linux/device.h> #include <linux/blkdev.h> @@ -41,39 +48,6 @@ static int storvsc_ringbuffer_size = STORVSC_RING_BUFFER_SIZE; module_param(storvsc_ringbuffer_size, int, S_IRUGO); MODULE_PARM_DESC(storvsc_ringbuffer_size, "Ring buffer size (bytes)"); -/* - * Copyright (c) 2009, Microsoft Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307 USA. - * - * Authors: - * Haiyang Zhang <haiyangz at microsoft.com> - * Hank Janssen <hjanssen at microsoft.com> - * K. Y. Srinivasan <kys at microsoft.com> - * - */ -#include <linux/kernel.h> -#include <linux/sched.h> -#include <linux/completion.h> -#include <linux/string.h> -#include <linux/slab.h> -#include <linux/mm.h> -#include <linux/delay.h> - -#include "hyperv.h" -#include "hyperv_storage.h" - static inline struct storvsc_device *alloc_stor_device(struct hv_device *device) { @@ -504,71 +478,6 @@ int storvsc_do_io(struct hv_device *device, return ret; } -/* - * The channel properties uniquely specify how the device is to be - * presented to the guest. Map this information for use by the block - * driver. For Linux guests on Hyper-V, we emulate a scsi HBA in the guest - * (storvsc_drv) and so scsi devices in the guest are handled by - * native upper level Linux drivers. Consequently, Hyper-V - * block driver, while being a generic block driver, presently does not - * deal with anything other than devices that would need to be presented - * to the guest as an IDE disk. - * - * This function maps the channel properties as embedded in the input - * parameter device_info onto information necessary to register the - * corresponding block device. - * - * Currently, there is no way to stop the emulation of the block device - * on the host side. And so, to prevent the native IDE drivers in Linux - * from taking over these devices (to be managedby Hyper-V block - * driver), we will take over if need be the major of the IDE controllers. - * - */ - -int storvsc_get_major_info(struct storvsc_device_info *device_info, - struct storvsc_major_info *major_info) -{ - static bool ide0_registered; - static bool ide1_registered; - - /* - * For now we only support IDE disks. - */ - major_info->devname = "ide"; - major_info->diskname = "hd"; - - if (device_info->path_id) { - major_info->major = 22; - if (!ide1_registered) { - major_info->do_register = true; - ide1_registered = true; - } else - major_info->do_register = false; - - if (device_info->target_id) - major_info->index = 3; - else - major_info->index = 2; - - return 0; - } else { - major_info->major = 3; - if (!ide0_registered) { - major_info->do_register = true; - ide0_registered = true; - } else - major_info->do_register = false; - - if (device_info->target_id) - major_info->index = 1; - else - major_info->index = 0; - - return 0; - } - - return -ENODEV; -} static const char *driver_name = "storvsc"; struct hv_host_device { -- 1.7.4.1
K. Y. Srinivasan
2011-Jun-29 14:39 UTC
[PATCH 35/40] Staging: hv: storvsc: Make storvsc_dev_add() a static function
Make storvsc_dev_add() a static function. Signed-off-by: K. Y. Srinivasan <kys at microsoft.com> Signed-off-by: Haiyang Zhang <haiyangz at microsoft.com> Signed-off-by: Abhishek Kane <v-abkane at microsoft.com> Signed-off-by: Hank Janssen <hjanssen at microsoft.com> --- drivers/staging/hv/storvsc_drv.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/drivers/staging/hv/storvsc_drv.c b/drivers/staging/hv/storvsc_drv.c index f54f013..4a3e4b3 100644 --- a/drivers/staging/hv/storvsc_drv.c +++ b/drivers/staging/hv/storvsc_drv.c @@ -667,7 +667,7 @@ static int storvsc_connect_to_vsp(struct hv_device *device, u32 ring_size) return ret; } -int storvsc_dev_add(struct hv_device *device, +static int storvsc_dev_add(struct hv_device *device, void *additional_info) { struct storvsc_device *stor_device; -- 1.7.4.1
K. Y. Srinivasan
2011-Jun-29 14:39 UTC
[PATCH 36/40] Staging: hv: storvsc: Make storvsc_dev_remove() a static function
Make storvsc_dev_remove() a static function. Signed-off-by: K. Y. Srinivasan <kys at microsoft.com> Signed-off-by: Haiyang Zhang <haiyangz at microsoft.com> Signed-off-by: Abhishek Kane <v-abkane at microsoft.com> Signed-off-by: Hank Janssen <hjanssen at microsoft.com> --- drivers/staging/hv/storvsc_drv.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/drivers/staging/hv/storvsc_drv.c b/drivers/staging/hv/storvsc_drv.c index 4a3e4b3..f513503 100644 --- a/drivers/staging/hv/storvsc_drv.c +++ b/drivers/staging/hv/storvsc_drv.c @@ -702,7 +702,7 @@ static int storvsc_dev_add(struct hv_device *device, return ret; } -int storvsc_dev_remove(struct hv_device *device) +static int storvsc_dev_remove(struct hv_device *device) { struct storvsc_device *stor_device; unsigned long flags; -- 1.7.4.1
K. Y. Srinivasan
2011-Jun-29 14:39 UTC
[PATCH 37/40] Staging: hv: storvsc: Make storvsc_do_io() a static function
Make storvsc_do_io() a static function. Signed-off-by: K. Y. Srinivasan <kys at microsoft.com> Signed-off-by: Haiyang Zhang <haiyangz at microsoft.com> Signed-off-by: Abhishek Kane <v-abkane at microsoft.com> Signed-off-by: Hank Janssen <hjanssen at microsoft.com> --- drivers/staging/hv/storvsc_drv.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/drivers/staging/hv/storvsc_drv.c b/drivers/staging/hv/storvsc_drv.c index f513503..42689d3 100644 --- a/drivers/staging/hv/storvsc_drv.c +++ b/drivers/staging/hv/storvsc_drv.c @@ -736,7 +736,7 @@ static int storvsc_dev_remove(struct hv_device *device) return 0; } -int storvsc_do_io(struct hv_device *device, +static int storvsc_do_io(struct hv_device *device, struct hv_storvsc_request *request) { struct storvsc_device *stor_device; -- 1.7.4.1
K. Y. Srinivasan
2011-Jun-29 14:39 UTC
[PATCH 39/40] Staging: hv: storvsc: In case of scsi errors offline the device
When we do get fatal errors from the host, offline the device since the host has already tried all possible recovery actions. Signed-off-by: K. Y. Srinivasan <kys at microsoft.com> Signed-off-by: Haiyang Zhang <haiyangz at microsoft.com> Signed-off-by: Abhishek Kane <v-abkane at microsoft.com> Signed-off-by: Hank Janssen <hjanssen at microsoft.com> --- drivers/staging/hv/storvsc_drv.c | 10 +++++++++- 1 files changed, 9 insertions(+), 1 deletions(-) diff --git a/drivers/staging/hv/storvsc_drv.c b/drivers/staging/hv/storvsc_drv.c index 0785947..cc4b128 100644 --- a/drivers/staging/hv/storvsc_drv.c +++ b/drivers/staging/hv/storvsc_drv.c @@ -1251,7 +1251,15 @@ static void storvsc_commmand_completion(struct hv_storvsc_request *request) } } - scmnd->result = vm_srb->scsi_status; + /* + * If there is an error; offline the device since all + * error recovery strategies would have already been + * deployed on the host side. + */ + if (vm_srb->srb_status == 0x4) + scmnd->result = DID_TARGET_FAILURE << 16; + else + scmnd->result = vm_srb->scsi_status; if (scmnd->result) { if (scsi_normalize_sense(scmnd->sense_buffer, -- 1.7.4.1
K. Y. Srinivasan
2011-Jun-29 14:39 UTC
[PATCH 40/40] Staging: hv: storvsc: No need to copy from bounce buffer in case of failure
In case we are not able to submit an I/O, there is no need to copy from the bounce buffers; get of the code to copy. Signed-off-by: K. Y. Srinivasan <kys at microsoft.com> Signed-off-by: Haiyang Zhang <haiyangz at microsoft.com> Signed-off-by: Abhishek Kane <v-abkane at microsoft.com> Signed-off-by: Hank Janssen <hjanssen at microsoft.com> --- drivers/staging/hv/storvsc_drv.c | 10 +--------- 1 files changed, 1 insertions(+), 9 deletions(-) diff --git a/drivers/staging/hv/storvsc_drv.c b/drivers/staging/hv/storvsc_drv.c index cc4b128..e2cdd7b 100644 --- a/drivers/staging/hv/storvsc_drv.c +++ b/drivers/staging/hv/storvsc_drv.c @@ -1421,17 +1421,9 @@ retry_request: if (ret == -EAGAIN) { /* no more space */ - if (cmd_request->bounce_sgl_count) { - /* - * FIXME: We can optimize on writes by just skipping - * this - */ - copy_from_bounce_buffer(scsi_sglist(scmnd), - cmd_request->bounce_sgl, - scsi_sg_count(scmnd)); + if (cmd_request->bounce_sgl_count) destroy_bounce_buffer(cmd_request->bounce_sgl, cmd_request->bounce_sgl_count); - } kmem_cache_free(host_dev->request_pool, cmd_request); -- 1.7.4.1
On Wed, Jun 29, 2011 at 07:38:21AM -0700, K. Y. Srinivasan wrote:> Further cleanup of the hv drivers: > > 1) Cleanup the reference counting mess for both stor and net devices.I really don't understand the need for reference counting on the storage side, especially now that you only have a SCSI driver. The SCSI midlayer does proper counting on it's objects (Scsi_Host, scsi_device, scsi_cmnd), so you'll get that for free given that SCSI drivers just piggyback on the midlayer lifetime rules. For now your patches should probably go in as-is, but mid-term you should be able to completely remove that code on the storage side.
Christoph Hellwig
2011-Jun-30 19:45 UTC
[PATCH 35/40] Staging: hv: storvsc: Make storvsc_dev_add() a static function
On Wed, Jun 29, 2011 at 07:39:32AM -0700, K. Y. Srinivasan wrote:> Make storvsc_dev_add() a static function.Making all pending functions static should be fine in a single patch.
KY Srinivasan
2011-Jul-01 13:01 UTC
[PATCH 24/40] Staging: hv: storvsc: On I/O get the correct IDE device
> -----Original Message----- > From: Christoph Hellwig [mailto:hch at infradead.org] > Sent: Friday, July 01, 2011 4:17 AM > To: KY Srinivasan > Cc: Christoph Hellwig; gregkh at suse.de; linux-kernel at vger.kernel.org; > devel at linuxdriverproject.org; virtualization at lists.osdl.org; Haiyang Zhang; > Abhishek Kane (Mindtree Consulting PVT LTD); Hank Janssen > Subject: Re: [PATCH 24/40] Staging: hv: storvsc: On I/O get the correct IDE device > > On Thu, Jun 30, 2011 at 09:15:54PM +0000, KY Srinivasan wrote: > > That is what I did initially. Then looking at the way we were handling scsi > devices > > where each scsi controller configured for the guest results in an emulated HBA > > (scsi host) in the guest and all the block devices under a given controller are > > handled through this one host, I decided to mimic a similar structure - one > > scsi host for all the block devices configured as an IDE device. > > > > I can go back to my earlier implementation with one host per disk. > > Was there any downside you noticed from using one host per emulated > disk?Nothing I could see from whatever testing I did. However, the guest will have Scsi hosts that the user did not configure. This is certainly the case in both approaches - just the numbers are different! K. Y