Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > by-pkgid > 89877e42827f16fa5f86b1df0c2860b1 > files > 2051

kernel-2.6.18-128.1.10.el5.src.rpm

From: Jeff Garzik <jgarzik@redhat.com>
Date: Wed, 19 Dec 2007 07:23:47 -0500
Subject: [sata] rhel5.2 general kernel prep
Message-id: 20071219122347.GA25477@devserv.devel.redhat.com
O-Subject: [RHEL5.2 PATCH 1/2] SATA update - general kernel prep
Bugzilla: 184884 307911

Prep work for next patch, which addresses 184884 and 307911
among others.

This is from upstream, except for minor differences:

* kABI requires a shadow struct when "adding" to struct scsi_device

* minor kobject_uevent_env() tweaks for backporting

 drivers/scsi/scsi_lib.c    |  196 +++++++++++++++++++++++++++++++++++++++++++++
 drivers/scsi/scsi_sysfs.c  |    4
 include/linux/kobject.h    |    2
 include/linux/pci_ids.h    |    3
 include/scsi/scsi_cmnd.h   |    4
 include/scsi/scsi_device.h |   33 +++++++
 lib/kobject_uevent.c       |   35 ++++++--
 7 files changed, 270 insertions(+), 7 deletions(-)

diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
index a869408..9cd2d39 100644
--- a/drivers/scsi/scsi_lib.c
+++ b/drivers/scsi/scsi_lib.c
@@ -1994,6 +1994,202 @@ scsi_device_set_state(struct scsi_device *sdev, enum scsi_device_state state)
 EXPORT_SYMBOL(scsi_device_set_state);
 
 /**
+ * 	sdev_evt_emit - emit a single SCSI device uevent
+ *	@sdev: associated SCSI device
+ *	@evt: event to emit
+ *
+ *	Send a single uevent (scsi_event) to the associated scsi_device.
+ */
+static void scsi_evt_emit(struct scsi_device *sdev, struct scsi_event *evt)
+{
+	int idx = 0;
+	char *envp[3];
+
+	switch (evt->evt_type) {
+	case SDEV_EVT_MEDIA_CHANGE:
+		envp[idx++] = "SDEV_MEDIA_CHANGE=1";
+		break;
+
+	default:
+		/* do nothing */
+		break;
+	}
+
+	envp[idx++] = NULL;
+
+	kobject_uevent_env(&sdev->sdev_gendev.kobj, KOBJ_CHANGE, envp);
+}
+
+/**
+ * 	sdev_evt_thread - send a uevent for each scsi event
+ *	@work: work struct for scsi_device
+ *
+ *	Dispatch queued events to their associated scsi_device kobjects
+ *	as uevents.
+ */
+void scsi_evt_thread(void *opaque)
+{
+	struct scsi_device *sdev = opaque;
+	struct scsi_device_shadow *shdev = sdev_shadow(sdev);
+	LIST_HEAD(event_list);
+
+	if (!shdev)
+		return;
+
+	while (1) {
+		struct scsi_event *evt;
+		struct list_head *this, *tmp;
+		unsigned long flags;
+
+		spin_lock_irqsave(&sdev->list_lock, flags);
+		list_splice_init(&shdev->event_list, &event_list);
+		spin_unlock_irqrestore(&sdev->list_lock, flags);
+
+		if (list_empty(&event_list))
+			break;
+
+		list_for_each_safe(this, tmp, &event_list) {
+			evt = list_entry(this, struct scsi_event, node);
+			list_del(&evt->node);
+			scsi_evt_emit(sdev, evt);
+			kfree(evt);
+		}
+	}
+}
+
+/**
+ * 	sdev_evt_send - send asserted event to uevent thread
+ *	@sdev: scsi_device event occurred on
+ *	@evt: event to send
+ *
+ *	Assert scsi device event asynchronously.
+ */
+void sdev_evt_send(struct scsi_device *sdev, struct scsi_event *evt)
+{
+	struct scsi_device_shadow *shdev = sdev_shadow(sdev);
+	unsigned long flags;
+
+	if (!shdev)
+		return;
+
+	if (!test_bit(evt->evt_type, shdev->supported_events)) {
+		kfree(evt);
+		return;
+	}
+
+	spin_lock_irqsave(&sdev->list_lock, flags);
+	list_add_tail(&evt->node, &shdev->event_list);
+	schedule_work(&shdev->event_work);
+	spin_unlock_irqrestore(&sdev->list_lock, flags);
+}
+EXPORT_SYMBOL_GPL(sdev_evt_send);
+
+/**
+ * 	sdev_evt_alloc - allocate a new scsi event
+ *	@evt_type: type of event to allocate
+ *	@gfpflags: GFP flags for allocation
+ *
+ *	Allocates and returns a new scsi_event.
+ */
+struct scsi_event *sdev_evt_alloc(enum scsi_device_event evt_type,
+				  gfp_t gfpflags)
+{
+	struct scsi_event *evt = kzalloc(sizeof(struct scsi_event), gfpflags);
+	if (!evt)
+		return NULL;
+
+	evt->evt_type = evt_type;
+	INIT_LIST_HEAD(&evt->node);
+
+	/* evt_type-specific initialization, if any */
+	switch (evt_type) {
+	case SDEV_EVT_MEDIA_CHANGE:
+	default:
+		/* do nothing */
+		break;
+	}
+
+	return evt;
+}
+EXPORT_SYMBOL_GPL(sdev_evt_alloc);
+
+/**
+ * 	sdev_evt_send_simple - send asserted event to uevent thread
+ *	@sdev: scsi_device event occurred on
+ *	@evt_type: type of event to send
+ *	@gfpflags: GFP flags for allocation
+ *
+ *	Assert scsi device event asynchronously, given an event type.
+ */
+void sdev_evt_send_simple(struct scsi_device *sdev,
+			  enum scsi_device_event evt_type, gfp_t gfpflags)
+{
+	struct scsi_event *evt = sdev_evt_alloc(evt_type, gfpflags);
+	if (!evt) {
+		sdev_printk(KERN_ERR, sdev, "event %d eaten due to OOM\n",
+			    evt_type);
+		return;
+	}
+
+	sdev_evt_send(sdev, evt);
+}
+EXPORT_SYMBOL_GPL(sdev_evt_send_simple);
+
+static LIST_HEAD(sdev_shadow_list);
+
+struct scsi_device_shadow *sdev_shadow(struct scsi_device *sdev)
+{
+	struct scsi_device_shadow *shdev;
+
+	list_for_each_entry(shdev, &sdev_shadow_list, shadow_node) {
+		if (shdev->sdev == sdev)
+			return shdev;
+	}
+
+	shdev = kzalloc(sizeof(*shdev), GFP_ATOMIC);
+	if (shdev) {
+		shdev->sdev = sdev;
+		INIT_LIST_HEAD(&shdev->event_list);
+		INIT_WORK(&shdev->event_work, scsi_evt_thread, sdev);
+		INIT_LIST_HEAD(&shdev->shadow_node);
+
+		list_add(&shdev->shadow_node, &sdev_shadow_list);
+	}
+
+	return shdev;
+}
+
+EXPORT_SYMBOL(sdev_shadow);
+
+void sdev_shadow_release(struct scsi_device *sdev)
+{
+	struct scsi_device_shadow *shdev = NULL, *shtmp;
+	struct list_head *this, *tmp;
+
+	list_for_each_entry(shtmp, &sdev_shadow_list, shadow_node) {
+		if (shtmp->sdev == sdev) {
+			shdev = shtmp;
+			break;
+		}
+	}
+	if (!shdev)
+		return;
+
+	flush_scheduled_work();
+
+	list_for_each_safe(this, tmp, &shdev->event_list) {
+		struct scsi_event *evt;
+
+		evt = list_entry(this, struct scsi_event, node);
+		list_del(&evt->node);
+		kfree(evt);
+	}
+
+	list_del(&shdev->shadow_node);
+	kfree(shdev);
+}
+
+/**
  *	scsi_device_quiesce - Block user issued commands.
  *	@sdev:	scsi device to quiesce.
  *
diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c
index e7fe565..48676b7 100644
--- a/drivers/scsi/scsi_sysfs.c
+++ b/drivers/scsi/scsi_sysfs.c
@@ -216,6 +216,8 @@ static void scsi_device_cls_release(struct class_device *class_dev)
 	put_device(&sdev->sdev_gendev);
 }
 
+extern void sdev_shadow_release(struct scsi_device *sdev);
+
 static void scsi_device_dev_release_usercontext(void *data)
 {
 	struct device *dev = data;
@@ -235,6 +237,8 @@ static void scsi_device_dev_release_usercontext(void *data)
 	list_del(&sdev->starved_entry);
 	spin_unlock_irqrestore(sdev->host->host_lock, flags);
 
+	sdev_shadow_release(sdev);
+
 	if (sdev->request_queue) {
 		sdev->request_queue->queuedata = NULL;
 		/* user context needed to free queue */
diff --git a/include/linux/kobject.h b/include/linux/kobject.h
index 2d22932..0b69c70 100644
--- a/include/linux/kobject.h
+++ b/include/linux/kobject.h
@@ -262,6 +262,8 @@ extern int subsys_create_file(struct subsystem * , struct subsys_attribute *);
 
 #if defined(CONFIG_HOTPLUG)
 void kobject_uevent(struct kobject *kobj, enum kobject_action action);
+int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
+			char *envp[]);
 
 int add_uevent_var(char **envp, int num_envp, int *cur_index,
 			char *buffer, int buffer_size, int *cur_len,
diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h
index 4a41a2c..1edb2a8 100644
--- a/include/linux/pci_ids.h
+++ b/include/linux/pci_ids.h
@@ -1226,6 +1226,8 @@
 #define PCI_DEVICE_ID_NVIDIA_NVENET_26              0x054E
 #define PCI_DEVICE_ID_NVIDIA_NVENET_27              0x054F
 #define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP67_IDE       0x0560
+#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP73_IDE       0x056C
+#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP77_IDE       0x0759
 
 #define PCI_VENDOR_ID_IMS		0x10e0
 #define PCI_DEVICE_ID_IMS_TT128		0x9128
@@ -1336,6 +1338,7 @@
 #define PCI_DEVICE_ID_VIA_8231_4	0x8235
 #define PCI_DEVICE_ID_VIA_8365_1	0x8305
 #define PCI_DEVICE_ID_VIA_CX700		0x8324
+#define PCI_DEVICE_ID_VIA_VX800		0x8353
 #define PCI_DEVICE_ID_VIA_8371_1	0x8391
 #define PCI_DEVICE_ID_VIA_82C598_1	0x8598
 #define PCI_DEVICE_ID_VIA_838X_1	0xB188
diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h
index fd75b43..af224bb 100644
--- a/include/scsi/scsi_device.h
+++ b/include/scsi/scsi_device.h
@@ -45,6 +45,22 @@ enum scsi_device_state {
 				 * to the scsi lld. */
 };
 
+enum scsi_device_event {
+	SDEV_EVT_MEDIA_CHANGE	= 1,	/* media has changed */
+
+	SDEV_EVT_LAST		= SDEV_EVT_MEDIA_CHANGE,
+	SDEV_EVT_MAXBITS	= SDEV_EVT_LAST + 1
+};
+
+struct scsi_event {
+	enum scsi_device_event	evt_type;
+	struct list_head	node;
+
+	/* put union of data structures, for non-simple event types,
+	 * here
+	 */
+};
+
 struct scsi_device {
 	struct Scsi_Host *host;
 	struct request_queue *request_queue;
@@ -161,6 +177,17 @@ struct scsi_device {
 #define scmd_printk(prefix, scmd, fmt, a...)	\
 	dev_printk(prefix, &(scmd)->device->sdev_gendev, fmt, ##a)
 
+struct scsi_device_shadow {
+#ifndef __GENKSYMS__
+	struct scsi_device	*sdev;
+	DECLARE_BITMAP(supported_events, SDEV_EVT_MAXBITS); /* supported events */
+	struct list_head	event_list;	/* asserted events */
+	struct work_struct	event_work;
+
+	struct list_head	shadow_node;
+#endif
+};
+
 enum scsi_target_state {
 	STARGET_RUNNING = 1,
 	STARGET_DEL,
@@ -203,6 +230,7 @@ static inline struct scsi_target *scsi_target(struct scsi_device *sdev)
 #define starget_printk(prefix, starget, fmt, a...)	\
 	dev_printk(prefix, &(starget)->dev, fmt, ##a)
 
+extern struct scsi_device_shadow *sdev_shadow(struct scsi_device *);
 extern struct scsi_device *__scsi_add_device(struct Scsi_Host *,
 		uint, uint, uint, void *hostdata);
 extern int scsi_add_device(struct Scsi_Host *host, uint channel,
@@ -275,6 +303,11 @@ extern int scsi_test_unit_ready(struct scsi_device *sdev, int timeout,
 				int retries);
 extern int scsi_device_set_state(struct scsi_device *sdev,
 				 enum scsi_device_state state);
+extern struct scsi_event *sdev_evt_alloc(enum scsi_device_event evt_type,
+					  gfp_t gfpflags);
+extern void sdev_evt_send(struct scsi_device *sdev, struct scsi_event *evt);
+extern void sdev_evt_send_simple(struct scsi_device *sdev,
+			  enum scsi_device_event evt_type, gfp_t gfpflags);
 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 *);
diff --git a/lib/kobject_uevent.c b/lib/kobject_uevent.c
index 7f20e7b..b8db06b 100644
--- a/lib/kobject_uevent.c
+++ b/lib/kobject_uevent.c
@@ -61,7 +61,8 @@ static char *action_to_string(enum kobject_action action)
  * @action: action that is happening (usually KOBJ_ADD and KOBJ_REMOVE)
  * @kobj: struct kobject that the action is happening to
  */
-void kobject_uevent(struct kobject *kobj, enum kobject_action action)
+int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
+		       char *envp_ext[])
 {
 	char **envp;
 	char *buffer;
@@ -74,14 +75,14 @@ void kobject_uevent(struct kobject *kobj, enum kobject_action action)
 	struct kset_uevent_ops *uevent_ops;
 	u64 seq;
 	char *seq_buff;
-	int i = 0;
+	int i = 0, j;
 	int retval;
 
 	pr_debug("%s\n", __FUNCTION__);
 
 	action_string = action_to_string(action);
 	if (!action_string)
-		return;
+		return 0;
 
 	/* search the kset we belong to */
 	top_kobj = kobj;
@@ -91,7 +92,7 @@ void kobject_uevent(struct kobject *kobj, enum kobject_action action)
 		} while (!top_kobj->kset && top_kobj->parent);
 	}
 	if (!top_kobj->kset)
-		return;
+		return 0;
 
 	kset = top_kobj->kset;
 	uevent_ops = kset->uevent_ops;
@@ -99,12 +100,12 @@ void kobject_uevent(struct kobject *kobj, enum kobject_action action)
 	/*  skip the event, if the filter returns zero. */
 	if (uevent_ops && uevent_ops->filter)
 		if (!uevent_ops->filter(kset, kobj))
-			return;
+			return 0;
 
 	/* environment index */
 	envp = kzalloc(NUM_ENVP * sizeof (char *), GFP_KERNEL);
 	if (!envp)
-		return;
+		return -ENOMEM;
 
 	/* environment values */
 	buffer = kmalloc(BUFFER_SIZE, GFP_KERNEL);
@@ -139,6 +140,11 @@ void kobject_uevent(struct kobject *kobj, enum kobject_action action)
 	envp[i++] = seq_buff = scratch;
 	scratch += strlen("SEQNUM=18446744073709551616") + 1;
 
+	/* keys passed in from the caller */
+	if (envp_ext)
+		for (j = 0; envp_ext[j]; j++)
+			envp[i++] = envp_ext[j];
+
 	/* let the kset specific function add its stuff */
 	if (uevent_ops && uevent_ops->uevent) {
 		retval = uevent_ops->uevent(kset, kobj,
@@ -198,7 +204,22 @@ exit:
 	kfree(devpath);
 	kfree(buffer);
 	kfree(envp);
-	return;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(kobject_uevent_env);
+
+/**
+ * kobject_uevent - notify userspace by ending an uevent
+ *
+ * @action: action that is happening
+ * @kobj: struct kobject that the action is happening to
+ *
+ * Returns 0 if kobject_uevent() is completed with success or the
+ * corresponding error when it fails.
+ */
+void kobject_uevent(struct kobject *kobj, enum kobject_action action)
+{
+	kobject_uevent_env(kobj, action, NULL);
 }
 EXPORT_SYMBOL_GPL(kobject_uevent);