Merge to Fedora kernel-2.6.18-1.2224_FC5 patched with stable patch-2.6.18.1-vs2.0...
[linux-2.6.git] / drivers / ieee1394 / nodemgr.c
index 082c7fd..8b12e66 100644 (file)
@@ -8,8 +8,8 @@
  * 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>
@@ -38,6 +38,7 @@ struct nodemgr_csr_info {
        struct hpsb_host *host;
        nodeid_t nodeid;
        unsigned int generation;
+       unsigned int speed_unverified:1;
 };
 
 
@@ -57,23 +58,75 @@ 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 ret;
+
+       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;
+               ret = hpsb_read(ci->host, ci->nodeid, ci->generation, addr,
+                               &q, sizeof(quadlet_t));
+               if (ret)
+                       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 ret;
+}
 
 static int nodemgr_bus_read(struct csr1212_csr *csr, u64 addr, u16 length,
                             void *buffer, void *__ci)
 {
        struct nodemgr_csr_info *ci = (struct nodemgr_csr_info*)__ci;
-       int i, ret = 0;
+       int i, ret;
 
        for (i = 1; ; i++) {
                ret = hpsb_read(ci->host, ci->nodeid, ci->generation, addr,
                                buffer, length);
-               if (!ret || i == 3)
+               if (!ret) {
+                       ci->speed_unverified = 0;
+                       break;
+               }
+               /* Give up after 3rd failure. */
+               if (i == 3)
                        break;
 
+               /* 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)) {
+                       ret = nodemgr_check_speed(ci, addr, buffer);
+                       if (!ret)
+                               break;
+               }
                if (msleep_interruptible(334))
                        return -EINTR;
        }
-
        return ret;
 }
 
@@ -281,10 +334,12 @@ static ssize_t fw_show_ne_bus_options(struct device *dev, struct device_attribut
 static DEVICE_ATTR(bus_options,S_IRUGO,fw_show_ne_bus_options,NULL);
 
 
+/* tlabels_free, tlabels_allocations, tlabels_mask are read non-atomically
+ * here, therefore displayed values may be occasionally wrong. */
 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);
+       return sprintf(buf, "%d\n", 64 - bitmap_weight(ne->tpool->pool, 64));
 }
 static DEVICE_ATTR(tlabels_free,S_IRUGO,fw_show_ne_tlabels_free,NULL);
 
@@ -1204,6 +1259,8 @@ static void nodemgr_node_scan_one(struct host_info *hi,
        ci->host = host;
        ci->nodeid = nodeid;
        ci->generation = generation;
+       ci->speed_unverified =
+               host->speed[NODEID_TO_NODE(nodeid)] > 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.  */
@@ -1269,6 +1326,7 @@ static void nodemgr_node_scan(struct host_info *hi, int generation)
 }
 
 
+/* Caller needs to hold nodemgr_ud_class.subsys.rwsem as reader. */
 static void nodemgr_suspend_ne(struct node_entry *ne)
 {
        struct class_device *cdev;
@@ -1321,15 +1379,14 @@ static void nodemgr_resume_ne(struct node_entry *ne)
 }
 
 
+/* Caller needs to hold nodemgr_ud_class.subsys.rwsem as reader. */
 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) {
+       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)
                        continue;
@@ -1342,7 +1399,6 @@ static void nodemgr_update_pdrv(struct node_entry *ne)
                        up_write(&ud->device.bus->subsys.rwsem);
                }
        }
-       up_read(&class->subsys.rwsem);
 }
 
 
@@ -1373,6 +1429,8 @@ static void nodemgr_irm_write_bc(struct node_entry *ne, int generation)
 }
 
 
+/* Caller needs to hold nodemgr_ud_class.subsys.rwsem as reader because the
+ * calls to nodemgr_update_pdrv() and nodemgr_suspend_ne() here require it. */
 static void nodemgr_probe_ne(struct host_info *hi, struct node_entry *ne, int generation)
 {
        struct device *dev;