If a scsi_device supports async notification for media change, then
let user space know this capability exists by creating a new sysfs
entry "media_change_notify", which will be 1 if it is supported, and
0 if not supported.  Create a routine which allows scsi devices to
send a uevent when media change events occur.

Signed-off-by: Kristen Carlson Accardi <[EMAIL PROTECTED]>

Index: 2.6-git/include/scsi/scsi_device.h
===================================================================
--- 2.6-git.orig/include/scsi/scsi_device.h
+++ 2.6-git/include/scsi/scsi_device.h
@@ -46,6 +46,16 @@ enum scsi_device_state {
                                 * to the scsi lld. */
 };
 
+/* must match scsi_device_event_strings in scsi_lib.c */
+enum scsi_device_event {
+       SDEV_MEDIA_CHANGE = 1,  /* media has changed */
+};
+
+struct scsi_device_event_info {
+       enum scsi_device_event event;
+       struct list_head link;
+};
+
 struct scsi_device {
        struct Scsi_Host *host;
        struct request_queue *request_queue;
@@ -126,7 +136,7 @@ struct scsi_device {
        unsigned fix_capacity:1;        /* READ_CAPACITY is too high by 1 */
        unsigned guess_capacity:1;      /* READ_CAPACITY might be too high by 1 
*/
        unsigned retry_hwerror:1;       /* Retry HARDWARE_ERROR */
-
+       unsigned media_change_notify:1; /* dev supports async media notify */
        unsigned int device_blocked;    /* Device returned QUEUE_FULL. */
 
        unsigned int max_device_blocked; /* what device_blocked counts down 
from  */
@@ -144,6 +154,7 @@ struct scsi_device {
        struct execute_work     ew; /* used to get process context on put */
 
        enum scsi_device_state sdev_state;
+       struct list_head        sdev_event_list;
        unsigned long           sdev_data[0];
 } __attribute__((aligned(sizeof(unsigned long))));
 #define        to_scsi_device(d)       \
@@ -275,6 +286,8 @@ extern int scsi_test_unit_ready(struct s
                                int retries);
 extern int scsi_device_set_state(struct scsi_device *sdev,
                                 enum scsi_device_state state);
+extern int scsi_device_event_notify(struct scsi_device *sdev,
+                               enum scsi_device_event event);
 extern int scsi_device_quiesce(struct scsi_device *sdev);
 extern void scsi_device_resume(struct scsi_device *sdev);
 extern void scsi_target_quiesce(struct scsi_target *);
Index: 2.6-git/drivers/scsi/scsi_lib.c
===================================================================
--- 2.6-git.orig/drivers/scsi/scsi_lib.c
+++ 2.6-git/drivers/scsi/scsi_lib.c
@@ -64,6 +64,11 @@ static struct scsi_host_sg_pool scsi_sg_
 };     
 #undef SP
 
+/* must match scsi_device_event enum in scsi_device.h */
+static char * scsi_device_event_strings[] = {
+       "MEDIA_CHANGE=1",
+};
+
 static void scsi_run_queue(struct request_queue *q);
 
 /*
@@ -2007,6 +2012,84 @@ scsi_device_set_state(struct scsi_device
 EXPORT_SYMBOL(scsi_device_set_state);
 
 /**
+ *     scsi_device_set_event - Add a new Async event to the event list
+ *     @sdev: scsi_device event occurred on
+ *     @event: the scsi device event
+ *
+ *     Add a new scsi_device_event_info struct to the scsi_device_event_list
+ *     queue.  This may be called from interrupt context.
+ *
+ *     Returns 0 if successful, -ENOMEM otherwise.
+ */
+static int
+scsi_device_set_event(struct scsi_device *sdev, enum scsi_device_event event)
+{
+       struct scsi_device_event_info *scsi_event;
+       unsigned long flags;
+
+       scsi_event = kzalloc(sizeof(*scsi_event), GFP_ATOMIC);
+       if (!scsi_event)
+               return -ENOMEM;
+       INIT_LIST_HEAD(&scsi_event->link);
+       scsi_event->event = event;
+       spin_lock_irqsave(&sdev->list_lock, flags);
+       list_add_tail(&scsi_event->link, &sdev->sdev_event_list);
+       spin_unlock_irqrestore(&sdev->list_lock, flags);
+       return 0;
+}
+
+/**
+ *     scsi_device_event_notify_thread - send a uevent for each scsi event
+ *     @work: work struct for scsi_device
+ *
+ *     Traverse the queue of scsi device events, dequeue each event and
+ *     send a uevent.  Frees event.  May not be called from interrupt.
+ */
+static void scsi_event_notify_thread(struct work_struct *work)
+{
+       struct scsi_device *sdev;
+       char *envp[] = { NULL, NULL };
+       struct list_head *tmp;
+       struct list_head *next;
+       struct scsi_device_event_info *sdev_event;
+       unsigned long flags;
+
+       sdev = container_of(work, struct scsi_device, ew.work);
+       list_for_each_safe(tmp, next, &sdev->sdev_event_list) {
+               sdev_event = list_entry(tmp, struct scsi_device_event_info,
+                                       link);
+               spin_lock_irqsave(&sdev->list_lock, flags);
+               list_del(&sdev_event->link);
+               spin_unlock_irqrestore(&sdev->list_lock, flags);
+               envp[0] = scsi_device_event_strings[sdev_event->event-1];
+               kobject_uevent_env(&sdev->sdev_gendev.kobj, KOBJ_CHANGE, envp);
+               kfree(sdev_event);
+       }
+}
+
+/**
+ *     scsi_device_event_notify - store event info and send an event
+ *     @sdev: scsi_device event occurred on
+ *     @event: the scsi device event
+ *
+ *     Store the event information and then switch process context
+ *     so that the event may be sent to user space.
+ *     This may be called from interrupt context.
+ *
+ *     Returns 0 if successful, -ENOMEM otherwise.
+ */
+int scsi_device_event_notify(struct scsi_device *sdev, enum scsi_device_event 
event)
+{
+       int rc;
+
+       rc = scsi_device_set_event(sdev, event);
+       if (!rc)
+               execute_in_process_context(scsi_event_notify_thread, &sdev->ew);
+       return rc;
+}
+EXPORT_SYMBOL_GPL(scsi_device_event_notify);
+
+/**
  *     scsi_device_quiesce - Block user issued commands.
  *     @sdev:  scsi device to quiesce.
  *
Index: 2.6-git/drivers/scsi/scsi_scan.c
===================================================================
--- 2.6-git.orig/drivers/scsi/scsi_scan.c
+++ 2.6-git/drivers/scsi/scsi_scan.c
@@ -253,6 +253,7 @@ static struct scsi_device *scsi_alloc_sd
        INIT_LIST_HEAD(&sdev->same_target_siblings);
        INIT_LIST_HEAD(&sdev->cmd_list);
        INIT_LIST_HEAD(&sdev->starved_entry);
+       INIT_LIST_HEAD(&sdev->sdev_event_list);
        spin_lock_init(&sdev->list_lock);
 
        sdev->sdev_gendev.parent = get_device(&starget->dev);
Index: 2.6-git/drivers/scsi/scsi_sysfs.c
===================================================================
--- 2.6-git.orig/drivers/scsi/scsi_sysfs.c
+++ 2.6-git/drivers/scsi/scsi_sysfs.c
@@ -575,6 +575,18 @@ sdev_show_modalias(struct device *dev, s
 }
 static DEVICE_ATTR(modalias, S_IRUGO, sdev_show_modalias, NULL);
 
+static ssize_t
+sdev_show_media_change_notify(struct device *dev, struct device_attribute 
*attr,
+                               char *buf)
+{
+       struct scsi_device *sdev = to_scsi_device(dev);
+       if (sdev->media_change_notify)
+               return snprintf(buf, 20, "%d\n", 1);
+       else
+               return snprintf(buf, 20, "%d\n", 0);
+}
+static DEVICE_ATTR(media_change_notify, S_IRUGO, 
sdev_show_media_change_notify, NULL);
+
 /* Default template for device attributes.  May NOT be modified */
 static struct device_attribute *scsi_sysfs_sdev_attrs[] = {
        &dev_attr_device_blocked,
@@ -594,6 +606,7 @@ static struct device_attribute *scsi_sys
        &dev_attr_iodone_cnt,
        &dev_attr_ioerr_cnt,
        &dev_attr_modalias,
+       &dev_attr_media_change_notify,
        NULL
 };
 

-- 
-
To unsubscribe from this list: send the line "unsubscribe linux-ide" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to