fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / drivers / ieee1394 / nodemgr.c
index cc9c999..e2e0771 100644 (file)
@@ -8,36 +8,36 @@
  * directory of the kernel sources for details.
  */
 
+#include <linux/bitmap.h>
 #include <linux/kernel.h>
-#include <linux/config.h>
 #include <linux/list.h>
 #include <linux/slab.h>
-#include <linux/smp_lock.h>
-#include <linux/interrupt.h>
-#include <linux/kmod.h>
-#include <linux/completion.h>
 #include <linux/delay.h>
-#include <linux/pci.h>
+#include <linux/kthread.h>
+#include <linux/module.h>
 #include <linux/moduleparam.h>
-#include <linux/suspend.h>
+#include <linux/freezer.h>
 #include <asm/atomic.h>
 
-#include "ieee1394_types.h"
-#include "ieee1394.h"
+#include "csr.h"
+#include "highlevel.h"
 #include "hosts.h"
+#include "ieee1394.h"
+#include "ieee1394_core.h"
+#include "ieee1394_hotplug.h"
+#include "ieee1394_types.h"
 #include "ieee1394_transactions.h"
-#include "highlevel.h"
-#include "csr.h"
 #include "nodemgr.h"
 
-static int ignore_drivers = 0;
-module_param(ignore_drivers, int, 0444);
+static int ignore_drivers;
+module_param(ignore_drivers, int, S_IRUGO | S_IWUSR);
 MODULE_PARM_DESC(ignore_drivers, "Disable automatic probing for drivers.");
 
 struct nodemgr_csr_info {
        struct hpsb_host *host;
        nodeid_t nodeid;
        unsigned int generation;
+       unsigned int speed_unverified:1;
 };
 
 
@@ -57,25 +57,76 @@ static char *nodemgr_find_oui_name(int oui)
        return NULL;
 }
 
+/*
+ * Correct the speed map entry.  This is necessary
+ *  - for nodes with link speed < phy speed,
+ *  - for 1394b nodes with negotiated phy port speed < IEEE1394_SPEED_MAX.
+ * A possible speed is determined by trial and error, using quadlet reads.
+ */
+static int nodemgr_check_speed(struct nodemgr_csr_info *ci, u64 addr,
+                              quadlet_t *buffer)
+{
+       quadlet_t q;
+       u8 i, *speed, old_speed, good_speed;
+       int error;
+
+       speed = &(ci->host->speed[NODEID_TO_NODE(ci->nodeid)]);
+       old_speed = *speed;
+       good_speed = IEEE1394_SPEED_MAX + 1;
+
+       /* Try every speed from S100 to old_speed.
+        * If we did it the other way around, a too low speed could be caught
+        * if the retry succeeded for some other reason, e.g. because the link
+        * just finished its initialization. */
+       for (i = IEEE1394_SPEED_100; i <= old_speed; i++) {
+               *speed = i;
+               error = hpsb_read(ci->host, ci->nodeid, ci->generation, addr,
+                                 &q, sizeof(quadlet_t));
+               if (error)
+                       break;
+               *buffer = q;
+               good_speed = i;
+       }
+       if (good_speed <= IEEE1394_SPEED_MAX) {
+               HPSB_DEBUG("Speed probe of node " NODE_BUS_FMT " yields %s",
+                          NODE_BUS_ARGS(ci->host, ci->nodeid),
+                          hpsb_speedto_str[good_speed]);
+               *speed = good_speed;
+               ci->speed_unverified = 0;
+               return 0;
+       }
+       *speed = old_speed;
+       return error;
+}
 
 static int nodemgr_bus_read(struct csr1212_csr *csr, u64 addr, u16 length,
-                            void *buffer, void *__ci)
+                           void *buffer, void *__ci)
 {
        struct nodemgr_csr_info *ci = (struct nodemgr_csr_info*)__ci;
-       int i, ret = 0;
+       int i, error;
 
-       for (i = 0; i < 3; i++) {
-               ret = hpsb_read(ci->host, ci->nodeid, ci->generation, addr,
-                               buffer, length);
-               if (!ret)
+       for (i = 1; ; i++) {
+               error = hpsb_read(ci->host, ci->nodeid, ci->generation, addr,
+                                 buffer, length);
+               if (!error) {
+                       ci->speed_unverified = 0;
+                       break;
+               }
+               /* Give up after 3rd failure. */
+               if (i == 3)
                        break;
 
-               set_current_state(TASK_INTERRUPTIBLE);
-               if (schedule_timeout (HZ/3))
+               /* The ieee1394_core guessed the node's speed capability from
+                * the self ID.  Check whether a lower speed works. */
+               if (ci->speed_unverified && length == sizeof(quadlet_t)) {
+                       error = nodemgr_check_speed(ci, addr, buffer);
+                       if (!error)
+                               break;
+               }
+               if (msleep_interruptible(334))
                        return -EINTR;
        }
-
-       return ret;
+       return error;
 }
 
 static int nodemgr_get_max_rom(quadlet_t *bus_info_data, void *__ci)
@@ -109,21 +160,17 @@ static struct csr1212_bus_ops nodemgr_csr_ops = {
  * but now we are much simpler because of the LDM.
  */
 
-static DECLARE_MUTEX(nodemgr_serialize);
+static DEFINE_MUTEX(nodemgr_serialize);
 
 struct host_info {
        struct hpsb_host *host;
        struct list_head list;
-       struct completion exited;
-       struct semaphore reset_sem;
-       int pid;
-       char daemon_name[15];
-       int kill_me;
+       struct task_struct *thread;
 };
 
 static int nodemgr_bus_match(struct device * dev, struct device_driver * drv);
-static int nodemgr_hotplug(struct class_device *cdev, char **envp, int num_envp,
-                          char *buffer, int buffer_size);
+static int nodemgr_uevent(struct class_device *cdev, char **envp, int num_envp,
+                         char *buffer, int buffer_size);
 static void nodemgr_resume_ne(struct node_entry *ne);
 static void nodemgr_remove_ne(struct node_entry *ne);
 static struct node_entry *find_entry_by_guid(u64 guid);
@@ -148,7 +195,7 @@ static void ne_cls_release(struct class_device *class_dev)
        put_device(&container_of((class_dev), struct node_entry, class_dev)->device);
 }
 
-struct class nodemgr_ne_class = {
+static struct class nodemgr_ne_class = {
        .name           = "ieee1394_node",
        .release        = ne_cls_release,
 };
@@ -160,10 +207,10 @@ static void ud_cls_release(struct class_device *class_dev)
 
 /* The name here is only so that unit directory hotplug works with old
  * style hotplug, which only ever did unit directories anyway. */
-struct class nodemgr_ud_class = {
+static struct class nodemgr_ud_class = {
        .name           = "ieee1394",
        .release        = ud_cls_release,
-       .hotplug        = nodemgr_hotplug,
+       .uevent         = nodemgr_uevent,
 };
 
 static struct hpsb_highlevel nodemgr_highlevel;
@@ -214,6 +261,16 @@ static struct device nodemgr_dev_template_ne = {
        .release        = nodemgr_release_ne,
 };
 
+/* This dummy driver prevents the host devices from being scanned. We have no
+ * useful drivers for them yet, and there would be a deadlock possible if the
+ * driver core scans the host device while the host's low-level driver (i.e.
+ * the host's parent device) is being removed. */
+static struct device_driver nodemgr_mid_layer_driver = {
+       .bus            = &ieee1394_bus_type,
+       .name           = "nodemgr",
+       .owner          = THIS_MODULE,
+};
+
 struct device nodemgr_dev_template_host = {
        .bus            = &ieee1394_bus_type,
        .release        = nodemgr_release_host,
@@ -221,7 +278,7 @@ struct device nodemgr_dev_template_host = {
 
 
 #define fw_attr(class, class_type, field, type, format_string)         \
-static ssize_t fw_show_##class##_##field (struct device *dev, char *buf)\
+static ssize_t fw_show_##class##_##field (struct device *dev, struct device_attribute *attr, char *buf)\
 {                                                                      \
        class_type *class;                                              \
        class = container_of(dev, class_type, device);                  \
@@ -233,7 +290,7 @@ static struct device_attribute dev_attr_##class##_##field = {               \
 };
 
 #define fw_attr_td(class, class_type, td_kv)                           \
-static ssize_t fw_show_##class##_##td_kv (struct device *dev, char *buf)\
+static ssize_t fw_show_##class##_##td_kv (struct device *dev, struct device_attribute *attr, char *buf)\
 {                                                                      \
        int len;                                                        \
        class_type *class = container_of(dev, class_type, device);      \
@@ -261,12 +318,12 @@ static ssize_t fw_drv_show_##field (struct device_driver *drv, char *buf) \
        return sprintf(buf, format_string, (type)driver->field);\
 }                                                              \
 static struct driver_attribute driver_attr_drv_##field = {     \
-        .attr = {.name = __stringify(field), .mode = S_IRUGO },        \
-        .show   = fw_drv_show_##field,                         \
+       .attr = {.name = __stringify(field), .mode = S_IRUGO }, \
+       .show   = fw_drv_show_##field,                          \
 };
 
 
-static ssize_t fw_show_ne_bus_options(struct device *dev, char *buf)
+static ssize_t fw_show_ne_bus_options(struct device *dev, struct device_attribute *attr, char *buf)
 {
        struct node_entry *ne = container_of(dev, struct node_entry, device);
 
@@ -282,50 +339,62 @@ static ssize_t fw_show_ne_bus_options(struct device *dev, char *buf)
 static DEVICE_ATTR(bus_options,S_IRUGO,fw_show_ne_bus_options,NULL);
 
 
-static ssize_t fw_show_ne_tlabels_free(struct device *dev, char *buf)
+#ifdef HPSB_DEBUG_TLABELS
+static ssize_t fw_show_ne_tlabels_free(struct device *dev,
+                                      struct device_attribute *attr, char *buf)
 {
        struct node_entry *ne = container_of(dev, struct node_entry, device);
-       return sprintf(buf, "%d\n", atomic_read(&ne->tpool->count.count) + 1);
-}
-static DEVICE_ATTR(tlabels_free,S_IRUGO,fw_show_ne_tlabels_free,NULL);
+       unsigned long flags;
+       unsigned long *tp = ne->host->tl_pool[NODEID_TO_NODE(ne->nodeid)].map;
+       int tf;
 
+       spin_lock_irqsave(&hpsb_tlabel_lock, flags);
+       tf = 64 - bitmap_weight(tp, 64);
+       spin_unlock_irqrestore(&hpsb_tlabel_lock, flags);
 
-static ssize_t fw_show_ne_tlabels_allocations(struct device *dev, char *buf)
-{
-       struct node_entry *ne = container_of(dev, struct node_entry, device);
-       return sprintf(buf, "%u\n", ne->tpool->allocations);
+       return sprintf(buf, "%d\n", tf);
 }
-static DEVICE_ATTR(tlabels_allocations,S_IRUGO,fw_show_ne_tlabels_allocations,NULL);
+static DEVICE_ATTR(tlabels_free,S_IRUGO,fw_show_ne_tlabels_free,NULL);
 
 
-static ssize_t fw_show_ne_tlabels_mask(struct device *dev, char *buf)
+static ssize_t fw_show_ne_tlabels_mask(struct device *dev,
+                                      struct device_attribute *attr, char *buf)
 {
        struct node_entry *ne = container_of(dev, struct node_entry, device);
+       unsigned long flags;
+       unsigned long *tp = ne->host->tl_pool[NODEID_TO_NODE(ne->nodeid)].map;
+       u64 tm;
+
+       spin_lock_irqsave(&hpsb_tlabel_lock, flags);
 #if (BITS_PER_LONG <= 32)
-       return sprintf(buf, "0x%08lx%08lx\n", ne->tpool->pool[0], ne->tpool->pool[1]);
+       tm = ((u64)tp[0] << 32) + tp[1];
 #else
-       return sprintf(buf, "0x%016lx\n", ne->tpool->pool[0]);
+       tm = tp[0];
 #endif
+       spin_unlock_irqrestore(&hpsb_tlabel_lock, flags);
+
+       return sprintf(buf, "0x%016llx\n", (unsigned long long)tm);
 }
 static DEVICE_ATTR(tlabels_mask, S_IRUGO, fw_show_ne_tlabels_mask, NULL);
+#endif /* HPSB_DEBUG_TLABELS */
 
 
-static ssize_t fw_set_ignore_driver(struct device *dev, const char *buf, size_t count)
+static ssize_t fw_set_ignore_driver(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
 {
        struct unit_directory *ud = container_of(dev, struct unit_directory, device);
        int state = simple_strtoul(buf, NULL, 10);
 
        if (state == 1) {
-               down_write(&dev->bus->subsys.rwsem);
-               device_release_driver(dev);
                ud->ignore_driver = 1;
-               up_write(&dev->bus->subsys.rwsem);
-       } else if (!state)
+               down_write(&ieee1394_bus_type.subsys.rwsem);
+               device_release_driver(dev);
+               up_write(&ieee1394_bus_type.subsys.rwsem);
+       } else if (state == 0)
                ud->ignore_driver = 0;
 
        return count;
 }
-static ssize_t fw_get_ignore_driver(struct device *dev, char *buf)
+static ssize_t fw_get_ignore_driver(struct device *dev, struct device_attribute *attr, char *buf)
 {
        struct unit_directory *ud = container_of(dev, struct unit_directory, device);
 
@@ -354,27 +423,15 @@ static ssize_t fw_get_destroy_node(struct bus_type *bus, char *buf)
 }
 static BUS_ATTR(destroy_node, S_IWUSR | S_IRUGO, fw_get_destroy_node, fw_set_destroy_node);
 
-static int nodemgr_rescan_bus_thread(void *__unused)
-{
-       /* No userlevel access needed */
-       daemonize("kfwrescan");
-
-       bus_rescan_devices(&ieee1394_bus_type);
 
-       return 0;
-}
-
-static ssize_t fw_set_rescan(struct bus_type *bus, const char *buf, size_t count)
+static ssize_t fw_set_rescan(struct bus_type *bus, const char *buf,
+                            size_t count)
 {
-       int state = simple_strtoul(buf, NULL, 10);
-
-       /* Don't wait for this, or care about errors. Root could do
-        * something stupid and spawn this a lot of times, but that's
-        * root's fault. */
-       if (state == 1)
-               kernel_thread(nodemgr_rescan_bus_thread, NULL, CLONE_KERNEL);
+       int error = 0;
 
-       return count;
+       if (simple_strtoul(buf, NULL, 10) == 1)
+               error = bus_rescan_devices(&ieee1394_bus_type);
+       return error ? error : count;
 }
 static ssize_t fw_get_rescan(struct bus_type *bus, char *buf)
 {
@@ -390,7 +447,7 @@ static ssize_t fw_set_ignore_drivers(struct bus_type *bus, const char *buf, size
 
        if (state == 1)
                ignore_drivers = 1;
-       else if (!state)
+       else if (state == 0)
                ignore_drivers = 0;
 
        return count;
@@ -429,9 +486,10 @@ static struct device_attribute *const fw_ne_attrs[] = {
        &dev_attr_ne_vendor_id,
        &dev_attr_ne_nodeid,
        &dev_attr_bus_options,
+#ifdef HPSB_DEBUG_TLABELS
        &dev_attr_tlabels_free,
-       &dev_attr_tlabels_allocations,
        &dev_attr_tlabels_mask,
+#endif
 };
 
 
@@ -482,7 +540,7 @@ static ssize_t fw_show_drv_device_ids(struct device_driver *drv, char *buf)
        int length = 0;
        char *scratch = buf;
 
-        driver = container_of(drv, struct hpsb_protocol_driver, driver);
+       driver = container_of(drv, struct hpsb_protocol_driver, driver);
 
        for (id = driver->id_table; id->match_flags != 0; id++) {
                int need_coma = 0;
@@ -539,7 +597,11 @@ static void nodemgr_create_drv_files(struct hpsb_protocol_driver *driver)
        int i;
 
        for (i = 0; i < ARRAY_SIZE(fw_drv_attrs); i++)
-               driver_create_file(drv, fw_drv_attrs[i]);
+               if (driver_create_file(drv, fw_drv_attrs[i]))
+                       goto fail;
+       return;
+fail:
+       HPSB_ERR("Failed to add sysfs attribute for driver %s", driver->name);
 }
 
 
@@ -559,7 +621,12 @@ static void nodemgr_create_ne_dev_files(struct node_entry *ne)
        int i;
 
        for (i = 0; i < ARRAY_SIZE(fw_ne_attrs); i++)
-               device_create_file(dev, fw_ne_attrs[i]);
+               if (device_create_file(dev, fw_ne_attrs[i]))
+                       goto fail;
+       return;
+fail:
+       HPSB_ERR("Failed to add sysfs attribute for node %016Lx",
+                (unsigned long long)ne->guid);
 }
 
 
@@ -569,11 +636,16 @@ static void nodemgr_create_host_dev_files(struct hpsb_host *host)
        int i;
 
        for (i = 0; i < ARRAY_SIZE(fw_host_attrs); i++)
-               device_create_file(dev, fw_host_attrs[i]);
+               if (device_create_file(dev, fw_host_attrs[i]))
+                       goto fail;
+       return;
+fail:
+       HPSB_ERR("Failed to add sysfs attribute for host %d", host->id);
 }
 
 
-static struct node_entry *find_entry_by_nodeid(struct hpsb_host *host, nodeid_t nodeid);
+static struct node_entry *find_entry_by_nodeid(struct hpsb_host *host,
+                                              nodeid_t nodeid);
 
 static void nodemgr_update_host_dev_links(struct hpsb_host *host)
 {
@@ -584,12 +656,18 @@ static void nodemgr_update_host_dev_links(struct hpsb_host *host)
        sysfs_remove_link(&dev->kobj, "busmgr_id");
        sysfs_remove_link(&dev->kobj, "host_id");
 
-       if ((ne = find_entry_by_nodeid(host, host->irm_id)))
-               sysfs_create_link(&dev->kobj, &ne->device.kobj, "irm_id");
-       if ((ne = find_entry_by_nodeid(host, host->busmgr_id)))
-               sysfs_create_link(&dev->kobj, &ne->device.kobj, "busmgr_id");
-       if ((ne = find_entry_by_nodeid(host, host->node_id)))
-               sysfs_create_link(&dev->kobj, &ne->device.kobj, "host_id");
+       if ((ne = find_entry_by_nodeid(host, host->irm_id)) &&
+           sysfs_create_link(&dev->kobj, &ne->device.kobj, "irm_id"))
+               goto fail;
+       if ((ne = find_entry_by_nodeid(host, host->busmgr_id)) &&
+           sysfs_create_link(&dev->kobj, &ne->device.kobj, "busmgr_id"))
+               goto fail;
+       if ((ne = find_entry_by_nodeid(host, host->node_id)) &&
+           sysfs_create_link(&dev->kobj, &ne->device.kobj, "host_id"))
+               goto fail;
+       return;
+fail:
+       HPSB_ERR("Failed to update sysfs attributes for host %d", host->id);
 }
 
 static void nodemgr_create_ud_dev_files(struct unit_directory *ud)
@@ -598,32 +676,39 @@ static void nodemgr_create_ud_dev_files(struct unit_directory *ud)
        int i;
 
        for (i = 0; i < ARRAY_SIZE(fw_ud_attrs); i++)
-               device_create_file(dev, fw_ud_attrs[i]);
-
+               if (device_create_file(dev, fw_ud_attrs[i]))
+                       goto fail;
        if (ud->flags & UNIT_DIRECTORY_SPECIFIER_ID)
-               device_create_file(dev, &dev_attr_ud_specifier_id);
-
+               if (device_create_file(dev, &dev_attr_ud_specifier_id))
+                       goto fail;
        if (ud->flags & UNIT_DIRECTORY_VERSION)
-               device_create_file(dev, &dev_attr_ud_version);
-
+               if (device_create_file(dev, &dev_attr_ud_version))
+                       goto fail;
        if (ud->flags & UNIT_DIRECTORY_VENDOR_ID) {
-               device_create_file(dev, &dev_attr_ud_vendor_id);
-               if (ud->vendor_name_kv)
-                       device_create_file(dev, &dev_attr_ud_vendor_name_kv);
+               if (device_create_file(dev, &dev_attr_ud_vendor_id))
+                       goto fail;
+               if (ud->vendor_name_kv &&
+                   device_create_file(dev, &dev_attr_ud_vendor_name_kv))
+                       goto fail;
        }
-
        if (ud->flags & UNIT_DIRECTORY_MODEL_ID) {
-               device_create_file(dev, &dev_attr_ud_model_id);
-               if (ud->model_name_kv)
-                       device_create_file(dev, &dev_attr_ud_model_name_kv);
+               if (device_create_file(dev, &dev_attr_ud_model_id))
+                       goto fail;
+               if (ud->model_name_kv &&
+                   device_create_file(dev, &dev_attr_ud_model_name_kv))
+                       goto fail;
        }
+       return;
+fail:
+       HPSB_ERR("Failed to add sysfs attributes for unit %s",
+                ud->device.bus_id);
 }
 
 
 static int nodemgr_bus_match(struct device * dev, struct device_driver * drv)
 {
-        struct hpsb_protocol_driver *driver;
-        struct unit_directory *ud;
+       struct hpsb_protocol_driver *driver;
+       struct unit_directory *ud;
        struct ieee1394_device_id *id;
 
        /* We only match unit directories */
@@ -631,55 +716,77 @@ static int nodemgr_bus_match(struct device * dev, struct device_driver * drv)
                return 0;
 
        ud = container_of(dev, struct unit_directory, device);
-       driver = container_of(drv, struct hpsb_protocol_driver, driver);
-
        if (ud->ne->in_limbo || ud->ignore_driver)
                return 0;
 
-        for (id = driver->id_table; id->match_flags != 0; id++) {
-                if ((id->match_flags & IEEE1394_MATCH_VENDOR_ID) &&
-                    id->vendor_id != ud->vendor_id)
-                        continue;
+       /* We only match drivers of type hpsb_protocol_driver */
+       if (drv == &nodemgr_mid_layer_driver)
+               return 0;
 
-                if ((id->match_flags & IEEE1394_MATCH_MODEL_ID) &&
-                    id->model_id != ud->model_id)
-                        continue;
+       driver = container_of(drv, struct hpsb_protocol_driver, driver);
+       for (id = driver->id_table; id->match_flags != 0; id++) {
+               if ((id->match_flags & IEEE1394_MATCH_VENDOR_ID) &&
+                   id->vendor_id != ud->vendor_id)
+                       continue;
 
-                if ((id->match_flags & IEEE1394_MATCH_SPECIFIER_ID) &&
-                    id->specifier_id != ud->specifier_id)
-                        continue;
+               if ((id->match_flags & IEEE1394_MATCH_MODEL_ID) &&
+                   id->model_id != ud->model_id)
+                       continue;
 
-                if ((id->match_flags & IEEE1394_MATCH_VERSION) &&
-                    id->version != ud->version)
-                        continue;
+               if ((id->match_flags & IEEE1394_MATCH_SPECIFIER_ID) &&
+                   id->specifier_id != ud->specifier_id)
+                       continue;
+
+               if ((id->match_flags & IEEE1394_MATCH_VERSION) &&
+                   id->version != ud->version)
+                       continue;
 
                return 1;
-        }
+       }
 
        return 0;
 }
 
 
+static DEFINE_MUTEX(nodemgr_serialize_remove_uds);
+
 static void nodemgr_remove_uds(struct node_entry *ne)
 {
-       struct class_device *cdev, *next;
-       struct unit_directory *ud;
-
-       list_for_each_entry_safe(cdev, next, &nodemgr_ud_class.children, node) {
-               ud = container_of(cdev, struct unit_directory, class_dev);
-
-               if (ud->ne != ne)
-                       continue;
-
+       struct class_device *cdev;
+       struct unit_directory *tmp, *ud;
+
+       /* Iteration over nodemgr_ud_class.children has to be protected by
+        * nodemgr_ud_class.sem, but class_device_unregister() will eventually
+        * take nodemgr_ud_class.sem too. Therefore pick out one ud at a time,
+        * release the semaphore, and then unregister the ud. Since this code
+        * may be called from other contexts besides the knodemgrds, protect the
+        * gap after release of the semaphore by nodemgr_serialize_remove_uds.
+        */
+       mutex_lock(&nodemgr_serialize_remove_uds);
+       for (;;) {
+               ud = NULL;
+               down(&nodemgr_ud_class.sem);
+               list_for_each_entry(cdev, &nodemgr_ud_class.children, node) {
+                       tmp = container_of(cdev, struct unit_directory,
+                                          class_dev);
+                       if (tmp->ne == ne) {
+                               ud = tmp;
+                               break;
+                       }
+               }
+               up(&nodemgr_ud_class.sem);
+               if (ud == NULL)
+                       break;
                class_device_unregister(&ud->class_dev);
                device_unregister(&ud->device);
        }
+       mutex_unlock(&nodemgr_serialize_remove_uds);
 }
 
 
 static void nodemgr_remove_ne(struct node_entry *ne)
 {
-       struct device *dev = &ne->device;
+       struct device *dev;
 
        dev = get_device(&ne->device);
        if (!dev)
@@ -696,14 +803,15 @@ static void nodemgr_remove_ne(struct node_entry *ne)
        put_device(dev);
 }
 
+static int __nodemgr_remove_host_dev(struct device *dev, void *data)
+{
+       nodemgr_remove_ne(container_of(dev, struct node_entry, device));
+       return 0;
+}
 
 static void nodemgr_remove_host_dev(struct device *dev)
 {
-       struct device *ne_dev, *next;
-
-       list_for_each_entry_safe(ne_dev, next, &dev->children, node)
-               nodemgr_remove_ne(container_of(ne_dev, struct node_entry, device));
-
+       WARN_ON(device_for_each_child(dev, NULL, __nodemgr_remove_host_dev));
        sysfs_remove_link(&dev->kobj, "irm_id");
        sysfs_remove_link(&dev->kobj, "busmgr_id");
        sysfs_remove_link(&dev->kobj, "host_id");
@@ -717,16 +825,16 @@ static void nodemgr_update_bus_options(struct node_entry *ne)
 #endif
        quadlet_t busoptions = be32_to_cpu(ne->csr->bus_info_data[2]);
 
-       ne->busopt.irmc         = (busoptions >> 31) & 1;
-       ne->busopt.cmc          = (busoptions >> 30) & 1;
-       ne->busopt.isc          = (busoptions >> 29) & 1;
-       ne->busopt.bmc          = (busoptions >> 28) & 1;
-       ne->busopt.pmc          = (busoptions >> 27) & 1;
-       ne->busopt.cyc_clk_acc  = (busoptions >> 16) & 0xff;
-       ne->busopt.max_rec      = 1 << (((busoptions >> 12) & 0xf) + 1);
+       ne->busopt.irmc         = (busoptions >> 31) & 1;
+       ne->busopt.cmc          = (busoptions >> 30) & 1;
+       ne->busopt.isc          = (busoptions >> 29) & 1;
+       ne->busopt.bmc          = (busoptions >> 28) & 1;
+       ne->busopt.pmc          = (busoptions >> 27) & 1;
+       ne->busopt.cyc_clk_acc  = (busoptions >> 16) & 0xff;
+       ne->busopt.max_rec      = 1 << (((busoptions >> 12) & 0xf) + 1);
        ne->busopt.max_rom      = (busoptions >> 8) & 0x3;
-       ne->busopt.generation   = (busoptions >> 4) & 0xf;
-       ne->busopt.lnkspd       = busoptions & 0x7;
+       ne->busopt.generation   = (busoptions >> 4) & 0xf;
+       ne->busopt.lnkspd       = busoptions & 0x7;
 
        HPSB_VERBOSE("NodeMgr: raw=0x%08x irmc=%d cmc=%d isc=%d bmc=%d pmc=%d "
                     "cyc_clk_acc=%d max_rec=%d max_rom=%d gen=%d lspd=%d",
@@ -743,21 +851,18 @@ static struct node_entry *nodemgr_create_node(octlet_t guid, struct csr1212_csr
                                              unsigned int generation)
 {
        struct hpsb_host *host = hi->host;
-        struct node_entry *ne;
-
-       ne = kmalloc(sizeof(struct node_entry), GFP_KERNEL);
-        if (!ne) return NULL;
-
-       memset(ne, 0, sizeof(struct node_entry));
+       struct node_entry *ne;
 
-       ne->tpool = &host->tpool[nodeid & NODE_MASK];
+       ne = kzalloc(sizeof(*ne), GFP_KERNEL);
+       if (!ne)
+               goto fail_alloc;
 
-        ne->host = host;
-        ne->nodeid = nodeid;
+       ne->host = host;
+       ne->nodeid = nodeid;
        ne->generation = generation;
        ne->needs_probe = 1;
 
-        ne->guid = guid;
+       ne->guid = guid;
        ne->guid_vendor_id = (guid >> 40) & 0xffffff;
        ne->guid_vendor_oui = nodemgr_find_oui_name(ne->guid_vendor_id);
        ne->csr = csr;
@@ -773,12 +878,15 @@ static struct node_entry *nodemgr_create_node(octlet_t guid, struct csr1212_csr
        snprintf(ne->class_dev.class_id, BUS_ID_SIZE, "%016Lx",
                 (unsigned long long)(ne->guid));
 
-       device_register(&ne->device);
-       class_device_register(&ne->class_dev);
+       if (device_register(&ne->device))
+               goto fail_devreg;
+       if (class_device_register(&ne->class_dev))
+               goto fail_classdevreg;
        get_device(&ne->device);
 
-       if (ne->guid_vendor_oui)
-               device_create_file(&ne->device, &dev_attr_ne_guid_vendor_oui);
+       if (ne->guid_vendor_oui &&
+           device_create_file(&ne->device, &dev_attr_ne_guid_vendor_oui))
+               goto fail_addoiu;
        nodemgr_create_ne_dev_files(ne);
 
        nodemgr_update_bus_options(ne);
@@ -787,18 +895,29 @@ static struct node_entry *nodemgr_create_node(octlet_t guid, struct csr1212_csr
                   (host->node_id == nodeid) ? "Host" : "Node",
                   NODE_BUS_ARGS(host, nodeid), (unsigned long long)guid);
 
-        return ne;
+       return ne;
+
+fail_addoiu:
+       put_device(&ne->device);
+fail_classdevreg:
+       device_unregister(&ne->device);
+fail_devreg:
+       kfree(ne);
+fail_alloc:
+       HPSB_ERR("Failed to create node ID:BUS[" NODE_BUS_FMT "]  GUID[%016Lx]",
+                NODE_BUS_ARGS(host, nodeid), (unsigned long long)guid);
+
+       return NULL;
 }
 
 
 static struct node_entry *find_entry_by_guid(u64 guid)
 {
-       struct class *class = &nodemgr_ne_class;
        struct class_device *cdev;
        struct node_entry *ne, *ret_ne = NULL;
 
-       down_read(&class->subsys.rwsem);
-       list_for_each_entry(cdev, &class->children, node) {
+       down(&nodemgr_ne_class.sem);
+       list_for_each_entry(cdev, &nodemgr_ne_class.children, node) {
                ne = container_of(cdev, struct node_entry, class_dev);
 
                if (ne->guid == guid) {
@@ -806,20 +925,20 @@ static struct node_entry *find_entry_by_guid(u64 guid)
                        break;
                }
        }
-       up_read(&class->subsys.rwsem);
+       up(&nodemgr_ne_class.sem);
 
-        return ret_ne;
+       return ret_ne;
 }
 
 
-static struct node_entry *find_entry_by_nodeid(struct hpsb_host *host, nodeid_t nodeid)
+static struct node_entry *find_entry_by_nodeid(struct hpsb_host *host,
+                                              nodeid_t nodeid)
 {
-       struct class *class = &nodemgr_ne_class;
        struct class_device *cdev;
        struct node_entry *ne, *ret_ne = NULL;
 
-       down_read(&class->subsys.rwsem);
-       list_for_each_entry(cdev, &class->children, node) {
+       down(&nodemgr_ne_class.sem);
+       list_for_each_entry(cdev, &nodemgr_ne_class.children, node) {
                ne = container_of(cdev, struct node_entry, class_dev);
 
                if (ne->host == host && ne->nodeid == nodeid) {
@@ -827,12 +946,49 @@ static struct node_entry *find_entry_by_nodeid(struct hpsb_host *host, nodeid_t
                        break;
                }
        }
-       up_read(&class->subsys.rwsem);
+       up(&nodemgr_ne_class.sem);
 
        return ret_ne;
 }
 
 
+static void nodemgr_register_device(struct node_entry *ne, 
+       struct unit_directory *ud, struct device *parent)
+{
+       memcpy(&ud->device, &nodemgr_dev_template_ud,
+              sizeof(ud->device));
+
+       ud->device.parent = parent;
+
+       snprintf(ud->device.bus_id, BUS_ID_SIZE, "%s-%u",
+                ne->device.bus_id, ud->id);
+
+       ud->class_dev.dev = &ud->device;
+       ud->class_dev.class = &nodemgr_ud_class;
+       snprintf(ud->class_dev.class_id, BUS_ID_SIZE, "%s-%u",
+                ne->device.bus_id, ud->id);
+
+       if (device_register(&ud->device))
+               goto fail_devreg;
+       if (class_device_register(&ud->class_dev))
+               goto fail_classdevreg;
+       get_device(&ud->device);
+
+       if (ud->vendor_oui &&
+           device_create_file(&ud->device, &dev_attr_ud_vendor_oui))
+               goto fail_addoui;
+       nodemgr_create_ud_dev_files(ud);
+
+       return;
+
+fail_addoui:
+       put_device(&ud->device);
+fail_classdevreg:
+       device_unregister(&ud->device);
+fail_devreg:
+       HPSB_ERR("Failed to create unit %s", ud->device.bus_id);
+}      
+
 
 /* This implementation currently only scans the config rom and its
  * immediate unit directories looking for software_id and
@@ -842,17 +998,15 @@ static struct unit_directory *nodemgr_process_unit_directory
         unsigned int *id, struct unit_directory *parent)
 {
        struct unit_directory *ud;
-       struct unit_directory *ud_temp = NULL;
+       struct unit_directory *ud_child = NULL;
        struct csr1212_dentry *dentry;
        struct csr1212_keyval *kv;
        u8 last_key_id = 0;
 
-       ud = kmalloc(sizeof(struct unit_directory), GFP_KERNEL);
+       ud = kzalloc(sizeof(*ud), GFP_KERNEL);
        if (!ud)
                goto unit_directory_error;
 
-       memset (ud, 0, sizeof(struct unit_directory));
-
        ud->ne = ne;
        ud->ignore_driver = ignore_drivers;
        ud->address = ud_kv->offset + CSR1212_CONFIG_ROM_SPACE_BASE;
@@ -909,42 +1063,60 @@ static struct unit_directory *nodemgr_process_unit_directory
                        break;
 
                case CSR1212_KV_ID_DEPENDENT_INFO:
-                       if (kv->key.type == CSR1212_KV_TYPE_DIRECTORY) {
+                       /* Logical Unit Number */
+                       if (kv->key.type == CSR1212_KV_TYPE_IMMEDIATE) {
+                               if (ud->flags & UNIT_DIRECTORY_HAS_LUN) {
+                                       ud_child = kmemdup(ud, sizeof(*ud_child), GFP_KERNEL);
+                                       if (!ud_child)
+                                               goto unit_directory_error;
+                                       nodemgr_register_device(ne, ud_child, &ne->device);
+                                       ud_child = NULL;
+                                       
+                                       ud->id = (*id)++;
+                               }
+                               ud->lun = kv->value.immediate;
+                               ud->flags |= UNIT_DIRECTORY_HAS_LUN;
+
+                       /* Logical Unit Directory */
+                       } else if (kv->key.type == CSR1212_KV_TYPE_DIRECTORY) {
                                /* This should really be done in SBP2 as this is
-                                * doing SBP2 specific parsing. */
+                                * doing SBP2 specific parsing.
+                                */
+                               
+                               /* first register the parent unit */
                                ud->flags |= UNIT_DIRECTORY_HAS_LUN_DIRECTORY;
-                               ud_temp = nodemgr_process_unit_directory(hi, ne, kv, id,
-                                                                        parent);
+                               if (ud->device.bus != &ieee1394_bus_type)
+                                       nodemgr_register_device(ne, ud, &ne->device);
+                               
+                               /* process the child unit */
+                               ud_child = nodemgr_process_unit_directory(hi, ne, kv, id, ud);
 
-                               if (ud_temp == NULL)
+                               if (ud_child == NULL)
                                        break;
-
-                               /* inherit unspecified values */
-                               if ((ud->flags & UNIT_DIRECTORY_VENDOR_ID) &&
-                                   !(ud_temp->flags & UNIT_DIRECTORY_VENDOR_ID))
-                               {
-                                       ud_temp->flags |=  UNIT_DIRECTORY_VENDOR_ID;
-                                       ud_temp->vendor_id = ud->vendor_id;
-                                       ud_temp->vendor_oui = ud->vendor_oui;
-                               }
+                               
+                               /* inherit unspecified values, the driver core picks it up */
                                if ((ud->flags & UNIT_DIRECTORY_MODEL_ID) &&
-                                   !(ud_temp->flags & UNIT_DIRECTORY_MODEL_ID))
+                                   !(ud_child->flags & UNIT_DIRECTORY_MODEL_ID))
                                {
-                                       ud_temp->flags |=  UNIT_DIRECTORY_MODEL_ID;
-                                       ud_temp->model_id = ud->model_id;
+                                       ud_child->flags |=  UNIT_DIRECTORY_MODEL_ID;
+                                       ud_child->model_id = ud->model_id;
                                }
                                if ((ud->flags & UNIT_DIRECTORY_SPECIFIER_ID) &&
-                                   !(ud_temp->flags & UNIT_DIRECTORY_SPECIFIER_ID))
+                                   !(ud_child->flags & UNIT_DIRECTORY_SPECIFIER_ID))
                                {
-                                       ud_temp->flags |=  UNIT_DIRECTORY_SPECIFIER_ID;
-                                       ud_temp->specifier_id = ud->specifier_id;
+                                       ud_child->flags |=  UNIT_DIRECTORY_SPECIFIER_ID;
+                                       ud_child->specifier_id = ud->specifier_id;
                                }
                                if ((ud->flags & UNIT_DIRECTORY_VERSION) &&
-                                   !(ud_temp->flags & UNIT_DIRECTORY_VERSION))
+                                   !(ud_child->flags & UNIT_DIRECTORY_VERSION))
                                {
-                                       ud_temp->flags |=  UNIT_DIRECTORY_VERSION;
-                                       ud_temp->version = ud->version;
+                                       ud_child->flags |=  UNIT_DIRECTORY_VERSION;
+                                       ud_child->version = ud->version;
                                }
+                               
+                               /* register the child unit */
+                               ud_child->flags |= UNIT_DIRECTORY_LUN_DIRECTORY;
+                               nodemgr_register_device(ne, ud_child, &ud->device);
                        }
 
                        break;
@@ -954,37 +1126,15 @@ static struct unit_directory *nodemgr_process_unit_directory
                }
                last_key_id = kv->key.id;
        }
-
-       memcpy(&ud->device, &nodemgr_dev_template_ud,
-              sizeof(ud->device));
-
-       if (parent) {
-               ud->flags |= UNIT_DIRECTORY_LUN_DIRECTORY;
-               ud->device.parent = &parent->device;
-       } else
-               ud->device.parent = &ne->device;
-
-       snprintf(ud->device.bus_id, BUS_ID_SIZE, "%s-%u",
-                ne->device.bus_id, ud->id);
-
-       ud->class_dev.dev = &ud->device;
-       ud->class_dev.class = &nodemgr_ud_class;
-       snprintf(ud->class_dev.class_id, BUS_ID_SIZE, "%s-%u",
-                ne->device.bus_id, ud->id);
-
-       device_register(&ud->device);
-       class_device_register(&ud->class_dev);
-       get_device(&ud->device);
-
-       if (ud->vendor_oui)
-               device_create_file(&ud->device, &dev_attr_ud_vendor_oui);
-       nodemgr_create_ud_dev_files(ud);
+       
+       /* do not process child units here and only if not already registered */
+       if (!parent && ud->device.bus != &ieee1394_bus_type)
+               nodemgr_register_device(ne, ud, &ne->device);
 
        return ud;
 
 unit_directory_error:
-       if (ud != NULL)
-               kfree(ud);
+       kfree(ud);
        return NULL;
 }
 
@@ -1032,20 +1182,28 @@ static void nodemgr_process_root_directory(struct host_info *hi, struct node_ent
                last_key_id = kv->key.id;
        }
 
-       if (ne->vendor_oui)
-               device_create_file(&ne->device, &dev_attr_ne_vendor_oui);
-       if (ne->vendor_name_kv)
-               device_create_file(&ne->device, &dev_attr_ne_vendor_name_kv);
+       if (ne->vendor_oui &&
+           device_create_file(&ne->device, &dev_attr_ne_vendor_oui))
+               goto fail;
+       if (ne->vendor_name_kv &&
+           device_create_file(&ne->device, &dev_attr_ne_vendor_name_kv))
+               goto fail;
+       return;
+fail:
+       HPSB_ERR("Failed to add sysfs attribute for node %016Lx",
+                (unsigned long long)ne->guid);
 }
 
 #ifdef CONFIG_HOTPLUG
 
-static int nodemgr_hotplug(struct class_device *cdev, char **envp, int num_envp,
-                          char *buffer, int buffer_size)
+static int nodemgr_uevent(struct class_device *cdev, char **envp, int num_envp,
+                         char *buffer, int buffer_size)
 {
        struct unit_directory *ud;
        int i = 0;
        int length = 0;
+       /* ieee1394:venNmoNspNverN */
+       char buf[8 + 1 + 3 + 8 + 2 + 8 + 2 + 8 + 3 + 8 + 1];
 
        if (!cdev)
                return -ENODEV;
@@ -1072,6 +1230,12 @@ do {                                                             \
        PUT_ENVP("GUID=%016Lx", (unsigned long long)ud->ne->guid);
        PUT_ENVP("SPECIFIER_ID=%06x", ud->specifier_id);
        PUT_ENVP("VERSION=%06x", ud->version);
+       snprintf(buf, sizeof(buf), "ieee1394:ven%08Xmo%08Xsp%08Xver%08X",
+                       ud->vendor_id,
+                       ud->model_id,
+                       ud->specifier_id,
+                       ud->version);
+       PUT_ENVP("MODALIAS=%s", buf);
 
 #undef PUT_ENVP
 
@@ -1082,8 +1246,8 @@ do {                                                              \
 
 #else
 
-static int nodemgr_hotplug(struct class_device *cdev, char **envp, int num_envp,
-                          char *buffer, int buffer_size)
+static int nodemgr_uevent(struct class_device *cdev, char **envp, int num_envp,
+                         char *buffer, int buffer_size)
 {
        return -ENODEV;
 }
@@ -1091,16 +1255,20 @@ static int nodemgr_hotplug(struct class_device *cdev, char **envp, int num_envp,
 #endif /* CONFIG_HOTPLUG */
 
 
-int hpsb_register_protocol(struct hpsb_protocol_driver *driver)
+int __hpsb_register_protocol(struct hpsb_protocol_driver *drv,
+                            struct module *owner)
 {
-       int ret;
+       int error;
 
-       /* This will cause a probe for devices */
-       ret = driver_register(&driver->driver);
-       if (!ret)
-               nodemgr_create_drv_files(driver);
+       drv->driver.bus = &ieee1394_bus_type;
+       drv->driver.owner = owner;
+       drv->driver.name = drv->name;
 
-       return ret;
+       /* This will cause a probe for devices */
+       error = driver_register(&drv->driver);
+       if (!error)
+               nodemgr_create_drv_files(drv);
+       return error;
 }
 
 void hpsb_unregister_protocol(struct hpsb_protocol_driver *driver)
@@ -1143,6 +1311,13 @@ static void nodemgr_update_node(struct node_entry *ne, struct csr1212_csr *csr,
 
                /* Mark the node as new, so it gets re-probed */
                ne->needs_probe = 1;
+       } else {
+               /* old cache is valid, so update its generation */
+               struct nodemgr_csr_info *ci = ne->csr->private;
+               ci->generation = generation;
+               /* free the partially filled now unneeded new cache */
+               kfree(csr->private);
+               csr1212_destroy_csr(csr);
        }
 
        if (ne->in_limbo)
@@ -1162,8 +1337,9 @@ static void nodemgr_node_scan_one(struct host_info *hi,
        octlet_t guid;
        struct csr1212_csr *csr;
        struct nodemgr_csr_info *ci;
+       u8 *speed;
 
-       ci = kmalloc(sizeof(struct nodemgr_csr_info), GFP_KERNEL);
+       ci = kmalloc(sizeof(*ci), GFP_KERNEL);
        if (!ci)
                return;
 
@@ -1171,6 +1347,12 @@ static void nodemgr_node_scan_one(struct host_info *hi,
        ci->nodeid = nodeid;
        ci->generation = generation;
 
+       /* Prepare for speed probe which occurs when reading the ROM */
+       speed = &(host->speed[NODEID_TO_NODE(nodeid)]);
+       if (*speed > host->csr.lnk_spd)
+               *speed = host->csr.lnk_spd;
+       ci->speed_unverified = *speed > IEEE1394_SPEED_100;
+
        /* We need to detect when the ConfigROM's generation has changed,
         * so we only update the node's info when it needs to be.  */
 
@@ -1209,29 +1391,27 @@ static void nodemgr_node_scan_one(struct host_info *hi,
                nodemgr_create_node(guid, csr, hi, nodeid, generation);
        else
                nodemgr_update_node(ne, csr, hi, nodeid, generation);
-
-       return;
 }
 
 
 static void nodemgr_node_scan(struct host_info *hi, int generation)
 {
-        int count;
-        struct hpsb_host *host = hi->host;
-        struct selfid *sid = (struct selfid *)host->topology_map;
-        nodeid_t nodeid = LOCAL_BUS;
+       int count;
+       struct hpsb_host *host = hi->host;
+       struct selfid *sid = (struct selfid *)host->topology_map;
+       nodeid_t nodeid = LOCAL_BUS;
 
-        /* Scan each node on the bus */
-        for (count = host->selfid_count; count; count--, sid++) {
-                if (sid->extended)
-                        continue;
+       /* Scan each node on the bus */
+       for (count = host->selfid_count; count; count--, sid++) {
+               if (sid->extended)
+                       continue;
 
-                if (!sid->link_active) {
-                        nodeid++;
-                        continue;
-                }
-                nodemgr_node_scan_one(hi, nodeid++, generation);
-        }
+               if (!sid->link_active) {
+                       nodeid++;
+                       continue;
+               }
+               nodemgr_node_scan_one(hi, nodeid++, generation);
+       }
 }
 
 
@@ -1244,21 +1424,22 @@ static void nodemgr_suspend_ne(struct node_entry *ne)
                   NODE_BUS_ARGS(ne->host, ne->nodeid), (unsigned long long)ne->guid);
 
        ne->in_limbo = 1;
-       device_create_file(&ne->device, &dev_attr_ne_in_limbo);
+       WARN_ON(device_create_file(&ne->device, &dev_attr_ne_in_limbo));
 
-       down_write(&ne->device.bus->subsys.rwsem);
+       down(&nodemgr_ud_class.sem);
        list_for_each_entry(cdev, &nodemgr_ud_class.children, node) {
                ud = container_of(cdev, struct unit_directory, class_dev);
-
                if (ud->ne != ne)
                        continue;
 
+               down_write(&ieee1394_bus_type.subsys.rwsem);
                if (ud->device.driver &&
                    (!ud->device.driver->suspend ||
-                     ud->device.driver->suspend(&ud->device, 0, 0)))
+                     ud->device.driver->suspend(&ud->device, PMSG_SUSPEND)))
                        device_release_driver(&ud->device);
+               up_write(&ieee1394_bus_type.subsys.rwsem);
        }
-       up_write(&ne->device.bus->subsys.rwsem);
+       up(&nodemgr_ud_class.sem);
 }
 
 
@@ -1270,17 +1451,18 @@ static void nodemgr_resume_ne(struct node_entry *ne)
        ne->in_limbo = 0;
        device_remove_file(&ne->device, &dev_attr_ne_in_limbo);
 
-       down_read(&ne->device.bus->subsys.rwsem);
+       down(&nodemgr_ud_class.sem);
        list_for_each_entry(cdev, &nodemgr_ud_class.children, node) {
                ud = container_of(cdev, struct unit_directory, class_dev);
-
                if (ud->ne != ne)
                        continue;
 
+               down_read(&ieee1394_bus_type.subsys.rwsem);
                if (ud->device.driver && ud->device.driver->resume)
-                       ud->device.driver->resume(&ud->device, 0);
+                       ud->device.driver->resume(&ud->device);
+               up_read(&ieee1394_bus_type.subsys.rwsem);
        }
-       up_read(&ne->device.bus->subsys.rwsem);
+       up(&nodemgr_ud_class.sem);
 
        HPSB_DEBUG("Node resumed: ID:BUS[" NODE_BUS_FMT "]  GUID[%016Lx]",
                   NODE_BUS_ARGS(ne->host, ne->nodeid), (unsigned long long)ne->guid);
@@ -1291,24 +1473,52 @@ static void nodemgr_update_pdrv(struct node_entry *ne)
 {
        struct unit_directory *ud;
        struct hpsb_protocol_driver *pdrv;
-       struct class *class = &nodemgr_ud_class;
        struct class_device *cdev;
 
-       down_read(&class->subsys.rwsem);
-       list_for_each_entry(cdev, &class->children, node) {
+       down(&nodemgr_ud_class.sem);
+       list_for_each_entry(cdev, &nodemgr_ud_class.children, node) {
                ud = container_of(cdev, struct unit_directory, class_dev);
-               if (ud->ne != ne || !ud->device.driver)
+               if (ud->ne != ne)
                        continue;
 
-               pdrv = container_of(ud->device.driver, struct hpsb_protocol_driver, driver);
-
-               if (pdrv->update && pdrv->update(ud)) {
-                       down_write(&ud->device.bus->subsys.rwsem);
-                       device_release_driver(&ud->device);
-                       up_write(&ud->device.bus->subsys.rwsem);
+               down_write(&ieee1394_bus_type.subsys.rwsem);
+               if (ud->device.driver) {
+                       pdrv = container_of(ud->device.driver,
+                                           struct hpsb_protocol_driver,
+                                           driver);
+                       if (pdrv->update && pdrv->update(ud))
+                               device_release_driver(&ud->device);
                }
+               up_write(&ieee1394_bus_type.subsys.rwsem);
        }
-       up_read(&class->subsys.rwsem);
+       up(&nodemgr_ud_class.sem);
+}
+
+
+/* Write the BROADCAST_CHANNEL as per IEEE1394a 8.3.2.3.11 and 8.4.2.3.  This
+ * seems like an optional service but in the end it is practically mandatory
+ * as a consequence of these clauses.
+ *
+ * Note that we cannot do a broadcast write to all nodes at once because some
+ * pre-1394a devices would hang. */
+static void nodemgr_irm_write_bc(struct node_entry *ne, int generation)
+{
+       const u64 bc_addr = (CSR_REGISTER_BASE | CSR_BROADCAST_CHANNEL);
+       quadlet_t bc_remote, bc_local;
+       int error;
+
+       if (!ne->host->is_irm || ne->generation != generation ||
+           ne->nodeid == ne->host->node_id)
+               return;
+
+       bc_local = cpu_to_be32(ne->host->csr.broadcast_channel);
+
+       /* Check if the register is implemented and 1394a compliant. */
+       error = hpsb_read(ne->host, ne->nodeid, generation, bc_addr, &bc_remote,
+                         sizeof(bc_remote));
+       if (!error && bc_remote & cpu_to_be32(0x80000000) &&
+           bc_remote != bc_local)
+               hpsb_node_write(ne, bc_addr, &bc_local, sizeof(bc_local));
 }
 
 
@@ -1323,6 +1533,8 @@ static void nodemgr_probe_ne(struct host_info *hi, struct node_entry *ne, int ge
        if (!dev)
                return;
 
+       nodemgr_irm_write_bc(ne, generation);
+
        /* If "needs_probe", then this is either a new or changed node we
         * rescan totally. If the generation matches for an existing node
         * (one that existed prior to the bus reset) we send update calls
@@ -1342,25 +1554,37 @@ static void nodemgr_probe_ne(struct host_info *hi, struct node_entry *ne, int ge
 static void nodemgr_node_probe(struct host_info *hi, int generation)
 {
        struct hpsb_host *host = hi->host;
-       struct class *class = &nodemgr_ne_class;
        struct class_device *cdev;
+       struct node_entry *ne;
 
        /* Do some processing of the nodes we've probed. This pulls them
         * into the sysfs layer if needed, and can result in processing of
         * unit-directories, or just updating the node and it's
-        * unit-directories. */
-       down_read(&class->subsys.rwsem);
-       list_for_each_entry(cdev, &class->children, node)
-               nodemgr_probe_ne(hi, container_of(cdev, struct node_entry, class_dev), generation);
-        up_read(&class->subsys.rwsem);
+        * unit-directories.
+        *
+        * Run updates before probes. Usually, updates are time-critical
+        * while probes are time-consuming. (Well, those probes need some
+        * improvement...) */
+
+       down(&nodemgr_ne_class.sem);
+       list_for_each_entry(cdev, &nodemgr_ne_class.children, node) {
+               ne = container_of(cdev, struct node_entry, class_dev);
+               if (!ne->needs_probe)
+                       nodemgr_probe_ne(hi, ne, generation);
+       }
+       list_for_each_entry(cdev, &nodemgr_ne_class.children, node) {
+               ne = container_of(cdev, struct node_entry, class_dev);
+               if (ne->needs_probe)
+                       nodemgr_probe_ne(hi, ne, generation);
+       }
+       up(&nodemgr_ne_class.sem);
 
 
        /* If we had a bus reset while we were scanning the bus, it is
         * possible that we did not probe all nodes.  In that case, we
         * skip the clean up for now, since we could remove nodes that
-        * were still on the bus.  The bus reset increased hi->reset_sem,
-        * so there's a bus scan pending which will do the clean up
-        * eventually.
+        * were still on the bus.  Another bus scan is pending which will
+        * do the clean up eventually.
         *
         * Now let's tell the bus to rescan our devices. This may seem
         * like overhead, but the driver-model core will only scan a
@@ -1371,14 +1595,30 @@ static void nodemgr_node_probe(struct host_info *hi, int generation)
         * just removed.  */
 
        if (generation == get_hpsb_generation(host))
-               bus_rescan_devices(&ieee1394_bus_type);
+               if (bus_rescan_devices(&ieee1394_bus_type))
+                       HPSB_DEBUG("bus_rescan_devices had an error");
+}
 
-       return;
+static int nodemgr_send_resume_packet(struct hpsb_host *host)
+{
+       struct hpsb_packet *packet;
+       int error = -ENOMEM;
+
+       packet = hpsb_make_phypacket(host,
+                       EXTPHYPACKET_TYPE_RESUME |
+                       NODEID_TO_NODE(host->node_id) << PHYPACKET_PORT_SHIFT);
+       if (packet) {
+               packet->no_waiter = 1;
+               packet->generation = get_hpsb_generation(host);
+               error = hpsb_send_packet(packet);
+       }
+       if (error)
+               HPSB_WARN("fw-host%d: Failed to broadcast resume packet",
+                         host->id);
+       return error;
 }
 
-/* Because we are a 1394a-2000 compliant IRM, we need to inform all the other
- * nodes of the broadcast channel.  (Really we're only setting the validity
- * bit). Other IRM responsibilities go in here as well. */
+/* Perform a few high-level IRM responsibilities. */
 static int nodemgr_do_irm_duties(struct hpsb_host *host, int cycles)
 {
        quadlet_t bc;
@@ -1387,13 +1627,8 @@ static int nodemgr_do_irm_duties(struct hpsb_host *host, int cycles)
        if (!host->is_irm || host->irm_id == (nodeid_t)-1)
                return 1;
 
-       host->csr.broadcast_channel |= 0x40000000;  /* set validity bit */
-
-       bc = cpu_to_be32(host->csr.broadcast_channel);
-
-       hpsb_write(host, LOCAL_BUS | ALL_NODES, get_hpsb_generation(host),
-                  (CSR_REGISTER_BASE | CSR_BROADCAST_CHANNEL),
-                  &bc, sizeof(quadlet_t));
+       /* We are a 1394a-2000 compliant IRM. Set the validity bit. */
+       host->csr.broadcast_channel |= 0x40000000;
 
        /* If there is no bus manager then we should set the root node's
         * force_root bit to promote bus stability per the 1394
@@ -1401,9 +1636,13 @@ static int nodemgr_do_irm_duties(struct hpsb_host *host, int cycles)
        if (host->busmgr_id == 0xffff && host->node_count > 1)
        {
                u16 root_node = host->node_count - 1;
-               struct node_entry *ne = find_entry_by_nodeid(host, root_node | LOCAL_BUS);
 
-               if (ne && ne->busopt.cmc)
+               /* get cycle master capability flag from root node */
+               if (host->is_cycmst ||
+                   (!hpsb_read(host, LOCAL_BUS | root_node, get_hpsb_generation(host),
+                               (CSR_REGISTER_BASE + CSR_CONFIG_ROM + 2 * sizeof(quadlet_t)),
+                               &bc, sizeof(quadlet_t)) &&
+                    be32_to_cpu(bc) & 1 << CSR_CMC_SHIFT))
                        hpsb_send_phy_config(host, root_node, -1);
                else {
                        HPSB_DEBUG("The root node is not cycle master capable; "
@@ -1422,6 +1661,13 @@ static int nodemgr_do_irm_duties(struct hpsb_host *host, int cycles)
                }
        }
 
+       /* Some devices suspend their ports while being connected to an inactive
+        * host adapter, i.e. if connected before the low-level driver is
+        * loaded.  They become visible either when physically unplugged and
+        * replugged, or when receiving a resume packet.  Send one once. */
+       if (!host->resume_packet_sent && !nodemgr_send_resume_packet(host))
+               host->resume_packet_sent = 1;
+
        return 1;
 }
 
@@ -1433,7 +1679,7 @@ static int nodemgr_check_irm_capability(struct hpsb_host *host, int cycles)
        quadlet_t bc;
        int status;
 
-       if (host->is_irm)
+       if (hpsb_disable_irm || host->is_irm)
                return 1;
 
        status = hpsb_read(host, LOCAL_BUS | (host->irm_id),
@@ -1465,41 +1711,37 @@ static int nodemgr_host_thread(void *__hi)
 {
        struct host_info *hi = (struct host_info *)__hi;
        struct hpsb_host *host = hi->host;
-       int reset_cycles = 0;
-
-       /* No userlevel access needed */
-       daemonize(hi->daemon_name);
+       unsigned int g, generation = 0;
+       int i, reset_cycles = 0;
 
        /* Setup our device-model entries */
        nodemgr_create_host_dev_files(host);
 
-       /* Sit and wait for a signal to probe the nodes on the bus. This
-        * happens when we get a bus reset. */
-       while (1) {
-               unsigned int generation = 0;
-               int i;
+       for (;;) {
+               /* Sleep until next bus reset */
+               set_current_state(TASK_INTERRUPTIBLE);
+               if (get_hpsb_generation(host) == generation)
+                       schedule();
+               __set_current_state(TASK_RUNNING);
+
+               /* Thread may have been woken up to freeze or to exit */
+               if (try_to_freeze())
+                       continue;
+               if (kthread_should_stop())
+                       goto exit;
 
-               if (down_interruptible(&hi->reset_sem) ||
-                   down_interruptible(&nodemgr_serialize)) {
-                       if (current->flags & PF_FREEZE) {
-                               refrigerator(0);
+               if (mutex_lock_interruptible(&nodemgr_serialize)) {
+                       if (try_to_freeze())
                                continue;
-                       }
-                       printk("NodeMgr: received unexpected signal?!\n" );
-                       break;
+                       goto exit;
                }
 
-               if (hi->kill_me)
-                       break;
-
                /* Pause for 1/4 second in 1/16 second intervals,
                 * to make sure things settle down. */
+               g = get_hpsb_generation(host);
                for (i = 0; i < 4 ; i++) {
-                       set_current_state(TASK_INTERRUPTIBLE);
-                       if (schedule_timeout(HZ/16)) {
-                               up(&nodemgr_serialize);
-                               goto caught_signal;
-                       }
+                       if (msleep_interruptible(63) || kthread_should_stop())
+                               goto unlock_exit;
 
                        /* Now get the generation in which the node ID's we collect
                         * are valid.  During the bus scan we will use this generation
@@ -1510,32 +1752,23 @@ static int nodemgr_host_thread(void *__hi)
 
                        /* If we get a reset before we are done waiting, then
                         * start the the waiting over again */
-                       while (!down_trylock(&hi->reset_sem))
-                               i = 0;
-
-                       /* Check the kill_me again */
-                       if (hi->kill_me)
-                               goto caught_signal;
+                       if (generation != g)
+                               g = generation, i = 0;
                }
 
-               if (!nodemgr_check_irm_capability(host, reset_cycles)) {
+               if (!nodemgr_check_irm_capability(host, reset_cycles) ||
+                   !nodemgr_do_irm_duties(host, reset_cycles)) {
                        reset_cycles++;
-                       up(&nodemgr_serialize);
+                       mutex_unlock(&nodemgr_serialize);
                        continue;
                }
+               reset_cycles = 0;
 
                /* Scan our nodes to get the bus options and create node
                 * entries. This does not do the sysfs stuff, since that
-                * would trigger hotplug callbacks and such, which is a
-                * bad idea at this point. */
+                * would trigger uevents and such, which is a bad idea at
+                * this point. */
                nodemgr_node_scan(hi, generation);
-               if (!nodemgr_do_irm_duties(host, reset_cycles)) {
-                       reset_cycles++;
-                       up(&nodemgr_serialize);
-                       continue;
-               }
-
-               reset_cycles = 0;
 
                /* This actually does the full probe, with sysfs
                 * registration. */
@@ -1544,53 +1777,29 @@ static int nodemgr_host_thread(void *__hi)
                /* Update some of our sysfs symlinks */
                nodemgr_update_host_dev_links(host);
 
-               up(&nodemgr_serialize);
+               mutex_unlock(&nodemgr_serialize);
        }
-
-caught_signal:
+unlock_exit:
+       mutex_unlock(&nodemgr_serialize);
+exit:
        HPSB_VERBOSE("NodeMgr: Exiting thread");
-
-       complete_and_exit(&hi->exited, 0);
-}
-
-struct node_entry *hpsb_guid_get_entry(u64 guid)
-{
-        struct node_entry *ne;
-
-       down(&nodemgr_serialize);
-        ne = find_entry_by_guid(guid);
-       up(&nodemgr_serialize);
-
-        return ne;
-}
-
-struct node_entry *hpsb_nodeid_get_entry(struct hpsb_host *host, nodeid_t nodeid)
-{
-       struct node_entry *ne;
-
-       down(&nodemgr_serialize);
-       ne = find_entry_by_nodeid(host, nodeid);
-       up(&nodemgr_serialize);
-
-       return ne;
+       return 0;
 }
 
-
 int nodemgr_for_each_host(void *__data, int (*cb)(struct hpsb_host *, void *))
 {
-       struct class *class = &hpsb_host_class;
        struct class_device *cdev;
        struct hpsb_host *host;
        int error = 0;
 
-       down_read(&class->subsys.rwsem);
-       list_for_each_entry(cdev, &class->children, node) {
+       down(&hpsb_host_class.sem);
+       list_for_each_entry(cdev, &hpsb_host_class.children, node) {
                host = container_of(cdev, struct hpsb_host, class_dev);
 
                if ((error = cb(host, __data)))
                        break;
        }
-       up_read(&class->subsys.rwsem);
+       up(&hpsb_host_class.sem);
 
        return error;
 }
@@ -1612,20 +1821,10 @@ int nodemgr_for_each_host(void *__data, int (*cb)(struct hpsb_host *, void *))
 
 void hpsb_node_fill_packet(struct node_entry *ne, struct hpsb_packet *pkt)
 {
-        pkt->host = ne->host;
-        pkt->generation = ne->generation;
+       pkt->host = ne->host;
+       pkt->generation = ne->generation;
        barrier();
-        pkt->node_id = ne->nodeid;
-}
-
-int hpsb_node_read(struct node_entry *ne, u64 addr,
-                  quadlet_t *buffer, size_t length)
-{
-       unsigned int generation = ne->generation;
-
-       barrier();
-       return hpsb_read(ne->host, ne->nodeid, generation,
-                        addr, buffer, length);
+       pkt->node_id = ne->nodeid;
 }
 
 int hpsb_node_write(struct node_entry *ne, u64 addr,
@@ -1638,56 +1837,32 @@ int hpsb_node_write(struct node_entry *ne, u64 addr,
                          addr, buffer, length);
 }
 
-int hpsb_node_lock(struct node_entry *ne, u64 addr,
-                  int extcode, quadlet_t *data, quadlet_t arg)
-{
-       unsigned int generation = ne->generation;
-
-       barrier();
-       return hpsb_lock(ne->host, ne->nodeid, generation,
-                        addr, extcode, data, arg);
-}
-
 static void nodemgr_add_host(struct hpsb_host *host)
 {
        struct host_info *hi;
 
        hi = hpsb_create_hostinfo(&nodemgr_highlevel, host, sizeof(*hi));
-
        if (!hi) {
-               HPSB_ERR ("NodeMgr: out of memory in add host");
+               HPSB_ERR("NodeMgr: out of memory in add host");
                return;
        }
-
        hi->host = host;
-       init_completion(&hi->exited);
-        sema_init(&hi->reset_sem, 0);
-
-       sprintf(hi->daemon_name, "knodemgrd_%d", host->id);
-
-       hi->pid = kernel_thread(nodemgr_host_thread, hi, CLONE_KERNEL);
-
-       if (hi->pid < 0) {
-               HPSB_ERR ("NodeMgr: failed to start %s thread for %s",
-                         hi->daemon_name, host->driver->name);
+       hi->thread = kthread_run(nodemgr_host_thread, hi, "knodemgrd_%d",
+                                host->id);
+       if (IS_ERR(hi->thread)) {
+               HPSB_ERR("NodeMgr: cannot start thread for host %d", host->id);
                hpsb_destroy_hostinfo(&nodemgr_highlevel, host);
-               return;
        }
-
-       return;
 }
 
 static void nodemgr_host_reset(struct hpsb_host *host)
 {
        struct host_info *hi = hpsb_get_hostinfo(&nodemgr_highlevel, host);
 
-       if (hi != NULL) {
-               HPSB_VERBOSE("NodeMgr: Processing host reset for %s", hi->daemon_name);
-               up(&hi->reset_sem);
-       } else
-               HPSB_ERR ("NodeMgr: could not process reset of unused host");
-
-       return;
+       if (hi) {
+               HPSB_VERBOSE("NodeMgr: Processing reset for host %d", host->id);
+               wake_up_process(hi->thread);
+       }
 }
 
 static void nodemgr_remove_host(struct hpsb_host *host)
@@ -1695,18 +1870,9 @@ static void nodemgr_remove_host(struct hpsb_host *host)
        struct host_info *hi = hpsb_get_hostinfo(&nodemgr_highlevel, host);
 
        if (hi) {
-               if (hi->pid >= 0) {
-                       hi->kill_me = 1;
-                       mb();
-                       up(&hi->reset_sem);
-                       wait_for_completion(&hi->exited);
-                       nodemgr_remove_host_dev(&host->device);
-               }
-       } else
-               HPSB_ERR("NodeMgr: host %s does not exist, cannot remove",
-                        host->driver->name);
-
-       return;
+               kthread_stop(hi->thread);
+               nodemgr_remove_host_dev(&host->device);
+       }
 }
 
 static struct hpsb_highlevel nodemgr_highlevel = {
@@ -1718,27 +1884,35 @@ static struct hpsb_highlevel nodemgr_highlevel = {
 
 int init_ieee1394_nodemgr(void)
 {
-       int ret;
+       int error;
 
-       ret = class_register(&nodemgr_ne_class);
-       if (ret < 0)
-               return ret;
-
-       ret = class_register(&nodemgr_ud_class);
-       if (ret < 0) {
-               class_unregister(&nodemgr_ne_class);
-               return ret;
-       }
+       error = class_register(&nodemgr_ne_class);
+       if (error)
+               goto fail_ne;
+       error = class_register(&nodemgr_ud_class);
+       if (error)
+               goto fail_ud;
+       error = driver_register(&nodemgr_mid_layer_driver);
+       if (error)
+               goto fail_ml;
+       /* This driver is not used if nodemgr is off (disable_nodemgr=1). */
+       nodemgr_dev_template_host.driver = &nodemgr_mid_layer_driver;
 
        hpsb_register_highlevel(&nodemgr_highlevel);
-
        return 0;
+
+fail_ml:
+       class_unregister(&nodemgr_ud_class);
+fail_ud:
+       class_unregister(&nodemgr_ne_class);
+fail_ne:
+       return error;
 }
 
 void cleanup_ieee1394_nodemgr(void)
 {
-        hpsb_unregister_highlevel(&nodemgr_highlevel);
-
+       hpsb_unregister_highlevel(&nodemgr_highlevel);
+       driver_unregister(&nodemgr_mid_layer_driver);
        class_unregister(&nodemgr_ud_class);
        class_unregister(&nodemgr_ne_class);
 }