Re: cardbus pirq conflict

From: Andrew Morton (andrewm@uow.edu.au)
Date: Fri Dec 08 2000 - 20:59:26 EST


Matthew Galgoci wrote:
>
> Hi Folks,
>
> I am running the 2.4.0test12pre7 kernel on my laptop computer, and
> I'm having some rather interesting problems.
>
> For the longest time, usb never worked on this machine. As of the
> happy patch that enabled bus mastering for usb controllers, it
> magically started working. I am really happy that it does work
> now.
>
> The usb controller and the pcmcia bridge both share the same
> irq, irq 10.
>
> Now, my cardbus cards have stopped working. When I insert a cardbus
> nic, I get the following message: "IRQ routing conflict in pirq
> table! Try 'pci=autoirq'"
>
> The card fails to initialize, and upon issuing the halt command, the
> system generates a kernel Oops. I tend to think that the Oops is a
> symptom of having a half initialized device. If anyone is interested,
> I'll catch the Oops, run it though ksymoops, and send it to them.

test12-pre7 has a number of hotplug problems. I think what you're
seeing is a deadlock where keventd is waiting on itself in
call_usermodehelper() :(

And yes, when you shutdown the system in this state it oopses. I'm
not sure why it was doing that - it shouldn't have. hmmm...

Could you please test this stuff?

--- linux-2.4.0-test12-pre7/include/linux/sched.h Thu Dec 7 22:05:21 2000
+++ linux-akpm/include/linux/sched.h Sat Dec 9 01:36:19 2000
@@ -152,6 +152,7 @@
 extern int schedule_task(struct tq_struct *task);
 extern void run_schedule_tasks(void);
 extern int start_context_thread(void);
+extern int current_is_keventd(void);
 
 /*
  * The default fd array needs to be at least BITS_PER_LONG,
--- linux-2.4.0-test12-pre7/include/linux/kernel.h Thu Dec 7 22:05:21 2000
+++ linux-akpm/include/linux/kernel.h Sat Dec 9 01:22:18 2000
@@ -63,6 +63,8 @@
 extern int get_option(char **str, int *pint);
 extern char *get_options(char *str, int nints, int *ints);
 extern unsigned long memparse(char *ptr, char **retptr);
+extern void dev_probe_lock(void);
+extern void dev_probe_unlock(void);
 
 extern int session_of_pgrp(int pgrp);
 
--- linux-2.4.0-test12-pre7/drivers/pci/pci.c Thu Dec 7 22:05:20 2000
+++ linux-akpm/drivers/pci/pci.c Sat Dec 9 01:24:46 2000
@@ -300,18 +300,25 @@
 pci_announce_device(struct pci_driver *drv, struct pci_dev *dev)
 {
         const struct pci_device_id *id;
+ int ret = 0;
 
         if (drv->id_table) {
                 id = pci_match_device(drv->id_table, dev);
- if (!id)
- return 0;
+ if (!id) {
+ ret = 0;
+ goto out;
+ }
         } else
                 id = NULL;
+
+ dev_probe_lock();
         if (drv->probe(dev, id) >= 0) {
                 dev->driver = drv;
- return 1;
+ ret = 1;
         }
- return 0;
+ dev_probe_unlock();
+out:
+ return ret;
 }
 
 int
@@ -360,9 +367,9 @@
         if (!hotplug_path[0])
                 return;
 
- sprintf(class_id, "PCI_CLASS=%X", pdev->class);
- sprintf(id, "PCI_ID=%X/%X", pdev->vendor, pdev->device);
- sprintf(sub_id, "PCI_SUBSYS_ID=%X/%X", pdev->subsystem_vendor, pdev->subsystem_device);
+ sprintf(class_id, "PCI_CLASS=%04X", pdev->class);
+ sprintf(id, "PCI_ID=%04X:%04X", pdev->vendor, pdev->device);
+ sprintf(sub_id, "PCI_SUBSYS_ID=%04X:%04X", pdev->subsystem_vendor, pdev->subsystem_device);
         sprintf(bus_id, "PCI_SLOT_NAME=%s", pdev->slot_name);
 
         i = 0;
--- linux-2.4.0-test12-pre7/kernel/exit.c Thu Dec 7 22:05:21 2000
+++ linux-akpm/kernel/exit.c Fri Dec 8 22:38:30 2000
@@ -302,9 +302,9 @@
 {
         struct mm_struct * mm = tsk->mm;
 
+ mm_release();
         if (mm) {
                 atomic_inc(&mm->mm_count);
- mm_release();
                 if (mm != tsk->active_mm) BUG();
                 /* more a memory barrier than a real lock */
                 task_lock(tsk);
--- linux-2.4.0-test12-pre7/kernel/kmod.c Thu Dec 7 22:05:21 2000
+++ linux-akpm/kernel/kmod.c Sat Dec 9 11:53:32 2000
@@ -256,21 +256,6 @@
 
 #endif /* CONFIG_HOTPLUG */
 
-
-static int exec_helper (void *arg)
-{
- long ret;
- void **params = (void **) arg;
- char *path = (char *) params [0];
- char **argv = (char **) params [1];
- char **envp = (char **) params [2];
-
- ret = exec_usermodehelper (path, argv, envp);
- if (ret < 0)
- ret = -ret;
- do_exit(ret);
-}
-
 struct subprocess_info {
         struct semaphore *sem;
         char *path;
@@ -279,73 +264,36 @@
         int retval;
 };
 
-/*
- * This is a standalone child of keventd. It forks off another thread which
- * is the desired usermode helper and then waits for the child to exit.
- * We return the usermode process's exit code, or some -ve error code.
- */
 static int ____call_usermodehelper(void *data)
 {
         struct subprocess_info *sub_info = data;
- struct task_struct *curtask = current;
- void *params [3] = { sub_info->path, sub_info->argv, sub_info->envp };
- pid_t pid, pid2;
- mm_segment_t fs;
- int retval = 0;
+ int retval;
 
- if (!curtask->fs->root) {
- printk(KERN_ERR "call_usermodehelper[%s]: no root fs\n", sub_info->path);
- retval = -EPERM;
- goto up_and_out;
- }
- if ((pid = kernel_thread(exec_helper, (void *) params, 0)) < 0) {
- printk(KERN_ERR "failed fork2 %s, errno = %d", sub_info->argv[0], -pid);
- retval = pid;
- goto up_and_out;
- }
+ retval = -EPERM;
+ if (current->fs->root)
+ retval = exec_usermodehelper(sub_info->path, sub_info->argv, sub_info->envp);
 
- if (retval >= 0) {
- /* Block everything but SIGKILL/SIGSTOP */
- spin_lock_irq(&curtask->sigmask_lock);
- siginitsetinv(&curtask->blocked, sigmask(SIGKILL) | sigmask(SIGSTOP));
- recalc_sigpending(curtask);
- spin_unlock_irq(&curtask->sigmask_lock);
-
- /* Allow the system call to access kernel memory */
- fs = get_fs();
- set_fs(KERNEL_DS);
- pid2 = waitpid(pid, &retval, __WCLONE);
- if (pid2 == -1 && errno < 0)
- pid2 = errno;
- set_fs(fs);
-
- if (pid2 != pid) {
- printk(KERN_ERR "waitpid(%d) failed, %d\n", pid, pid2);
- retval = (pid2 < 0) ? pid2 : -1;
- }
- }
-
-up_and_out:
+ /* Exec failed? */
         sub_info->retval = retval;
- curtask->exit_signal = SIGCHLD; /* Wake up parent */
- up_and_exit(sub_info->sem, retval);
+ do_exit(0);
 }
 
 /*
- * This is a schedule_task function, so we must not sleep for very long at all.
- * But the exec'ed process could do anything at all. So we launch another
- * kernel thread.
+ * This is run by keventd.
  */
 static void __call_usermodehelper(void *data)
 {
         struct subprocess_info *sub_info = data;
         pid_t pid;
 
- if ((pid = kernel_thread (____call_usermodehelper, (void *)sub_info, 0)) < 0) {
- printk(KERN_ERR "failed fork1 %s, errno = %d", sub_info->argv[0], -pid);
+ /*
+ * CLONE_VFORK: wait until the usermode helper has execve'd successfully
+ * We need the data structures to stay around until that is done.
+ */
+ pid = kernel_thread (____call_usermodehelper, sub_info, CLONE_VFORK | SIGCHLD);
+ if (pid < 0)
                 sub_info->retval = pid;
- up(sub_info->sem);
- }
+ up(sub_info->sem);
 }
 
 /*
@@ -358,22 +306,50 @@
 {
         DECLARE_MUTEX_LOCKED(sem);
         struct subprocess_info sub_info = {
- sem: &sem,
- path: path,
- argv: argv,
- envp: envp,
- retval: 0,
- };
- struct tq_struct tqs = {
- next: 0,
- sync: 0,
- routine: __call_usermodehelper,
- data: &sub_info,
+ sem: &sem,
+ path: path,
+ argv: argv,
+ envp: envp,
+ retval: 0,
         };
+ int retval = 0;
+
+ if (path[0] == '\0')
+ goto out;
 
- schedule_task(&tqs);
- down(&sem); /* Wait for an error or completion */
- return sub_info.retval;
+ if (current_is_keventd()) {
+ /* We can't wait on keventd! */
+ __call_usermodehelper(&sub_info);
+ } else {
+ struct tq_struct tqs = {
+ next: 0,
+ sync: 0,
+ routine: __call_usermodehelper,
+ data: &sub_info,
+ };
+
+ schedule_task(&tqs);
+ down(&sem); /* Wait for an error or completion */
+ }
+ retval = sub_info.retval;
+out:
+ return retval;
+}
+
+/*
+ * This is for the serialisation of device probe() functions
+ * against device open() functions
+ */
+static DECLARE_MUTEX(dev_probe_sem);
+
+void dev_probe_lock(void)
+{
+ down(&dev_probe_sem);
+}
+
+void dev_probe_unlock(void)
+{
+ up(&dev_probe_sem);
 }
 
 EXPORT_SYMBOL(exec_usermodehelper);
--- linux-2.4.0-test12-pre7/kernel/context.c Thu Dec 7 22:05:21 2000
+++ linux-akpm/kernel/context.c Fri Dec 8 22:38:30 2000
@@ -18,6 +18,17 @@
 static DECLARE_TASK_QUEUE(tq_context);
 static DECLARE_WAIT_QUEUE_HEAD(context_task_wq);
 static int keventd_running;
+static struct task_struct *keventd_task;
+
+int current_is_keventd(void)
+{
+ int ret = 0;
+ if (keventd_running == 0)
+ printk(KERN_ERR "current_is_keventd(): keventd has not started\n");
+ else
+ ret = (current == keventd_task);
+ return ret;
+}
 
 int schedule_task(struct tq_struct *task)
 {
@@ -38,6 +49,7 @@
         daemonize();
         strcpy(curtask->comm, "keventd");
         keventd_running = 1;
+ keventd_task = curtask;
 
         spin_lock_irq(&curtask->sigmask_lock);
         siginitsetinv(&curtask->blocked, sigmask(SIGCHLD));
--- linux-2.4.0-test12-pre7/net/core/dev.c Thu Dec 7 22:05:21 2000
+++ linux-akpm/net/core/dev.c Sat Dec 9 02:11:10 2000
@@ -154,6 +154,12 @@
 static struct timer_list samp_timer = { function: sample_queue };
 #endif
 
+#ifdef CONFIG_HOTPLUG
+static int net_run_sbin_hotplug(struct net_device *dev, char *action);
+#else
+#define net_run_sbin_hotplug(dev, action) ({ 0; })
+#endif
+
 /*
  * Our notifier list
  */
@@ -2196,9 +2202,11 @@
                         if (!capable(CAP_NET_ADMIN))
                                 return -EPERM;
                         dev_load(ifr.ifr_name);
+ dev_probe_lock();
                         rtnl_lock();
                         ret = dev_ifsioc(&ifr, cmd);
                         rtnl_unlock();
+ dev_probe_unlock();
                         return ret;
         
                 case SIOCGIFMEM:
@@ -2217,9 +2225,11 @@
                         if (cmd >= SIOCDEVPRIVATE &&
                             cmd <= SIOCDEVPRIVATE + 15) {
                                 dev_load(ifr.ifr_name);
+ dev_probe_lock();
                                 rtnl_lock();
                                 ret = dev_ifsioc(&ifr, cmd);
                                 rtnl_unlock();
+ dev_probe_unlock();
                                 if (!ret && copy_to_user(arg, &ifr, sizeof(struct ifreq)))
                                         return -EFAULT;
                                 return ret;
@@ -2388,10 +2398,12 @@
         if (ret)
                 return ret;
 #endif /* CONFIG_NET_DIVERT */
-
+
         /* Notify protocols, that a new device appeared. */
         notifier_call_chain(&netdev_chain, NETDEV_REGISTER, dev);
 
+ net_run_sbin_hotplug(dev, "register");
+
         return 0;
 }
 
@@ -2475,6 +2487,8 @@
                 /* Shutdown queueing discipline. */
                 dev_shutdown(dev);
 
+ net_run_sbin_hotplug(dev, "unregister");
+
                 /* Notify protocols, that we are about to destroy
                    this device. They should clean all the things.
                  */
@@ -2714,29 +2728,15 @@
 /* Notify userspace when a netdevice event occurs,
  * by running '/sbin/hotplug net' with certain
  * environment variables set.
- *
- * Currently reported events are listed in netdev_event_names[].
  */
 
-/* /sbin/hotplug ONLY executes for events named here */
-static char *netdev_event_names[] = {
- [NETDEV_REGISTER] = "register",
- [NETDEV_UNREGISTER] = "unregister",
-};
-
-static int run_sbin_hotplug(struct notifier_block *this,
- unsigned long event, void *ptr)
+static int net_run_sbin_hotplug(struct net_device *dev, char *action)
 {
- struct net_device *dev = (struct net_device *) ptr;
- char *argv[3], *envp[5], ifname[12 + IFNAMSIZ], action[32];
+ char *argv[3], *envp[5], ifname[12 + IFNAMSIZ], action_str[32];
         int i;
 
- if ((event >= ARRAY_SIZE(netdev_event_names)) ||
- !netdev_event_names[event])
- return NOTIFY_DONE;
-
         sprintf(ifname, "INTERFACE=%s", dev->name);
- sprintf(action, "ACTION=%s", netdev_event_names[event]);
+ sprintf(action_str, "ACTION=%s", action);
 
         i = 0;
         argv[i++] = hotplug_path;
@@ -2748,27 +2748,11 @@
         envp [i++] = "HOME=/";
         envp [i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
         envp [i++] = ifname;
- envp [i++] = action;
+ envp [i++] = action_str;
         envp [i] = 0;
         
         call_usermodehelper (argv [0], argv, envp);
 
         return NOTIFY_DONE;
-}
-
-static struct notifier_block sbin_hotplug = {
- notifier_call: run_sbin_hotplug,
-};
-
-/*
- * called from init/main.c, -after- all the initcalls are complete.
- * Registers a hook that calls /sbin/hotplug on every netdev
- * addition and removal.
- */
-void __init net_notifier_init (void)
-{
- if (register_netdevice_notifier(&sbin_hotplug))
- printk (KERN_WARNING "unable to register netdev notifier\n"
- KERN_WARNING "/sbin/hotplug will not be run.\n");
 }
 #endif
--- linux-2.4.0-test12-pre7/net/ipv4/devinet.c Thu Aug 24 21:07:25 2000
+++ linux-akpm/net/ipv4/devinet.c Sat Dec 9 11:14:03 2000
@@ -519,6 +519,7 @@
                 return -EINVAL;
         }
 
+ dev_probe_lock();
         rtnl_lock();
 
         if ((dev = __dev_get_by_name(ifr.ifr_name)) == NULL) {
@@ -649,10 +650,12 @@
         }
 done:
         rtnl_unlock();
+ dev_probe_unlock();
         return ret;
 
 rarok:
         rtnl_unlock();
+ dev_probe_unlock();
         if (copy_to_user(arg, &ifr, sizeof(struct ifreq)))
                 return -EFAULT;
         return 0;
--- linux-2.4.0-test12-pre7/init/main.c Thu Dec 7 22:05:21 2000
+++ linux-akpm/init/main.c Sat Dec 9 00:47:46 2000
@@ -716,14 +716,6 @@
         /* Mount the root filesystem.. */
         mount_root();
 
-#if defined(CONFIG_HOTPLUG) && defined(CONFIG_NET)
- /* do this after other 'do this last' stuff, because we want
- * to minimize spurious executions of /sbin/hotplug
- * during boot-up
- */
- net_notifier_init();
-#endif
-
         mount_devfs_fs ();
 
 #ifdef CONFIG_BLK_DEV_INITRD
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
Please read the FAQ at http://www.tux.org/lkml/



This archive was generated by hypermail 2b29 : Fri Dec 15 2000 - 21:00:16 EST