patch-2.4.8 linux/drivers/ieee1394/nodemgr.c

Next file: linux/drivers/ieee1394/nodemgr.h
Previous file: linux/drivers/ieee1394/ieee1394_syms.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.7/linux/drivers/ieee1394/nodemgr.c linux/drivers/ieee1394/nodemgr.c
@@ -39,18 +39,31 @@
  * XXX: Most of this isn't done yet :)  */
 
 
-static atomic_t outstanding_requests;
-
 static LIST_HEAD(node_list);
 rwlock_t node_lock = RW_LOCK_UNLOCKED;
 
 static LIST_HEAD(host_info_list);
 spinlock_t host_info_lock = SPIN_LOCK_UNLOCKED;
 
+struct bus_options {
+	u8	irmc;		/* Iso Resource Manager Capable */
+	u8	cmc;		/* Cycle Master Capable */
+	u8	isc;		/* Iso Capable */
+	u8	bmc;		/* Bus Master Capable */
+	u8	pmc;		/* Power Manager Capable (PNP spec) */
+	u8	cyc_clk_acc;	/* Cycle clock accuracy */
+	u8	generation;	/* Incremented when configrom changes */
+	u8	lnkspd;		/* Link speed */
+	u16	max_rec;	/* Maximum packet size node can receive */
+	atomic_t changed;	/* We set this to 1 if generation has changed */
+};
+
 struct host_info {
 	struct hpsb_host *host;
-	int pid;
-	wait_queue_head_t reset_wait;
+	pid_t pid;			/* PID of the nodemgr thread */
+	pid_t ppid;			/* Parent PID for the thread */
+	struct tq_struct task;		/* Used to kickstart the thread */
+	wait_queue_head_t reset_wait;	/* Wait queue awoken on bus reset */
 	struct list_head list;
 };
 
@@ -62,70 +75,126 @@
 
         struct hpsb_host *host;
         nodeid_t node_id;
-        
+
+	struct bus_options busopt;
+
         atomic_t generation;
 };
 
 static struct node_entry *create_node_entry(void)
 {
-        struct node_entry *ge;
+        struct node_entry *ne;
         unsigned long flags;
 
-        ge = kmalloc(sizeof(struct node_entry), SLAB_ATOMIC);
-        if (!ge) return NULL;
+        ne = kmalloc(sizeof(struct node_entry), SLAB_ATOMIC);
+        if (!ne) return NULL;
 
-        INIT_LIST_HEAD(&ge->list);
-        atomic_set(&ge->refcount, 0);
-        ge->guid = (u64) -1;
-        ge->host = NULL;
-        ge->node_id = 0;
-        atomic_set(&ge->generation, -1);
+        INIT_LIST_HEAD(&ne->list);
+        atomic_set(&ne->refcount, 0);
+        ne->guid = (u64) -1;
+        ne->host = NULL;
+        ne->node_id = 0;
+        atomic_set(&ne->generation, -1);
+	atomic_set(&ne->busopt.changed, 0);
 
         write_lock_irqsave(&node_lock, flags);
-        list_add_tail(&ge->list, &node_list);
+        list_add_tail(&ne->list, &node_list);
         write_unlock_irqrestore(&node_lock, flags);
 
-        return ge;
+        return ne;
 }
 
 static struct node_entry *find_entry(u64 guid)
 {
         struct list_head *lh;
-        struct node_entry *ge;
+        struct node_entry *ne;
         
         lh = node_list.next;
         while (lh != &node_list) {
-                ge = list_entry(lh, struct node_entry, list);
-                if (ge->guid == guid) return ge;
+                ne = list_entry(lh, struct node_entry, list);
+                if (ne->guid == guid) return ne;
                 lh = lh->next;
         }
 
         return NULL;
 }
 
-static void associate_guid(struct hpsb_host *host, nodeid_t nodeid, u64 guid)
+static int register_guid(struct hpsb_host *host, nodeid_t nodeid, u64 guid,
+			  quadlet_t busoptions)
 {
-        struct node_entry *ge;
-        unsigned long flags;
-
-        HPSB_DEBUG("Node %d on %s host: GUID %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
-                   nodeid & NODE_MASK, host->template->name, ((u8 *)&guid)[0],
-		   ((u8 *)&guid)[1], ((u8 *)&guid)[2], ((u8 *)&guid)[3],
-		   ((u8 *)&guid)[4], ((u8 *)&guid)[5], ((u8 *)&guid)[6],
-		   ((u8 *)&guid)[7]);
+        struct node_entry *ne;
+        unsigned long flags, new = 0;
 
         read_lock_irqsave(&node_lock, flags);
-        ge = find_entry(guid);
+        ne = find_entry(guid);
         read_unlock_irqrestore(&node_lock, flags);
 
-        if (!ge) ge = create_node_entry();
-        if (!ge) return;
+	/* New entry */
+	if (!ne) {
+		if ((ne = create_node_entry()) == NULL)
+			return -1;
+
+		HPSB_DEBUG("%s added: node %d, bus %d: GUID %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
+			   (host->node_id == nodeid) ? "Local host" : "Device",
+			   nodeid & NODE_MASK, (nodeid & BUS_MASK) >> 6, ((u8 *)&guid)[0],
+			   ((u8 *)&guid)[1], ((u8 *)&guid)[2], ((u8 *)&guid)[3],
+			   ((u8 *)&guid)[4], ((u8 *)&guid)[5], ((u8 *)&guid)[6],
+			   ((u8 *)&guid)[7]);
+
+		ne->guid = guid;
+		new = 1;
+	}
+
+	if (!new && ne->node_id != nodeid)
+		HPSB_DEBUG("Node %d changed to %d on bus %d",
+			   ne->node_id & NODE_MASK, nodeid & NODE_MASK, (nodeid & BUS_MASK) >> 6);
+
+	ne->host = host;
+        ne->node_id = nodeid;
+
+        atomic_set(&ne->generation, get_hpsb_generation());
+
+	/* Now set the bus options. Only do all this crap if this is a new
+	 * node, or if the generation number has changed.  */
+	if (new || ne->busopt.generation != ((busoptions >> 6) & 0x3)) {
+		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.generation	= (busoptions >> 6) & 0x3;
+		ne->busopt.lnkspd	= busoptions & 0x7;
+
+		new = 1; /* To make sure we probe the rest of the ConfigROM too */
+	}
 
-        ge->host = host;
-        ge->node_id = nodeid;
-        ge->guid = guid;
+#ifdef CONFIG_IEEE1394_VERBOSEDEBUG
+	HPSB_DEBUG("raw=0x%08x irmc=%d cmc=%d isc=%d bmc=%d pmc=%d cyc_clk_acc=%d "
+		   "max_rec=%d gen=%d lspd=%d\n", busoptions,
+		   ne->busopt.irmc, ne->busopt.cmc, ne->busopt.isc, ne->busopt.bmc,
+		   ne->busopt.pmc, ne->busopt.cyc_clk_acc, ne->busopt.max_rec,
+		   ne->busopt.generation, ne->busopt.lnkspd);
+#endif
+
+	return new;
+}
+
+static void nodemgr_remove_node(struct node_entry *ne)
+{
+	HPSB_DEBUG("Device removed: node %d, bus %d: GUID "
+		   "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
+		   ne->node_id & NODE_MASK, (ne->node_id & BUS_MASK) >> 6,
+		   ((u8 *)&ne->guid)[0], ((u8 *)&ne->guid)[1],
+		   ((u8 *)&ne->guid)[2], ((u8 *)&ne->guid)[3],
+		   ((u8 *)&ne->guid)[4], ((u8 *)&ne->guid)[5],
+		   ((u8 *)&ne->guid)[6], ((u8 *)&ne->guid)[7]);
 
-        atomic_set(&ge->generation, get_hpsb_generation());
+	list_del(&ne->list);
+	kfree(ne);
+
+	return;
 }
 
 /* This is where we probe the nodes for their information and provided
@@ -133,94 +202,150 @@
 static void nodemgr_node_probe(struct hpsb_host *host)
 {
         struct selfid *sid = (struct selfid *)host->topology_map;
+	struct list_head *lh;
+	struct node_entry *ne;
         int nodecount = host->node_count;
         nodeid_t nodeid = LOCAL_BUS;
 	quadlet_t buffer[5], quad;
 	octlet_t base = CSR_REGISTER_BASE + CSR_CONFIG_ROM;
-	int retval;
+	int flags;
 
 	/* We need to detect when the ConfigROM's generation has changed,
 	 * so we only update the node's info when it needs to be.  */
         for (; nodecount; nodecount--, nodeid++, sid++) {
-		int header_count = 0;
-		unsigned header_size = 0;
+		int retries = 3;
+		int header_count;
+		unsigned header_size;
+		octlet_t guid;
+
+		/* Skip extended, and non-active node's */
                 while (sid->extended)
 			sid++;
                 if (!sid->link_active)
 			continue;
-		if (nodeid == host->node_id)
+
+		/* Just use our local ROM */
+		if (nodeid == host->node_id) {
+			int i;
+			for (i = 0; i < 5; i++)
+				buffer[i] = be32_to_cpu(host->csr.rom[i]);
+			goto set_options;
+		}
+
+retry_configrom:
+		
+		if (!retries--) {
+			HPSB_ERR("Giving up on node %d for ConfigROM probe, too many errors",
+				 nodeid & NODE_MASK);
 			continue;
+		}
 
-		HPSB_DEBUG("Initiating ConfigROM request for node %d", nodeid & NODE_MASK);
+		header_count = 0;
+		header_size = 0;
 
-		retval = hpsb_read(host, nodeid, base, &quad, 4);
-		buffer[header_count++] = be32_to_cpu(quad);
+#ifdef CONFIG_IEEE1394_VERBOSEDEBUG
+		HPSB_INFO("Initiating ConfigROM request for node %d", nodeid & NODE_MASK);
+#endif
+
+		/* Now, P1212 says that devices should support 64byte block
+		 * reads, aligned on 64byte boundaries. That doesn't seem
+		 * to work though, and we are forced to doing quadlet
+		 * sized reads.  */
 
-		if (retval) {
-			HPSB_ERR("ConfigROM quadlet transaction error for %d",
+		if (hpsb_read(host, nodeid, base, &quad, 4)) {
+			HPSB_ERR("ConfigROM quadlet transaction error for node %d",
 				 nodeid & NODE_MASK);
-			continue;
+			goto retry_configrom;
 		}
+		buffer[header_count++] = be32_to_cpu(quad);
 
 		header_size = buffer[0] >> 24;
 
 		if (header_size < 4) {
-			HPSB_INFO("Node %d on %s host has non-standard ROM format (%d quads), "
-				  "cannot parse", nodeid & NODE_MASK, host->template->name,
+			HPSB_INFO("Node %d on bus %d has non-standard ROM format (%d quads), "
+				  "cannot parse", nodeid & NODE_MASK, (nodeid & BUS_MASK) >> 6,
 				  header_size);
 			continue;
 		}
 
 		while (header_count <= header_size && (header_count<<2) < sizeof(buffer)) {
-			retval = hpsb_read(host, nodeid, base + (header_count<<2), &quad, 4);
-			buffer[header_count++] = be32_to_cpu(quad);
-
-			if (retval) {
+			if (hpsb_read(host, nodeid, base + (header_count<<2), &quad, 4)) {
 				HPSB_ERR("ConfigROM quadlet transaction error for %d",
 					 nodeid & NODE_MASK);
-				goto failed_read;
+				goto retry_configrom;
 			}
-
+			buffer[header_count++] = be32_to_cpu(quad);
+		}
+set_options:
+		guid = be64_to_cpu(((u64)buffer[3] << 32) | buffer[4]);
+		switch (register_guid(host, nodeid, guid, buffer[2])) {
+			case -1:
+				HPSB_ERR("Failed to register node in ConfigROM probe");
+				continue;
+			case 1:
+				/* Need to probe the rest of the ConfigROM
+				 * here.  */
+				break;
+			default:
+				/* Nothing to do, this is an old unchanged
+				 * node.  */
+				break;
 		}
-
-		associate_guid(host, nodeid, be64_to_cpu(((u64)buffer[3] << 32) | buffer[4]));
-failed_read:
-		continue;
         }
 
-	/* Need to detect when nodes are no longer associated with
-	 * anything. I believe we can do this using the generation of the
-	 * entries after a reset, compared the the hosts generation.  */
+	/* Now check to see if we have any nodes that aren't referenced
+	 * any longer.  */
+        write_lock_irqsave(&node_lock, flags);
+	lh = node_list.next;
+	while (lh != &node_list) {
+		ne = list_entry(lh, struct node_entry, list);
+
+		/* Only checking this host */
+		if (ne->host != host)
+			continue;
+
+		/* If the generation didn't get updated, then either the
+		 * node was removed, or it failed the above probe. Either
+		 * way, we remove references to it, since they are
+		 * invalid.  */
+		if (atomic_read(&ne->generation) != get_hpsb_generation())
+			nodemgr_remove_node(ne);
+
+		lh = lh->next;
+	}
+	write_unlock_irqrestore(&node_lock, flags);
+
+	return;
 }
 
 
 struct node_entry *hpsb_guid_get_handle(u64 guid)
 {
         unsigned long flags;
-        struct node_entry *ge;
+        struct node_entry *ne;
 
         read_lock_irqsave(&node_lock, flags);
-        ge = find_entry(guid);
-        if (ge) atomic_inc(&ge->refcount);
+        ne = find_entry(guid);
+        if (ne) atomic_inc(&ne->refcount);
         read_unlock_irqrestore(&node_lock, flags);
 
-        return ge;
+        return ne;
 }
 
-struct hpsb_host *hpsb_get_host_by_ge(struct node_entry *ge)
+struct hpsb_host *hpsb_get_host_by_ne(struct node_entry *ne)
 {
-        if (atomic_read(&ge->generation) != get_hpsb_generation()) return NULL;
-        if (ge->node_id == ge->host->node_id) return ge->host;
+        if (atomic_read(&ne->generation) != get_hpsb_generation()) return NULL;
+        if (ne->node_id == ne->host->node_id) return ne->host;
         return NULL;
 }
 
-int hpsb_guid_fill_packet(struct node_entry *ge, struct hpsb_packet *pkt)
+int hpsb_guid_fill_packet(struct node_entry *ne, struct hpsb_packet *pkt)
 {
-        if (atomic_read(&ge->generation) != get_hpsb_generation()) return 0;
+        if (atomic_read(&ne->generation) != get_hpsb_generation()) return 0;
 
-        pkt->host = ge->host;
-        pkt->node_id = ge->node_id;
-        pkt->generation = atomic_read(&ge->generation);
+        pkt->host = ne->host;
+        pkt->node_id = ne->node_id;
+        pkt->generation = atomic_read(&ne->generation);
         return 1;
 }
 
@@ -229,33 +354,38 @@
 	struct host_info *hi = (struct host_info *)__hi;
 	struct hpsb_host *host = hi->host;
 
-	/* Standard thread setup */
+
 	lock_kernel();
-	daemonize();
-	strcpy(current->comm, "NodeMngr");
-	unlock_kernel();
 
-	for (;;) {
-		if (signal_pending(current))
-			break;
+	siginitsetinv(&current->blocked, sigmask(SIGKILL)|sigmask(SIGINT)|sigmask(SIGTERM));
 
-		/* Let's take a short pause to make sure all the devices
-		 * have time to settle.  */
+	strcpy(current->comm, "NodeMngr");
 
+	unlock_kernel();
+
+	do {
 		current->state = TASK_INTERRUPTIBLE;
 		schedule_timeout(HZ/50);
 
-		if (hi && host)
+		if (hi && host) {
 			nodemgr_node_probe(host);
-
-		/* Wait for the next bus reset */
-		if (hi && host)
 			interruptible_sleep_on(&hi->reset_wait);
-	}
+		} else
+			break;
+
+	} while (!signal_pending(current) && hi);
 
 	return(0);
 }
 
+static void nodemgr_schedule_thread (void *__hi)
+{
+	struct host_info *hi = (struct host_info *)__hi;
+
+	hi->ppid = current->pid;
+	hi->pid = kernel_thread(nodemgr_reset_handler, hi, 0);
+}
+
 static void nodemgr_add_host(struct hpsb_host *host)
 {
 	struct host_info *hi = kmalloc (sizeof (struct host_info), GFP_KERNEL);
@@ -266,10 +396,14 @@
 		return;
 	}
 
+	/* We simply initialize the struct here. We don't start the thread
+	 * until the first bus reset.  */
 	hi->host = host;
 	INIT_LIST_HEAD(&hi->list);
 	hi->pid = -1;
+	hi->ppid = -1;
 	init_waitqueue_head(&hi->reset_wait);
+	INIT_TQUEUE(&hi->task, nodemgr_schedule_thread, hi);
 
 	spin_lock_irqsave (&host_info_lock, flags);
 	list_add_tail (&hi->list, &host_info_list);
@@ -278,14 +412,6 @@
 	return;
 }
 
-static void nodemgr_schedule_thread (void *__hi)
-{
-	struct host_info *hi = (struct host_info *)__hi;
-
-	hi->pid = kernel_thread(nodemgr_reset_handler, hi,
-				CLONE_FS|CLONE_FILES|CLONE_SIGHAND);
-}
-
 static void nodemgr_host_reset(struct hpsb_host *host)
 {
 	struct list_head *lh;
@@ -311,18 +437,7 @@
 	if (hi->pid >= 0) {
 		wake_up(&hi->reset_wait);
 	} else {
-		if (in_interrupt()) {
-			static struct tq_struct task;
-			memset(&task, 0, sizeof(struct tq_struct));
-
-			task.routine = nodemgr_schedule_thread;
-			task.data = (void*)hi;
-
-			if (schedule_task(&task) < 0)
-				HPSB_ERR ("Failed to schedule Node Manager thread!\n");
-		} else {
-			nodemgr_schedule_thread(hi);
-		}
+		schedule_task(&hi->task);
 	}
 
 done_reset_host:
@@ -335,8 +450,24 @@
 {
 	struct list_head *lh;
 	struct host_info *hi = NULL;
+	struct node_entry *ne;
 	int flags;
 
+	/* First remove all node entries for this host */
+	write_lock_irqsave(&node_lock, flags);
+	lh = node_list.next;
+	while (lh != &node_list) {
+		ne = list_entry(lh, struct node_entry, list);
+
+		/* Only checking this host */
+		if (ne->host != host)
+			continue;
+
+		nodemgr_remove_node(ne);
+		lh = lh->next;
+	}
+	write_unlock_irqrestore(&node_lock, flags);
+
 	spin_lock_irqsave (&host_info_lock, flags);
 	lh = host_info_list.next;
 	while (lh != &host_info_list) {
@@ -349,15 +480,24 @@
 	}
 
 	if (hi == NULL) {
-		HPSB_ERR ("Could not remove non-exitent host in Node Manager");
+		HPSB_ERR ("Could not remove non-existent host in Node Manager");
 		goto done_remove_host;
 	}
 
-	if (hi->pid >= 0)
-		kill_proc(hi->pid, SIGINT, 1);
+	mb();
 
-	current->state = TASK_INTERRUPTIBLE;
-	schedule_timeout(HZ*2);   /* 2 second delay */
+	if (hi->pid >= 0) {
+		/* Kill the proc */
+		kill_proc(hi->pid, SIGKILL, 1);
+
+		/* XXX: We need a better way... */
+		current->state = TASK_INTERRUPTIBLE;
+		schedule_timeout(HZ/20);
+
+		/* Now tell the parent to sluff off the zombied body */
+		mb();
+		kill_proc(hi->ppid, SIGCHLD, 1);
+	}
 
 	kfree (hi);
 
@@ -377,8 +517,6 @@
 
 void init_ieee1394_nodemgr(void)
 {
-        atomic_set(&outstanding_requests, 0);
-
         hl = hpsb_register_highlevel("Node manager", &guid_ops);
         if (!hl) {
                 HPSB_ERR("Out of memory during ieee1394 initialization");

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)