ChangeSet 1.2181.4.59, 2005/03/24 15:13:02-08:00, [EMAIL PROTECTED]

[PATCH] USB Storage: make usb-storage structures refcounted by SCSI

This patch started life as as474 from Alan Stern.  It's been rediffed
against the tip, tho that is now several days old.

This patch changes the way our private struct us_data is allocated; now it
gets stored at the end of the Scsi_Host rather than separately.  That's
what the hostdata field is intended for, and this is how other low-level
host drivers operate.  In order to convert between us_data and the
corresponding Scsi_Host I added two new inline routines: us_to_host and
host_to_us.  (The conversion actually should be quicker than before by a
microscopic amount, because now it only involves adding an offset whereas
before it involved dereferencing a pointer.)

The main advantage is that the host is refcounted, so now our us_data
automatically is too.  Although that doesn't matter at the moment, it will
matter later on when the control thread may need to outlive the disconnect
callback.


Signed-off-by: Alan Stern <[EMAIL PROTECTED]>
Signed-off-by: Matthew Dharm <[EMAIL PROTECTED]>
Signed-off-by: Greg Kroah-Hartman <[EMAIL PROTECTED]>


 drivers/usb/storage/scsiglue.c  |   51 +++++++++++++++++--------------------
 drivers/usb/storage/transport.c |    4 +-
 drivers/usb/storage/usb.c       |   55 ++++++++++++++++------------------------
 drivers/usb/storage/usb.h       |    9 +++++-
 4 files changed, 57 insertions(+), 62 deletions(-)


diff -Nru a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c
--- a/drivers/usb/storage/scsiglue.c    2005-03-30 13:34:07 -08:00
+++ b/drivers/usb/storage/scsiglue.c    2005-03-30 13:34:07 -08:00
@@ -82,7 +82,7 @@
 
 static int slave_configure(struct scsi_device *sdev)
 {
-       struct us_data *us = (struct us_data *) sdev->host->hostdata[0];
+       struct us_data *us = host_to_us(sdev->host);
 
        /* Scatter-gather buffers (all but the last) must have a length
         * divisible by the bulk maxpacket size.  Otherwise a data packet
@@ -172,14 +172,13 @@
 }
 
 /* queue a command */
-/* This is always called with scsi_lock(srb->host) held */
+/* This is always called with scsi_lock(host) held */
 static int queuecommand(struct scsi_cmnd *srb,
                        void (*done)(struct scsi_cmnd *))
 {
-       struct us_data *us = (struct us_data *)srb->device->host->hostdata[0];
+       struct us_data *us = host_to_us(srb->device->host);
 
        US_DEBUGP("%s called\n", __FUNCTION__);
-       srb->host_scribble = (unsigned char *)us;
 
        /* check for state-transition errors */
        if (us->srb != NULL) {
@@ -209,11 +208,10 @@
  ***********************************************************************/
 
 /* Command timeout and abort */
-/* This is always called with scsi_lock(srb->host) held */
-static int command_abort(struct scsi_cmnd *srb )
+/* This is always called with scsi_lock(host) held */
+static int command_abort(struct scsi_cmnd *srb)
 {
-       struct Scsi_Host *host = srb->device->host;
-       struct us_data *us = (struct us_data *) host->hostdata[0];
+       struct us_data *us = host_to_us(srb->device->host);
 
        US_DEBUGP("%s called\n", __FUNCTION__);
 
@@ -233,13 +231,13 @@
                set_bit(US_FLIDX_ABORTING, &us->flags);
                usb_stor_stop_transport(us);
        }
-       scsi_unlock(host);
+       scsi_unlock(us_to_host(us));
 
        /* Wait for the aborted command to finish */
        wait_for_completion(&us->notify);
 
        /* Reacquire the lock and allow USB transfers to resume */
-       scsi_lock(host);
+       scsi_lock(us_to_host(us));
        clear_bit(US_FLIDX_ABORTING, &us->flags);
        clear_bit(US_FLIDX_TIMED_OUT, &us->flags);
        return SUCCESS;
@@ -247,15 +245,15 @@
 
 /* This invokes the transport reset mechanism to reset the state of the
  * device */
-/* This is always called with scsi_lock(srb->host) held */
+/* This is always called with scsi_lock(host) held */
 static int device_reset(struct scsi_cmnd *srb)
 {
-       struct us_data *us = (struct us_data *)srb->device->host->hostdata[0];
+       struct us_data *us = host_to_us(srb->device->host);
        int result;
 
        US_DEBUGP("%s called\n", __FUNCTION__);
 
-       scsi_unlock(srb->device->host);
+       scsi_unlock(us_to_host(us));
 
        /* lock the device pointers and do the reset */
        down(&(us->dev_semaphore));
@@ -267,22 +265,22 @@
        up(&(us->dev_semaphore));
 
        /* lock the host for the return */
-       scsi_lock(srb->device->host);
+       scsi_lock(us_to_host(us));
        return result;
 }
 
 /* This resets the device's USB port. */
 /* It refuses to work if there's more than one interface in
  * the device, so that other users are not affected. */
-/* This is always called with scsi_lock(srb->host) held */
+/* This is always called with scsi_lock(host) held */
 static int bus_reset(struct scsi_cmnd *srb)
 {
-       struct us_data *us = (struct us_data *)srb->device->host->hostdata[0];
+       struct us_data *us = host_to_us(srb->device->host);
        int result, rc;
 
        US_DEBUGP("%s called\n", __FUNCTION__);
 
-       scsi_unlock(srb->device->host);
+       scsi_unlock(us_to_host(us));
 
        /* The USB subsystem doesn't handle synchronisation between
         * a device's several drivers. Therefore we reset only devices
@@ -310,7 +308,7 @@
        up(&(us->dev_semaphore));
 
        /* lock the host for the return */
-       scsi_lock(srb->device->host);
+       scsi_lock(us_to_host(us));
        return result < 0 ? FAILED : SUCCESS;
 }
 
@@ -320,11 +318,12 @@
 void usb_stor_report_device_reset(struct us_data *us)
 {
        int i;
+       struct Scsi_Host *host = us_to_host(us);
 
-       scsi_report_device_reset(us->host, 0, 0);
+       scsi_report_device_reset(host, 0, 0);
        if (us->flags & US_FL_SCM_MULT_TARG) {
-               for (i = 1; i < us->host->max_id; ++i)
-                       scsi_report_device_reset(us->host, 0, i);
+               for (i = 1; i < host->max_id; ++i)
+                       scsi_report_device_reset(host, 0, i);
        }
 }
 
@@ -337,10 +336,10 @@
 #define SPRINTF(args...) \
        do { if (pos < buffer+length) pos += sprintf(pos, ## args); } while (0)
 
-static int proc_info (struct Scsi_Host *hostptr, char *buffer, char **start, 
off_t offset,
-               int length, int inout)
+static int proc_info (struct Scsi_Host *host, char *buffer,
+               char **start, off_t offset, int length, int inout)
 {
-       struct us_data *us;
+       struct us_data *us = host_to_us(host);
        char *pos = buffer;
        const char *string;
 
@@ -348,10 +347,8 @@
        if (inout)
                return length;
 
-       us = (struct us_data*)hostptr->hostdata[0];
-
        /* print the controller name */
-       SPRINTF("   Host scsi%d: usb-storage\n", hostptr->host_no);
+       SPRINTF("   Host scsi%d: usb-storage\n", host->host_no);
 
        /* print product, vendor, and serial number strings */
        if (us->pusb_dev->manufacturer)
diff -Nru a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c
--- a/drivers/usb/storage/transport.c   2005-03-30 13:34:07 -08:00
+++ b/drivers/usb/storage/transport.c   2005-03-30 13:34:07 -08:00
@@ -1139,11 +1139,11 @@
         * RESETTING bit, and clear the ABORTING bit so that the reset
         * may proceed.
         */
-       scsi_lock(us->host);
+       scsi_lock(us_to_host(us));
        usb_stor_report_device_reset(us);
        set_bit(US_FLIDX_RESETTING, &us->flags);
        clear_bit(US_FLIDX_ABORTING, &us->flags);
-       scsi_unlock(us->host);
+       scsi_unlock(us_to_host(us));
 
        /* A 20-second timeout may seem rather long, but a LaCie
         * StudioDrive USB2 device takes 16+ seconds to get going
diff -Nru a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c
--- a/drivers/usb/storage/usb.c 2005-03-30 13:34:07 -08:00
+++ b/drivers/usb/storage/usb.c 2005-03-30 13:34:07 -08:00
@@ -277,7 +277,7 @@
 static int usb_stor_control_thread(void * __us)
 {
        struct us_data *us = (struct us_data *)__us;
-       struct Scsi_Host *host = us->host;
+       struct Scsi_Host *host = us_to_host(us);
 
        lock_kernel();
 
@@ -756,20 +756,6 @@
 
        up(&us->dev_semaphore);
 
-       /*
-        * Since this is a new device, we need to register a SCSI
-        * host definition with the higher SCSI layers.
-        */
-       us->host = scsi_host_alloc(&usb_stor_host_template, sizeof(us));
-       if (!us->host) {
-               printk(KERN_WARNING USB_STORAGE
-                       "Unable to allocate the scsi host\n");
-               return -EBUSY;
-       }
-
-       /* Set the hostdata to prepare for scanning */
-       us->host->hostdata[0] = (unsigned long) us;
-
        /* Start up our control thread */
        p = kernel_thread(usb_stor_control_thread, us, CLONE_VM);
        if (p < 0) {
@@ -809,9 +795,9 @@
                 * Enqueue the command, wake up the thread, and wait for 
                 * notification that it has exited.
                 */
-               scsi_lock(us->host);
+               scsi_lock(us_to_host(us));
                us->srb = NULL;
-               scsi_unlock(us->host);
+               scsi_unlock(us_to_host(us));
                up(&us->dev_semaphore);
 
                up(&us->sema);
@@ -824,10 +810,6 @@
                us->extra_destructor(us->extra);
        }
 
-       /* Finish the host removal sequence */
-       if (us->host)
-               scsi_host_put(us->host);
-
        /* Free the extra data and the URB */
        kfree(us->extra);
        usb_free_urb(us->current_urb);
@@ -848,9 +830,6 @@
 
        /* Remove our private data from the interface */
        usb_set_intfdata(us->pusb_intf, NULL);
-
-       /* Free the structure itself */
-       kfree(us);
 }
 
 /* Thread to carry out delayed SCSI-device scanning */
@@ -885,7 +864,7 @@
 
        /* If the device is still connected, perform the scanning */
        if (!test_bit(US_FLIDX_DISCONNECTING, &us->flags)) {
-               scsi_scan_host(us->host);
+               scsi_scan_host(us_to_host(us));
                printk(KERN_DEBUG "usb-storage: device scan complete\n");
        }
 
@@ -897,18 +876,25 @@
 static int storage_probe(struct usb_interface *intf,
                         const struct usb_device_id *id)
 {
+       struct Scsi_Host *host;
        struct us_data *us;
        const int id_index = id - storage_usb_ids; 
        int result;
 
        US_DEBUGP("USB Mass Storage device detected\n");
 
-       /* Allocate the us_data structure and initialize the mutexes */
-       us = (struct us_data *) kmalloc(sizeof(*us), GFP_KERNEL);
-       if (!us) {
-               printk(KERN_WARNING USB_STORAGE "Out of memory\n");
+       /*
+        * Ask the SCSI layer to allocate a host structure, with extra
+        * space at the end for our private us_data structure.
+        */
+       host = scsi_host_alloc(&usb_stor_host_template, sizeof(*us));
+       if (!host) {
+               printk(KERN_WARNING USB_STORAGE
+                       "Unable to allocate the scsi host\n");
                return -ENOMEM;
        }
+
+       us = host_to_us(host);
        memset(us, 0, sizeof(struct us_data));
        init_MUTEX(&(us->dev_semaphore));
        init_MUTEX_LOCKED(&(us->sema));
@@ -968,7 +954,7 @@
        result = usb_stor_acquire_resources(us);
        if (result)
                goto BadDevice;
-       result = scsi_add_host(us->host, &intf->dev);
+       result = scsi_add_host(host, &intf->dev);
        if (result) {
                printk(KERN_WARNING USB_STORAGE
                        "Unable to add the scsi host\n");
@@ -980,7 +966,7 @@
        if (result < 0) {
                printk(KERN_WARNING USB_STORAGE 
                       "Unable to start the device-scanning thread\n");
-               scsi_remove_host(us->host);
+               scsi_remove_host(host);
                goto BadDevice;
        }
 
@@ -991,6 +977,7 @@
        US_DEBUGP("storage_probe() failed\n");
        usb_stor_release_resources(us);
        dissociate_dev(us);
+       scsi_host_put(host);
        return result;
 }
 
@@ -1015,11 +1002,15 @@
        /* Wait for the current command to finish, then remove the host */
        down(&us->dev_semaphore);
        up(&us->dev_semaphore);
-       scsi_remove_host(us->host);
+       scsi_remove_host(us_to_host(us));
 
        /* Wait for everything to become idle and release all our resources */
        usb_stor_release_resources(us);
        dissociate_dev(us);
+
+       /* Drop our reference to the host; the SCSI core will free it
+        * (and "us" along with it) when the refcount becomes 0. */
+       scsi_host_put(us_to_host(us));
 }
 
 /***********************************************************************
diff -Nru a/drivers/usb/storage/usb.h b/drivers/usb/storage/usb.h
--- a/drivers/usb/storage/usb.h 2005-03-30 13:34:07 -08:00
+++ b/drivers/usb/storage/usb.h 2005-03-30 13:34:07 -08:00
@@ -155,7 +155,6 @@
        proto_cmnd              proto_handler;   /* protocol handler       */
 
        /* SCSI interfaces */
-       struct Scsi_Host        *host;           /* our dummy host data */
        struct scsi_cmnd        *srb;            /* current srb         */
 
        /* thread information */
@@ -180,6 +179,14 @@
        void                    *extra;          /* Any extra data          */
        extra_data_destructor   extra_destructor;/* extra data destructor   */
 };
+
+/* Convert between us_data and the corresponding Scsi_Host */
+static struct Scsi_Host inline *us_to_host(struct us_data *us) {
+       return container_of((void *) us, struct Scsi_Host, hostdata);
+}
+static struct us_data inline *host_to_us(struct Scsi_Host *host) {
+       return (struct us_data *) host->hostdata;
+}
 
 /* Function to fill an inquiry response. See usb.c for details */
 extern void fill_inquiry_response(struct us_data *us,



-------------------------------------------------------
This SF.net email is sponsored by Demarc:
A global provider of Threat Management Solutions.
Download our HomeAdmin security software for free today!
http://www.demarc.com/info/Sentarus/hamr30
_______________________________________________
linux-usb-devel@lists.sourceforge.net
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel

Reply via email to