[PATCH] scsi: use notifier chain for Asynchronous EventNotification

From: Kristen Carlson Accardi
Date: Wed Sep 12 2007 - 14:07:03 EST


Add a notifier chain for SCSI asynchronous events. Add a
notifier block for events which should be sent to user
space, and add support for the MEDIA_CHANGE event, which
would be used by a driver when new media has been inserted.

Signed-off-by: Kristen Carlson Accardi <kristen.c.accardi@xxxxxxxxx>
---
This patch is against the latest -mm and is on top of what
has already been put in for AN support. It just moves the
event notification thread out of scsi_lib.c and into it's own
file, as well as adding notifier chain support.

Index: 2.6-mm/drivers/scsi/scsi.c
===================================================================
--- 2.6-mm.orig/drivers/scsi/scsi.c
+++ 2.6-mm/drivers/scsi/scsi.c
@@ -1045,6 +1045,8 @@ static int __init init_scsi(void)
if (error)
goto cleanup_sysctl;

+ scsi_aen_init();
+
scsi_netlink_init();

printk(KERN_NOTICE "SCSI subsystem initialized\n");
Index: 2.6-mm/drivers/scsi/scsi_aen.c
===================================================================
--- /dev/null
+++ 2.6-mm/drivers/scsi/scsi_aen.c
@@ -0,0 +1,168 @@
+/*
+ * SCSI Asynchronous Event Notification
+ *
+ * Copyright (c) 2007, Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that 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
+ *
+ * Send Feedback to: Kristen Carlson Accardi <kristen.c.accardi@xxxxxxxxx>
+ */
+#include <linux/notifier.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_dbg.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_driver.h>
+#include <scsi/scsi_eh.h>
+#include <scsi/scsi_host.h>
+
+static int scsi_aen_uevent_notifier(struct notifier_block *nb,
+ unsigned long action, void *sdev);
+
+BLOCKING_NOTIFIER_HEAD(scsi_aen_chain);
+
+static struct notifier_block scsi_aen_nb = {
+ .notifier_call = scsi_aen_uevent_notifier,
+};
+
+/* must match scsi_device_event enum in scsi_device.h */
+static char * scsi_device_event_strings[] = {
+ "MEDIA_CHANGE=1",
+};
+
+/** scsi_aen_notifier_register - register to receive aen events
+ * @nb: callers notifier_block
+ *
+ * Add a callers notifier_block to the AEN notify chain.
+ */
+int scsi_aen_notifier_register(struct notifier_block *nb)
+{
+ return blocking_notifier_chain_register(&scsi_aen_chain, nb);
+}
+EXPORT_SYMBOL_GPL(scsi_aen_notifier_register);
+
+/** scsi_aen_notifier_register - register to receive aen events
+ * @nb: callers notifier_block
+ *
+ * remove a callers notifier_block from the AEN notify chain.
+ */
+int scsi_aen_notifier_unregister(struct notifier_block *nb)
+{
+ return blocking_notifier_chain_unregister(&scsi_aen_chain, nb);
+}
+EXPORT_SYMBOL_GPL(scsi_aen_notifier_unregister);
+
+/**
+ * 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;
+ 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);
+ /* ignore return code for now */
+ blocking_notifier_call_chain(&scsi_aen_chain,
+ sdev_event->event, sdev);
+ kfree(sdev_event);
+ }
+}
+
+static int scsi_aen_uevent_notifier(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct scsi_device *sdev = data;
+ char *envp[] = { NULL, NULL };
+
+ if (action == SDEV_MEDIA_CHANGE) {
+ envp[0] = scsi_device_event_strings[action-1];
+ kobject_uevent_env(&sdev->sdev_gendev.kobj, KOBJ_CHANGE, envp);
+ }
+ return NOTIFY_OK;
+}
+
+/**
+ * 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);
+
+__init int scsi_aen_init(void)
+{
+ return scsi_aen_notifier_register(&scsi_aen_nb);
+}
+
+void scsi_aen_exit(void)
+{
+ scsi_aen_notifier_unregister(&scsi_aen_nb);
+}
+
Index: 2.6-mm/drivers/scsi/scsi_lib.c
===================================================================
--- 2.6-mm.orig/drivers/scsi/scsi_lib.c
+++ 2.6-mm/drivers/scsi/scsi_lib.c
@@ -51,11 +51,6 @@ 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);

/*
@@ -2136,84 +2131,6 @@ 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-mm/drivers/scsi/Makefile
===================================================================
--- 2.6-mm.orig/drivers/scsi/Makefile
+++ 2.6-mm/drivers/scsi/Makefile
@@ -150,7 +150,7 @@ obj-$(CONFIG_SCSI_DEBUG) += scsi_debug.o
obj-$(CONFIG_SCSI_WAIT_SCAN) += scsi_wait_scan.o

scsi_mod-y += scsi.o hosts.o scsi_ioctl.o constants.o \
- scsicam.o scsi_error.o scsi_lib.o
+ scsicam.o scsi_error.o scsi_lib.o scsi_aen.o
scsi_mod-$(CONFIG_SCSI_DMA) += scsi_lib_dma.o
scsi_mod-y += scsi_scan.o scsi_sysfs.o scsi_devinfo.o
scsi_mod-$(CONFIG_SCSI_NETLINK) += scsi_netlink.o
Index: 2.6-mm/drivers/scsi/scsi_priv.h
===================================================================
--- 2.6-mm.orig/drivers/scsi/scsi_priv.h
+++ 2.6-mm/drivers/scsi/scsi_priv.h
@@ -128,6 +128,10 @@ static inline void scsi_netlink_init(voi
static inline void scsi_netlink_exit(void) {}
#endif

+/* scsi_aen.c */
+extern int scsi_aen_init(void);
+extern void scsi_aen_exit(void);
+
/*
* internal scsi timeout functions: for use by mid-layer and transport
* classes.
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/