public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* "Oops" in 2.6.9 SCSI w/ usb-storage & multi-LUN
@ 2004-12-21  1:23 Stephen Warren
  2004-12-21 17:04 ` Greg KH
  0 siblings, 1 reply; 2+ messages in thread
From: Stephen Warren @ 2004-12-21  1:23 UTC (permalink / raw)
  To: linux-kernel

[-- Attachment #1: Type: text/plain, Size: 5065 bytes --]

We have found a reproducable kernel problem (looks like an oops, but
isn't...) in 2.6.9 somewhere related to the usb-storage driver, the SCSI
stack, and in particular multi-LUN devices. We have the SCSI multi-LUN
kernel configuration option enabled.

The basic scenario is that our application monitors for USB hotplug
notifications, and whenever a device is plugged, it will open that
device, and whenever the device is unplugged, it will close the
previously opened file descriptor. The monitoring is done by listening
to hotplug events on a netlink socket. We've back-ported the netlink
hotplug code from 2.6.10-rc3 to our 2.6.9 kernel.

The problem is, that when we unplug a device and go on to close the
filehandle, we get a kernel oops-like message. Our test  application
then hangs and can't be killed. This seems to be guaranteed to happen
with multi-LUN devices (e.g. multi-format memory "stick" readers) - in
this case, we open e.g. 4 devices on plug, and close those 4 devices on
unplug. The second close call hangs our application, and the oops-like
message is generated a few seconds after.

This seems to be somewhat timing sensitive. We have a test app written
in C++ that does exhibit the problem very reliably on two machines. The
simplified non-C++ version doesn't show the problem!

Both of these test applications are attached. Also attached is a patch
against 2.6.9 that adds netlink support to it, so you can try these test
apps under 2.6.9.

I've tried both test application under 2.6.10-rc3, and everything seems
to work fine on that kernel. I see there were a lot of USB changes in
2.6.10.

Does anyone know what change from 2.6.10 fixed this specific issue. Is
it something that's easy to isolate and back-port to 2.6.9?

Here are relevant portions from /var/log/messages

Dec 20 11:50:56 localhost kernel: usb 1-1: new high speed USB device
using address 84
Dec 20 11:50:56 localhost kernel: scsi80 : SCSI emulation for USB Mass
Storage devices
Dec 20 11:50:56 localhost kernel:   Vendor: Generic   Model: STORAGE
DEVICE    Rev: 9139
Dec 20 11:50:56 localhost kernel:   Type:   Direct-Access
ANSI SCSI revision: 02
Dec 20 11:50:56 localhost kernel: Attached scsi removable disk sda at
scsi80, channel 0, id 0, lun 0
Dec 20 11:50:56 localhost kernel:   Vendor: Generic   Model: STORAGE
DEVICE    Rev: 9139
Dec 20 11:50:56 localhost kernel:   Type:   Direct-Access
ANSI SCSI revision: 02
Dec 20 11:50:56 localhost kernel: Attached scsi removable disk sdb at
scsi80, channel 0, id 0, lun 1
Dec 20 11:50:56 localhost kernel: Device not ready.  Make sure there is
a disc in the drive.
Dec 20 11:50:56 localhost kernel:   Vendor: Generic   Model: STORAGE
DEVICE    Rev: 9139
Dec 20 11:50:56 localhost kernel:   Type:   Direct-Access
ANSI SCSI revision: 02
Dec 20 11:50:56 localhost kernel: Attached scsi removable disk sdc at
scsi80, channel 0, id 0, lun 2
Dec 20 11:50:56 localhost kernel:   Vendor: Generic   Model: STORAGE
DEVICE    Rev: 9139
Dec 20 11:50:56 localhost kernel:   Type:   Direct-Access
ANSI SCSI revision: 02
Dec 20 11:50:56 localhost kernel: Attached scsi removable disk sdd at
scsi80, channel 0, id 0, lun 3
Dec 20 11:50:56 localhost kernel: Device not ready.  Make sure there is
a disc in the drive.
Dec 20 11:50:56 localhost last message repeated 2 times
Dec 20 11:50:57 localhost scsi.agent[30093]: disk at
/devices/pci0000:00/0000:00:02.2/usb1/1-1/1-1:1.0/host80/80:0:0:0
Dec 20 11:50:57 localhost scsi.agent[30111]: disk at
/devices/pci0000:00/0000:00:02.2/usb1/1-1/1-1:1.0/host80/80:0:0:1
Dec 20 11:50:57 localhost scsi.agent[30129]: disk at
/devices/pci0000:00/0000:00:02.2/usb1/1-1/1-1:1.0/host80/80:0:0:2
Dec 20 11:50:57 localhost scsi.agent[30147]: disk at
/devices/pci0000:00/0000:00:02.2/usb1/1-1/1-1:1.0/host80/80:0:0:3
Dec 20 11:51:08 localhost kernel: usb 1-1: USB disconnect, address 84
Dec 20 11:51:18 localhost kernel: usb-storage: Error in bus_reset:
invalid state 1028932437
Dec 20 11:51:18 localhost kernel: scsi: Device offlined - not ready
after error recovery: host 80 channel 0 id 0 lun 0
Dec 20 11:51:18 localhost kernel:  80:0:0:0: Illegal state transition
deleted->offline
Dec 20 11:51:18 localhost kernel: Badness in scsi_device_set_state at
/home/swarren/p4_wa/swarren-linux-alt/embedded/dvd/new_kernel-2.6/linux-
nvidia/drivers/scsi/scsi_lib.c:1688
Dec 20 11:51:18 localhost kernel:  [<c03c6cf0>]
scsi_device_set_state+0xc4/0x112
Dec 20 11:51:18 localhost kernel:  [<c03c4b8b>]
scsi_eh_offline_sdevs+0x64/0x80
Dec 20 11:51:18 localhost kernel:  [<c03c4ffa>]
scsi_unjam_host+0xc5/0xce
Dec 20 11:51:18 localhost kernel:  [<c03c50b5>]
scsi_error_handler+0xb2/0xda
Dec 20 11:51:18 localhost kernel:  [<c03c5003>]
scsi_error_handler+0x0/0xda
Dec 20 11:51:18 localhost kernel:  [<c0103d39>]
kernel_thread_helper+0x5/0xb

-- 
Stephen Warren, Software Engineer, NVIDIA, Fort Collins, CO
swarren@nvidia.com        http://www.nvidia.com/
swarren@wwwdotorg.org     http://www.wwwdotorg.org/pgp.html

[-- Attachment #2: netlink.c --]
[-- Type: application/octet-stream, Size: 2110 bytes --]

#include <stdio.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <asm/types.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>

struct rtnl_handle
{
        int fd;
        struct sockaddr_nl  local;
        struct sockaddr_nl  peer;
         __u32 seq;
        __u32  dump;
};

#define ADD_STR "add@/block/sd"
#define REMOVE_STR "remove@/block/sd"

int main(int argc, char** argv)
{
    int addr_len;

    int fd = socket(AF_NETLINK, SOCK_DGRAM, 15);
    if (fd < 0) {
        perror("cannot open netlink socket");
        return -1;
    }

    struct sockaddr_nl local;
    memset(&local, 0, sizeof(local));
    local.nl_family = AF_NETLINK;
    local.nl_pid = getpid();
    local.nl_groups = 1;

    if (bind(fd, (struct sockaddr*)&local, sizeof(local)) < 0)
    {
        perror("cannot bind netlink socket");
        return -1;
    }

    fd_set fds;
    struct timeval tv;
    char buffer[1024];
    int fd_sd[26];

    for (;;) {
        FD_ZERO (&fds);
        FD_SET (fd, &fds);
        tv.tv_sec = 0;
        tv.tv_usec = 100000;
        while ((select(fd + 1, &fds, NULL, NULL, &tv)) > 0) {

        int i = read(fd, buffer, sizeof(buffer));
        printf("READ: %d bytes\n", i);
        buffer[i] = 0;
        printf("%s\n", buffer);
        if ((strlen(buffer) == strlen(ADD_STR) + 1) && (!strncmp(buffer, ADD_STR, strlen(ADD_STR)))) {
            char dev = buffer[strlen(ADD_STR)];
            int dev_idx = dev - 'a';
            char dev_path[64];
            sprintf(dev_path, "/dev/sd%c", dev);
            printf("***** OPEN %s\n", dev_path);
            fd_sd[dev_idx] = open(dev_path, O_RDONLY, O_NONBLOCK);
        }
        if ((strlen(buffer) == strlen(REMOVE_STR) + 1) && (!strncmp(buffer, REMOVE_STR, strlen(REMOVE_STR)))) {
            char dev = buffer[strlen(REMOVE_STR)];
            int dev_idx = dev - 'a';
            printf("***** CLOSE /dev/sd%c\n", dev);
            close(fd_sd[dev_idx]);
        }

        }
    }

    return 0;
}

[-- Attachment #3: netlink-2.cpp --]
[-- Type: application/octet-stream, Size: 2802 bytes --]

// Netlink.cpp

#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <string>
#include <vector>
#include <asm/types.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/mount.h>
#include <sys/time.h>
#include <scsi/scsi_ioctl.h>
#include <scsi/scsi.h>
#include <scsi/sg.h>

#define NETLINK_KOBJECT_UEVENT	15	/* Kernel messages to userspace */

struct sDevice
{
  std::string strName;
  bool bActive;
  bool bMedia;
  int fd;
};

std::vector<struct sDevice> vDevices;

int main(int argc, char** argv)
{
    char buffer[1024];
    struct sDevice sDev;
    std::vector<struct sDevice>::iterator it;

    for (char c = 'a'; c <= 'h'; ++c) {
        sDev.strName = "/dev/sd";
	sDev.strName += c;
        sDev.bActive = false;
	sDev.bMedia  = false;
        sDev.fd = -1;
        vDevices.push_back(sDev);
    }

    // Open netlink socket
    int fd;
    fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
    if (fd < 0) {
        printf ("Cannot create netlink socket\n");
        return -1;
    }

    // Bind netlink socket
    struct sockaddr_nl addr;
    memset(&addr, 0, sizeof(addr));
    addr.nl_family = AF_NETLINK;
    addr.nl_pid = getpid();
    addr.nl_groups = 1;
    if (bind(fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
        perror("Cannot bind netlink socket\n");
        return -1;
    }
  
    // Listen netlink socket
    fd_set fds;
    struct timeval tv;
    for (;;) {
        FD_ZERO (&fds);
        FD_SET (fd, &fds);
	tv.tv_sec = 0;
	tv.tv_usec = 100000;
        while ((select(FD_SETSIZE, &fds, NULL, NULL, &tv)) > 0) {
            int status = read(fd, buffer, sizeof(buffer)-1);
            if (status > 0) {
	        buffer[status] = 0; // null terminate

		std::string sMessage;
		sMessage = buffer;

		if ((sMessage.substr(0, 4) == "add@") && (sMessage.size() == 14)) {
		  for (it = vDevices.begin(); it != vDevices.end(); ++it) {
		      // Add device
		      if (sMessage.substr(11, 3) == (*it).strName.substr(5, 3)) { 
			  printf ("%s added\n", (*it).strName.c_str());
			  (*it).bActive = true;
			  (*it).bMedia = false;
			  printf ("opening\n");;
			  (*it).fd = open((*it).strName.c_str(), O_RDONLY | O_NONBLOCK);
			  printf ("opened %d\n", (*it).fd);
			}
		    }
		}
		else if ((sMessage.substr(0, 7) == "remove@") && (sMessage.size() == 17)) {
		  for (it = vDevices.begin(); it != vDevices.end(); ++it) {
		      // Remove device
		      if (sMessage.substr(14, 3) == (*it).strName.substr(5, 3)) { 
			  printf ("%s removed\n", (*it).strName.c_str());
			  (*it).bActive = false;
			  (*it).bMedia = false;
			  printf ("closing %d\n", (*it).fd);
			  close ((*it).fd);
			  printf ("closed\n");
			}
		    }
	        }
	    }
	}
    }
}


[-- Attachment #4: nvidia-2.6.9-kobject-uevents.diff --]
[-- Type: application/octet-stream, Size: 21241 bytes --]

diff -urN linux-kernel.org/drivers/base/firmware_class.c linux-kernel.org-2/drivers/base/firmware_class.c
--- linux-kernel.org/drivers/base/firmware_class.c	2004-10-18 15:53:06.000000000 -0600
+++ linux-kernel.org-2/drivers/base/firmware_class.c	2004-12-13 09:26:57.000000000 -0700
@@ -422,7 +422,7 @@
 		add_timer(&fw_priv->timeout);
 	}
 
-	kobject_hotplug("add", &class_dev->kobj);
+	kobject_hotplug(&class_dev->kobj, KOBJ_ADD);
 	wait_for_completion(&fw_priv->completion);
 	set_bit(FW_STATUS_DONE, &fw_priv->status);
 
diff -urN linux-kernel.org/include/linux/kobject.h linux-kernel.org-2/include/linux/kobject.h
--- linux-kernel.org/include/linux/kobject.h	2004-10-18 15:53:07.000000000 -0600
+++ linux-kernel.org-2/include/linux/kobject.h	2004-12-13 09:38:39.000000000 -0700
@@ -22,6 +22,7 @@
 #include <linux/sysfs.h>
 #include <linux/rwsem.h>
 #include <linux/kref.h>
+#include <linux/kobject_uevent.h>
 #include <asm/atomic.h>
 
 #define KOBJ_NAME_LEN	20
@@ -59,9 +60,9 @@
 extern struct kobject * kobject_get(struct kobject *);
 extern void kobject_put(struct kobject *);
 
-extern void kobject_hotplug(const char *action, struct kobject *);
+extern void kobject_hotplug(struct kobject *, enum kobject_action action);
 
-extern char * kobject_get_path(struct kset *, struct kobject *, int);
+extern char * kobject_get_path(struct kobject *, int);
 
 struct kobj_type {
 	void (*release)(struct kobject *);
diff -urN linux-kernel.org/include/linux/kobject_uevent.h linux-kernel.org-2/include/linux/kobject_uevent.h
--- linux-kernel.org/include/linux/kobject_uevent.h	1969-12-31 17:00:00.000000000 -0700
+++ linux-kernel.org-2/include/linux/kobject_uevent.h	2004-12-13 09:07:18.000000000 -0700
@@ -0,0 +1,57 @@
+/*
+ * kobject_uevent.h - list of kobject user events that can be generated
+ *
+ * Copyright (C) 2004 IBM Corp.
+ * Copyright (C) 2004 Greg Kroah-Hartman <greg@kroah.com>
+ *
+ * This file is released under the GPLv2.
+ *
+ */
+
+#ifndef _KOBJECT_EVENT_H_
+#define _KOBJECT_EVENT_H_
+
+#define HOTPLUG_PATH_LEN	256
+
+/* path to the hotplug userspace helper executed on an event */
+extern char hotplug_path[];
+
+/*
+ * If you add an action here, you must also add the proper string to the
+ * lib/kobject_uevent.c file.
+ */
+typedef int __bitwise kobject_action_t;
+enum kobject_action {
+	KOBJ_ADD	= (__force kobject_action_t) 0x01,	/* add event, for hotplug */
+	KOBJ_REMOVE	= (__force kobject_action_t) 0x02,	/* remove event, for hotplug */
+	KOBJ_CHANGE	= (__force kobject_action_t) 0x03,	/* a sysfs attribute file has changed */
+	KOBJ_MOUNT	= (__force kobject_action_t) 0x04,	/* mount event for block devices */
+	KOBJ_UMOUNT	= (__force kobject_action_t) 0x05,	/* umount event for block devices */
+	KOBJ_OFFLINE	= (__force kobject_action_t) 0x06,	/* offline event for hotplug devices */
+	KOBJ_ONLINE	= (__force kobject_action_t) 0x07,	/* online event for hotplug devices */
+};
+
+
+#ifdef CONFIG_KOBJECT_UEVENT
+int kobject_uevent(struct kobject *kobj,
+		   enum kobject_action action,
+		   struct attribute *attr);
+int kobject_uevent_atomic(struct kobject *kobj,
+			  enum kobject_action action,
+			  struct attribute *attr);
+#else
+static inline int kobject_uevent(struct kobject *kobj,
+				 enum kobject_action action,
+				 struct attribute *attr)
+{
+	return 0;
+}
+static inline int kobject_uevent_atomic(struct kobject *kobj,
+				        enum kobject_action action,
+					struct attribute *attr)
+{
+	return 0;
+}
+#endif
+
+#endif
diff -urN linux-kernel.org/include/linux/netlink.h linux-kernel.org-2/include/linux/netlink.h
--- linux-kernel.org/include/linux/netlink.h	2004-10-18 15:55:06.000000000 -0600
+++ linux-kernel.org-2/include/linux/netlink.h	2004-12-13 09:09:24.000000000 -0700
@@ -17,6 +17,7 @@
 #define NETLINK_ROUTE6		11	/* af_inet6 route comm channel */
 #define NETLINK_IP6_FW		13
 #define NETLINK_DNRTMSG		14	/* DECnet routing messages */
+#define NETLINK_KOBJECT_UEVENT	15	/* Kernel messages to userspace */
 #define NETLINK_TAPBASE		16	/* 16 to 31 are ethertap */
 
 #define MAX_LINKS 32		
diff -urN linux-kernel.org/init/Kconfig linux-kernel.org-2/init/Kconfig
--- linux-kernel.org/init/Kconfig	2004-10-18 15:54:55.000000000 -0600
+++ linux-kernel.org-2/init/Kconfig	2004-12-13 10:20:06.634696344 -0700
@@ -205,6 +205,26 @@
 	  agent" (/sbin/hotplug) to load modules and set up software needed
 	  to use devices as you hotplug them.
 
+config KOBJECT_UEVENT
+	bool "Kernel Userspace Events"
+	depends on NET
+	default y
+	help
+	  This option enables the kernel userspace event layer, which is a
+	  simple mechanism for kernel-to-user communication over a netlink
+	  socket.
+
+	  The goal of the kernel userspace events layer is to provide a simple
+	  and efficient events system, that notifies userspace about kobject
+	  state changes. This will enable applications to just listen for
+	  events instead of polling system devices and files.
+	  Hotplug events (kobject addition and removal) are also available on
+	  the netlink socket in addition to the execution of /sbin/hotplug if
+	  CONFIG_HOTPLUG is enabled.
+
+	  Say Y, unless you are building a system requiring minimal memory
+	  consumption.
+
 config IKCONFIG
 	bool "Kernel .config support"
 	---help---
diff -urN linux-kernel.org/kernel/kmod.c linux-kernel.org-2/kernel/kmod.c
--- linux-kernel.org/kernel/kmod.c	2004-10-18 15:53:45.000000000 -0600
+++ linux-kernel.org-2/kernel/kmod.c	2004-12-13 10:06:38.000000000 -0700
@@ -115,29 +115,6 @@
 EXPORT_SYMBOL(request_module);
 #endif /* CONFIG_KMOD */
 
-#ifdef CONFIG_HOTPLUG
-/*
-	hotplug path is set via /proc/sys
-	invoked by hotplug-aware bus drivers,
-	with call_usermodehelper
-
-	argv [0] = hotplug_path;
-	argv [1] = "usb", "scsi", "pci", "network", etc;
-	... plus optional type-specific parameters
-	argv [n] = 0;
-
-	envp [*] = HOME, PATH; optional type-specific parameters
-
-	a hotplug bus should invoke this for device add/remove
-	events.  the command is expected to load drivers when
-	necessary, and may perform additional system setup.
-*/
-char hotplug_path[KMOD_PATH_LEN] = "/sbin/hotplug";
-
-EXPORT_SYMBOL(hotplug_path);
-
-#endif /* CONFIG_HOTPLUG */
-
 struct subprocess_info {
 	struct completion *complete;
 	char *path;
diff -urN linux-kernel.org/lib/kobject.c linux-kernel.org-2/lib/kobject.c
--- linux-kernel.org/lib/kobject.c	2004-10-18 15:55:07.000000000 -0600
+++ linux-kernel.org-2/lib/kobject.c	2004-12-13 10:03:24.000000000 -0700
@@ -63,7 +63,7 @@
 	return container_of(entry,struct kobject,entry);
 }
 
-static int get_kobj_path_length(struct kset *kset, struct kobject *kobj)
+static int get_kobj_path_length(struct kobject *kobj)
 {
 	int length = 1;
 	struct kobject * parent = kobj;
@@ -79,7 +79,7 @@
 	return length;
 }
 
-static void fill_kobj_path(struct kset *kset, struct kobject *kobj, char *path, int length)
+static void fill_kobj_path(struct kobject *kobj, char *path, int length)
 {
 	struct kobject * parent;
 
@@ -103,142 +103,21 @@
  * @kobj:	kobject in question, with which to build the path
  * @gfp_mask:	the allocation type used to allocate the path
  */
-char * kobject_get_path(struct kset *kset, struct kobject *kobj, int gfp_mask)
+char * kobject_get_path(struct kobject *kobj, int gfp_mask)
 {
 	char *path;
 	int len;
 
-	len = get_kobj_path_length(kset, kobj);
+	len = get_kobj_path_length(kobj);
 	path = kmalloc(len, gfp_mask);
 	if (!path)
 		return NULL;
 	memset(path, 0x00, len);
-	fill_kobj_path(kset, kobj, path, len);
+	fill_kobj_path(kobj, path, len);
 
 	return path;
 }
 
-#ifdef CONFIG_HOTPLUG
-
-#define BUFFER_SIZE	1024	/* should be enough memory for the env */
-#define NUM_ENVP	32	/* number of env pointers */
-static unsigned long sequence_num;
-static spinlock_t sequence_lock = SPIN_LOCK_UNLOCKED;
-
-static void kset_hotplug(const char *action, struct kset *kset,
-			 struct kobject *kobj)
-{
-	char *argv [3];
-	char **envp = NULL;
-	char *buffer = NULL;
-	char *scratch;
-	int i = 0;
-	int retval;
-	char *kobj_path = NULL;
-	char *name = NULL;
-	unsigned long seq;
-
-	/* If the kset has a filter operation, call it. If it returns
-	   failure, no hotplug event is required. */
-	if (kset->hotplug_ops->filter) {
-		if (!kset->hotplug_ops->filter(kset, kobj))
-			return;
-	}
-
-	pr_debug ("%s\n", __FUNCTION__);
-
-	if (!hotplug_path[0])
-		return;
-
-	envp = kmalloc(NUM_ENVP * sizeof (char *), GFP_KERNEL);
-	if (!envp)
-		return;
-	memset (envp, 0x00, NUM_ENVP * sizeof (char *));
-
-	buffer = kmalloc(BUFFER_SIZE, GFP_KERNEL);
-	if (!buffer)
-		goto exit;
-
-	if (kset->hotplug_ops->name)
-		name = kset->hotplug_ops->name(kset, kobj);
-	if (name == NULL)
-		name = kset->kobj.name;
-
-	argv [0] = hotplug_path;
-	argv [1] = name;
-	argv [2] = NULL;
-
-	/* minimal command environment */
-	envp [i++] = "HOME=/";
-	envp [i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
-
-	scratch = buffer;
-
-	envp [i++] = scratch;
-	scratch += sprintf(scratch, "ACTION=%s", action) + 1;
-
-	spin_lock(&sequence_lock);
-	seq = sequence_num++;
-	spin_unlock(&sequence_lock);
-
-	envp [i++] = scratch;
-	scratch += sprintf(scratch, "SEQNUM=%ld", seq) + 1;
-
-	kobj_path = kobject_get_path(kset, kobj, GFP_KERNEL);
-	if (!kobj_path)
-		goto exit;
-
-	envp [i++] = scratch;
-	scratch += sprintf (scratch, "DEVPATH=%s", kobj_path) + 1;
-
-	if (kset->hotplug_ops->hotplug) {
-		/* have the kset specific function add its stuff */
-		retval = kset->hotplug_ops->hotplug (kset, kobj,
-				  &envp[i], NUM_ENVP - i, scratch,
-				  BUFFER_SIZE - (scratch - buffer));
-		if (retval) {
-			pr_debug ("%s - hotplug() returned %d\n",
-				  __FUNCTION__, retval);
-			goto exit;
-		}
-	}
-
-	pr_debug ("%s: %s %s %s %s %s %s %s\n", __FUNCTION__, argv[0], argv[1],
-		  envp[0], envp[1], envp[2], envp[3], envp[4]);
-	retval = call_usermodehelper (argv[0], argv, envp, 0);
-	if (retval)
-		pr_debug ("%s - call_usermodehelper returned %d\n",
-			  __FUNCTION__, retval);
-
-exit:
-	kfree(kobj_path);
-	kfree(buffer);
-	kfree(envp);
-	return;
-}
-
-void kobject_hotplug(const char *action, struct kobject *kobj)
-{
-	struct kobject * top_kobj = kobj;
-
-	/* If this kobj does not belong to a kset,
-	   try to find a parent that does. */
-	if (!top_kobj->kset && top_kobj->parent) {
-		do {
-			top_kobj = top_kobj->parent;
-		} while (!top_kobj->kset && top_kobj->parent);
-	}
-
-	if (top_kobj->kset && top_kobj->kset->hotplug_ops)
-		kset_hotplug(action, top_kobj->kset, kobj);
-}
-#else
-void kobject_hotplug(const char *action, struct kobject *kobj)
-{
-	return;
-}
-#endif	/* CONFIG_HOTPLUG */
-
 /**
  *	kobject_init - initialize object.
  *	@kobj:	object in question.
@@ -308,7 +187,7 @@
 		if (parent)
 			kobject_put(parent);
 	} else {
-		kobject_hotplug("add", kobj);
+		kobject_hotplug(kobj, KOBJ_ADD);
 	}
 
 	return error;
@@ -422,7 +301,7 @@
 
 void kobject_del(struct kobject * kobj)
 {
-	kobject_hotplug("remove", kobj);
+	kobject_hotplug(kobj, KOBJ_REMOVE);
 	sysfs_remove_dir(kobj);
 	unlink(kobj);
 }
diff -urN linux-kernel.org/lib/kobject_uevent.c linux-kernel.org-2/lib/kobject_uevent.c
--- linux-kernel.org/lib/kobject_uevent.c	1969-12-31 17:00:00.000000000 -0700
+++ linux-kernel.org-2/lib/kobject_uevent.c	2004-12-13 09:13:29.000000000 -0700
@@ -0,0 +1,369 @@
+/*
+ * kernel userspace event delivery
+ *
+ * Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+ * Copyright (C) 2004 Novell, Inc.  All rights reserved.
+ * Copyright (C) 2004 IBM, Inc. All rights reserved.
+ *
+ * Licensed under the GNU GPL v2.
+ *
+ * Authors:
+ *	Robert Love		<rml@novell.com>
+ *	Kay Sievers		<kay.sievers@vrfy.org>
+ *	Arjan van de Ven	<arjanv@redhat.com>
+ *	Greg Kroah-Hartman	<greg@kroah.com>
+ */
+
+#include <linux/spinlock.h>
+#include <linux/socket.h>
+#include <linux/skbuff.h>
+#include <linux/netlink.h>
+#include <linux/string.h>
+#include <linux/kobject_uevent.h>
+#include <linux/kobject.h>
+#include <net/sock.h>
+
+#define BUFFER_SIZE	1024	/* buffer for the hotplug env */
+#define NUM_ENVP	32	/* number of env pointers */
+
+#if defined(CONFIG_KOBJECT_UEVENT) || defined(CONFIG_HOTPLUG)
+static char *action_to_string(enum kobject_action action)
+{
+	switch (action) {
+	case KOBJ_ADD:
+		return "add";
+	case KOBJ_REMOVE:
+		return "remove";
+	case KOBJ_CHANGE:
+		return "change";
+	case KOBJ_MOUNT:
+		return "mount";
+	case KOBJ_UMOUNT:
+		return "umount";
+	case KOBJ_OFFLINE:
+		return "offline";
+	case KOBJ_ONLINE:
+		return "online";
+	default:
+		return NULL;
+	}
+}
+#endif
+
+#ifdef CONFIG_KOBJECT_UEVENT
+static struct sock *uevent_sock;
+
+/**
+ * send_uevent - notify userspace by sending event trough netlink socket
+ *
+ * @signal: signal name
+ * @obj: object path (kobject)
+ * @envp: possible hotplug environment to pass with the message
+ * @gfp_mask:
+ */
+static int send_uevent(const char *signal, const char *obj,
+		       char **envp, int gfp_mask)
+{
+	struct sk_buff *skb;
+	char *pos;
+	int len;
+
+	if (!uevent_sock)
+		return -EIO;
+
+	len = strlen(signal) + 1;
+	len += strlen(obj) + 1;
+
+	/* allocate buffer with the maximum possible message size */
+	skb = alloc_skb(len + BUFFER_SIZE, gfp_mask);
+	if (!skb)
+		return -ENOMEM;
+
+	pos = skb_put(skb, len);
+	sprintf(pos, "%s@%s", signal, obj);
+
+	/* copy the environment key by key to our continuous buffer */
+	if (envp) {
+		int i;
+
+		for (i = 2; envp[i]; i++) {
+			len = strlen(envp[i]) + 1;
+			pos = skb_put(skb, len);
+			strcpy(pos, envp[i]);
+		}
+	}
+
+	return netlink_broadcast(uevent_sock, skb, 0, 1, gfp_mask);
+}
+
+static int do_kobject_uevent(struct kobject *kobj, enum kobject_action action, 
+			     struct attribute *attr, int gfp_mask)
+{
+	char *path;
+	char *attrpath;
+	char *signal;
+	int len;
+	int rc = -ENOMEM;
+
+	path = kobject_get_path(kobj, gfp_mask);
+	if (!path)
+		return -ENOMEM;
+
+	signal = action_to_string(action);
+	if (!signal)
+		return -EINVAL;
+
+	if (attr) {
+		len = strlen(path);
+		len += strlen(attr->name) + 2;
+		attrpath = kmalloc(len, gfp_mask);
+		if (!attrpath)
+			goto exit;
+		sprintf(attrpath, "%s/%s", path, attr->name);
+		rc = send_uevent(signal, attrpath, NULL, gfp_mask);
+		kfree(attrpath);
+	} else
+		rc = send_uevent(signal, path, NULL, gfp_mask);
+
+exit:
+	kfree(path);
+	return rc;
+}
+
+/**
+ * kobject_uevent - notify userspace by sending event through netlink socket
+ * 
+ * @signal: signal name
+ * @kobj: struct kobject that the event is happening to
+ * @attr: optional struct attribute the event belongs to
+ */
+int kobject_uevent(struct kobject *kobj, enum kobject_action action,
+		   struct attribute *attr)
+{
+	return do_kobject_uevent(kobj, action, attr, GFP_KERNEL);
+}
+EXPORT_SYMBOL_GPL(kobject_uevent);
+
+int kobject_uevent_atomic(struct kobject *kobj, enum kobject_action action,
+			  struct attribute *attr)
+{
+	return do_kobject_uevent(kobj, action, attr, GFP_ATOMIC);
+}
+EXPORT_SYMBOL_GPL(kobject_uevent_atomic);
+
+static int __init kobject_uevent_init(void)
+{
+	uevent_sock = netlink_kernel_create(NETLINK_KOBJECT_UEVENT, NULL);
+
+	if (!uevent_sock) {
+		printk(KERN_ERR
+		       "kobject_uevent: unable to create netlink socket!\n");
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+postcore_initcall(kobject_uevent_init);
+
+#else
+static inline int send_uevent(const char *signal, const char *obj,
+			      char **envp, int gfp_mask)
+{
+	return 0;
+}
+
+#endif /* CONFIG_KOBJECT_UEVENT */
+
+
+#ifdef CONFIG_HOTPLUG
+char hotplug_path[HOTPLUG_PATH_LEN] = "/sbin/hotplug";
+u64 hotplug_seqnum;
+static spinlock_t sequence_lock = SPIN_LOCK_UNLOCKED;
+
+/**
+ * kobject_hotplug - notify userspace by executing /sbin/hotplug
+ *
+ * @action: action that is happening (usually "ADD" or "REMOVE")
+ * @kobj: struct kobject that the action is happening to
+ */
+void kobject_hotplug(struct kobject *kobj, enum kobject_action action)
+{
+	char *argv [3];
+	char **envp = NULL;
+	char *buffer = NULL;
+	char *seq_buff;
+	char *scratch;
+	int i = 0;
+	int retval;
+	char *kobj_path = NULL;
+	char *name = NULL;
+	char *action_string;
+	u64 seq;
+	struct kobject *top_kobj = kobj;
+	struct kset *kset;
+	static struct kset_hotplug_ops null_hotplug_ops;
+	struct kset_hotplug_ops *hotplug_ops = &null_hotplug_ops;
+
+	/* If this kobj does not belong to a kset,
+	   try to find a parent that does. */
+	if (!top_kobj->kset && top_kobj->parent) {
+		do {
+			top_kobj = top_kobj->parent;
+		} while (!top_kobj->kset && top_kobj->parent);
+	}
+
+	if (top_kobj->kset)
+		kset = top_kobj->kset;
+	else
+		return;
+
+	if (kset->hotplug_ops)
+		hotplug_ops = kset->hotplug_ops;
+
+	/* If the kset has a filter operation, call it.
+	   Skip the event, if the filter returns zero. */
+	if (hotplug_ops->filter) {
+		if (!hotplug_ops->filter(kset, kobj))
+			return;
+	}
+
+	pr_debug ("%s\n", __FUNCTION__);
+
+	action_string = action_to_string(action);
+	if (!action_string)
+		return;
+
+	envp = kmalloc(NUM_ENVP * sizeof (char *), GFP_KERNEL);
+	if (!envp)
+		return;
+	memset (envp, 0x00, NUM_ENVP * sizeof (char *));
+
+	buffer = kmalloc(BUFFER_SIZE, GFP_KERNEL);
+	if (!buffer)
+		goto exit;
+
+	if (hotplug_ops->name)
+		name = hotplug_ops->name(kset, kobj);
+	if (name == NULL)
+		name = kset->kobj.name;
+
+	argv [0] = hotplug_path;
+	argv [1] = name;
+	argv [2] = NULL;
+
+	/* minimal command environment */
+	envp [i++] = "HOME=/";
+	envp [i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
+
+	scratch = buffer;
+
+	envp [i++] = scratch;
+	scratch += sprintf(scratch, "ACTION=%s", action_string) + 1;
+
+	kobj_path = kobject_get_path(kobj, GFP_KERNEL);
+	if (!kobj_path)
+		goto exit;
+
+	envp [i++] = scratch;
+	scratch += sprintf (scratch, "DEVPATH=%s", kobj_path) + 1;
+
+	envp [i++] = scratch;
+	scratch += sprintf(scratch, "SUBSYSTEM=%s", name) + 1;
+
+	/* reserve space for the sequence,
+	 * put the real one in after the hotplug call */
+	envp[i++] = seq_buff = scratch;
+	scratch += strlen("SEQNUM=18446744073709551616") + 1;
+
+	if (hotplug_ops->hotplug) {
+		/* have the kset specific function add its stuff */
+		retval = hotplug_ops->hotplug (kset, kobj,
+				  &envp[i], NUM_ENVP - i, scratch,
+				  BUFFER_SIZE - (scratch - buffer));
+		if (retval) {
+			pr_debug ("%s - hotplug() returned %d\n",
+				  __FUNCTION__, retval);
+			goto exit;
+		}
+	}
+
+	spin_lock(&sequence_lock);
+	seq = ++hotplug_seqnum;
+	spin_unlock(&sequence_lock);
+	sprintf(seq_buff, "SEQNUM=%llu", (unsigned long long)seq);
+
+	pr_debug ("%s: %s %s seq=%llu %s %s %s %s %s\n",
+		  __FUNCTION__, argv[0], argv[1], (unsigned long long)seq,
+		  envp[0], envp[1], envp[2], envp[3], envp[4]);
+
+	send_uevent(action_string, kobj_path, envp, GFP_KERNEL);
+
+	if (!hotplug_path[0])
+		goto exit;
+
+	retval = call_usermodehelper (argv[0], argv, envp, 0);
+	if (retval)
+		pr_debug ("%s - call_usermodehelper returned %d\n",
+			  __FUNCTION__, retval);
+
+exit:
+	kfree(kobj_path);
+	kfree(buffer);
+	kfree(envp);
+	return;
+}
+EXPORT_SYMBOL(kobject_hotplug);
+
+/**
+ * add_hotplug_env_var - helper for creating hotplug environment variables
+ * @envp: Pointer to table of environment variables, as passed into
+ * hotplug() method.
+ * @num_envp: Number of environment variable slots available, as
+ * passed into hotplug() method.
+ * @cur_index: Pointer to current index into @envp.  It should be
+ * initialized to 0 before the first call to add_hotplug_env_var(),
+ * and will be incremented on success.
+ * @buffer: Pointer to buffer for environment variables, as passed
+ * into hotplug() method.
+ * @buffer_size: Length of @buffer, as passed into hotplug() method.
+ * @cur_len: Pointer to current length of space used in @buffer.
+ * Should be initialized to 0 before the first call to
+ * add_hotplug_env_var(), and will be incremented on success.
+ * @format: Format for creating environment variable (of the form
+ * "XXX=%x") for snprintf().
+ *
+ * Returns 0 if environment variable was added successfully or -ENOMEM
+ * if no space was available.
+ */
+int add_hotplug_env_var(char **envp, int num_envp, int *cur_index,
+			char *buffer, int buffer_size, int *cur_len,
+			const char *format, ...)
+{
+	va_list args;
+
+	/*
+	 * We check against num_envp - 1 to make sure there is at
+	 * least one slot left after we return, since the hotplug
+	 * method needs to set the last slot to NULL.
+	 */
+	if (*cur_index >= num_envp - 1)
+		return -ENOMEM;
+
+	envp[*cur_index] = buffer + *cur_len;
+
+	va_start(args, format);
+	*cur_len += vsnprintf(envp[*cur_index],
+			      max(buffer_size - *cur_len, 0),
+			      format, args) + 1;
+	va_end(args);
+
+	if (*cur_len > buffer_size)
+		return -ENOMEM;
+
+	(*cur_index)++;
+	return 0;
+}
+EXPORT_SYMBOL(add_hotplug_env_var);
+
+#endif /* CONFIG_HOTPLUG */
diff -urN linux-kernel.org/lib/Makefile linux-kernel.org-2/lib/Makefile
--- linux-kernel.org/lib/Makefile	2004-10-18 15:53:08.000000000 -0600
+++ linux-kernel.org-2/lib/Makefile	2004-12-13 09:36:23.000000000 -0700
@@ -6,7 +6,7 @@
 lib-y := errno.o ctype.o string.o vsprintf.o cmdline.o \
 	 bust_spinlocks.o rbtree.o radix-tree.o dump_stack.o \
 	 kobject.o kref.o idr.o div64.o parser.o int_sqrt.o \
-	 bitmap.o extable.o
+	 bitmap.o extable.o kobject_uevent.o
 
 lib-$(CONFIG_RWSEM_GENERIC_SPINLOCK) += rwsem-spinlock.o
 lib-$(CONFIG_RWSEM_XCHGADD_ALGORITHM) += rwsem.o

^ permalink raw reply	[flat|nested] 2+ messages in thread

* Re: "Oops" in 2.6.9 SCSI w/ usb-storage & multi-LUN
  2004-12-21  1:23 "Oops" in 2.6.9 SCSI w/ usb-storage & multi-LUN Stephen Warren
@ 2004-12-21 17:04 ` Greg KH
  0 siblings, 0 replies; 2+ messages in thread
From: Greg KH @ 2004-12-21 17:04 UTC (permalink / raw)
  To: Stephen Warren; +Cc: linux-kernel

On Mon, Dec 20, 2004 at 05:23:44PM -0800, Stephen Warren wrote:
> I've tried both test application under 2.6.10-rc3, and everything seems
> to work fine on that kernel. I see there were a lot of USB changes in
> 2.6.10.

2.6.10 isn't released yet :)

> Does anyone know what change from 2.6.10 fixed this specific issue. Is
> it something that's easy to isolate and back-port to 2.6.9?

Lots of different scsi and usb changes probably helped fix this.  I
suggest just going through the different patches and trying to narrow it
down if you really need to backport this.

Good luck,

greg k-h

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2004-12-21 17:04 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2004-12-21  1:23 "Oops" in 2.6.9 SCSI w/ usb-storage & multi-LUN Stephen Warren
2004-12-21 17:04 ` Greg KH

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox