📄 nodemgr.c
字号:
/* * When the config rom changes we disconnect all drivers and * free the cached unit directories and reread the whole * thing. If this was a new device, the call to * nodemgr_disconnect_drivers is a no-op and all is well. */ nodemgr_free_unit_directories(ne); nodemgr_process_root_directory(ne); nodemgr_bind_drivers(ne);}/* * This function updates nodes that were present on the bus before the * reset and still are after the reset. The nodeid and the config rom * may have changed, and the drivers managing this device must be * informed that this device just went through a bus reset, to allow * the to take whatever actions required. */static void nodemgr_update_node(struct node_entry *ne, quadlet_t busoptions, struct hpsb_host *host, nodeid_t nodeid, unsigned int generation){ struct list_head *lh; struct unit_directory *ud; if (ne->nodeid != nodeid) { HPSB_DEBUG("Node " NODE_BUS_FMT " changed to " NODE_BUS_FMT, NODE_BUS_ARGS(ne->nodeid), NODE_BUS_ARGS(nodeid)); ne->nodeid = nodeid; } if (ne->busopt.generation != ((busoptions >> 4) & 0xf)) nodemgr_process_config_rom (ne, busoptions); /* Since that's done, we can declare this record current */ ne->generation = generation; list_for_each (lh, &ne->unit_directories) { ud = list_entry (lh, struct unit_directory, node_list); if (ud->driver != NULL && ud->driver->update != NULL) ud->driver->update(ud); }}static int read_businfo_block(struct hpsb_host *host, nodeid_t nodeid, unsigned int generation, quadlet_t *buffer, int buffer_length){ octlet_t addr = CSR_REGISTER_BASE + CSR_CONFIG_ROM; unsigned header_size; int i; /* IEEE 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. */#ifdef CONFIG_IEEE1394_VERBOSEDEBUG HPSB_INFO("Initiating ConfigROM request for node " NODE_BUS_FMT, NODE_BUS_ARGS(nodeid));#endif /* * Must retry a few times if config rom read returns zero (how long?). Will * not normally occur, but we should do the right thing. For example, with * some sbp2 devices, the bridge chipset cannot return valid config rom reads * immediately after power-on, since they need to detect the type of * device attached (disk or CD-ROM). */ for (i = 0; i < 4; i++) { if (nodemgr_read_quadlet(host, nodeid, generation, addr, &buffer[0]) < 0) { HPSB_ERR("ConfigROM quadlet transaction error for node " NODE_BUS_FMT, NODE_BUS_ARGS(nodeid)); return -1; } if (buffer[0]) break; set_current_state(TASK_INTERRUPTIBLE); if (schedule_timeout (HZ/4)) return -1; } header_size = buffer[0] >> 24; addr += 4; if (header_size < 4) { HPSB_INFO("Node " NODE_BUS_FMT " has non-standard ROM " "format (%d quads), cannot parse", NODE_BUS_ARGS(nodeid), header_size); return -1; } for (i = 1; i < buffer_length; i++, addr += 4) { if (nodemgr_read_quadlet(host, nodeid, generation, addr, &buffer[i]) < 0) { HPSB_ERR("ConfigROM quadlet transaction " "error for node " NODE_BUS_FMT, NODE_BUS_ARGS(nodeid)); return -1; } } return 0;} static void nodemgr_remove_node(struct node_entry *ne){ HPSB_DEBUG("%s removed: Node[" NODE_BUS_FMT "] GUID[%016Lx] [%s]", (ne->host->node_id == ne->nodeid) ? "Host" : "Device", NODE_BUS_ARGS(ne->nodeid), (unsigned long long)ne->guid, ne->vendor_name ?: "Unknown"); nodemgr_free_unit_directories(ne); list_del(&ne->list); kfree(ne); return;}/* This is where we probe the nodes for their information and provided * features. */static void nodemgr_node_probe_one(struct hpsb_host *host, nodeid_t nodeid, int generation){ struct node_entry *ne; quadlet_t buffer[5]; octlet_t guid; /* We need to detect when the ConfigROM's generation has changed, * so we only update the node's info when it needs to be. */ if (read_businfo_block (host, nodeid, generation, buffer, sizeof(buffer) >> 2)) return; if (buffer[1] != IEEE1394_BUSID_MAGIC) { /* This isn't a 1394 device, but we let it slide. There * was a report of a device with broken firmware which * reported '2394' instead of '1394', which is obviously a * mistake. One would hope that a non-1394 device never * gets connected to Firewire bus. If someone does, we * shouldn't be held responsible, so we'll allow it with a * warning. */ HPSB_WARN("Node " NODE_BUS_FMT " has invalid busID magic [0x%08x]", NODE_BUS_ARGS(nodeid), buffer[1]); } guid = ((u64)buffer[3] << 32) | buffer[4]; ne = find_entry_by_guid(guid); if (!ne) nodemgr_create_node(guid, buffer[2], host, nodeid, generation); else nodemgr_update_node(ne, buffer[2], host, nodeid, generation); return;}static void nodemgr_node_probe_cleanup(struct hpsb_host *host, unsigned int generation){ struct list_head *lh, *next; struct node_entry *ne; /* Now check to see if we have any nodes that aren't referenced * any longer. */ list_for_each_safe(lh, next, &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 (ne->generation != generation) nodemgr_remove_node(ne); } return;}static void nodemgr_node_probe(struct hpsb_host *host){ int count; struct selfid *sid = (struct selfid *)host->topology_map; nodeid_t nodeid = LOCAL_BUS; unsigned int generation; /* Pause for 1/4 second, to make sure things settle down. If * schedule_timeout returns non-zero, it means we caught a signal * and need to return. */ set_current_state(TASK_INTERRUPTIBLE); if (schedule_timeout (HZ/4)) return; /* Now get the generation in which the node ID's we collect * are valid. During the bus scan we will use this generation * for the read transactions, so that if another reset occurs * during the scan the transactions will fail instead of * returning bogus data. */ generation = get_hpsb_generation(host); /* 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_probe_one(host, nodeid++, generation); } /* 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. */ if (generation == get_hpsb_generation(host)) nodemgr_node_probe_cleanup(host, generation); return;}static int nodemgr_host_thread(void *__hi){ struct host_info *hi = (struct host_info *)__hi; /* No userlevel access needed */ daemonize(); strcpy(current->comm, "knodemgrd"); /* Sit and wait for a signal to probe the nodes on the bus. This * happens when we get a bus reset. */ while (!down_interruptible(&hi->reset_sem) && !down_interruptible(&nodemgr_serialize)) { nodemgr_node_probe(hi->host); up(&nodemgr_serialize); }#ifdef CONFIG_IEEE1394_VERBOSEDEBUG HPSB_DEBUG ("NodeMgr: Exiting thread for %s", hi->host->driver->name);#endif 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(nodeid_t nodeid){ struct node_entry *ne; down(&nodemgr_serialize); ne = find_entry_by_nodeid(nodeid); up(&nodemgr_serialize); return ne;}/* The following four convenience functions use a struct node_entry * for addressing a node on the bus. They are intended for use by any * process context, not just the nodemgr thread, so we need to be a * little careful when reading out the node ID and generation. The * thing that can go wrong is that we get the node ID, then a bus * reset occurs, and then we read the generation. The node ID is * possibly invalid, but the generation is current, and we end up * sending a packet to a the wrong node. * * The solution is to make sure we read the generation first, so that * if a reset occurs in the process, we end up with a stale generation * and the transactions will fail instead of silently using wrong node * ID's. */void hpsb_node_fill_packet(struct node_entry *ne, struct hpsb_packet *pkt){ 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);}int hpsb_node_write(struct node_entry *ne, u64 addr, quadlet_t *buffer, size_t length){ unsigned int generation = ne->generation; barrier(); return hpsb_write(ne->host, ne->nodeid, generation, 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 = kmalloc (sizeof (struct host_info), GFP_KERNEL); unsigned long flags; if (!hi) { HPSB_ERR ("NodeMgr: out of memory in add host"); return; } /* Initialize the hostinfo here and start the thread. The * thread blocks on the reset semaphore until a bus reset * happens. */ hi->host = host; INIT_LIST_HEAD(&hi->list); init_completion(&hi->exited); sema_init(&hi->reset_sem, 0); hi->pid = kernel_thread(nodemgr_host_thread, hi, CLONE_FS | CLONE_FILES | CLONE_SIGHAND); if (hi->pid < 0) { HPSB_ERR ("NodeMgr: failed to start NodeMgr thread for %s", host->driver->name); kfree(hi); return; } spin_lock_irqsave (&host_info_lock, flags); list_add_tail (&hi->list, &host_info_list); spin_unlock_irqrestore (&host_info_lock, flags); return;}static void nodemgr_host_reset(struct hpsb_host *host){ struct list_head *lh; struct host_info *hi = NULL; unsigned long flags; spin_lock_irqsave (&host_info_lock, flags); list_for_each(lh, &host_info_list) { struct host_info *myhi = list_entry(lh, struct host_info, list); if (myhi->host == host) { hi = myhi; break; } } if (hi != NULL) {#ifdef CONFIG_IEEE1394_VERBOSEDEBUG HPSB_DEBUG ("NodeMgr: Processing host reset for %s", host->driver->name);#endif up(&hi->reset_sem); } else HPSB_ERR ("NodeMgr: could not process reset of non-existent host"); spin_unlock_irqrestore (&host_info_lock, flags); return;}static void nodemgr_remove_host(struct hpsb_host *host){ struct list_head *lh, *next; struct node_entry *ne; unsigned long flags; struct host_info *hi = NULL; spin_lock_irqsave (&host_info_lock, flags); list_for_each_safe(lh, next, &host_info_list) { struct host_info *myhi = list_entry(lh, struct host_info, list); if (myhi->host == host) { list_del(&myhi->list); hi = myhi; break; } } spin_unlock_irqrestore (&host_info_lock, flags); if (hi) { if (hi->pid >= 0) { kill_proc(hi->pid, SIGTERM, 1); wait_for_completion(&hi->exited); } kfree(hi); } else HPSB_ERR("NodeMgr: host %s does not exist, cannot remove", host->driver->name); down(&nodemgr_serialize); /* Even if we fail the host_info part, remove all the node * entries. */ list_for_each_safe(lh, next, &node_list) { ne = list_entry(lh, struct node_entry, list); if (ne->host == host) nodemgr_remove_node(ne); } up(&nodemgr_serialize); return;}static struct hpsb_highlevel_ops nodemgr_ops = { .add_host = nodemgr_add_host, .host_reset = nodemgr_host_reset, .remove_host = nodemgr_remove_host,};static struct hpsb_highlevel *hl;#define PROC_ENTRY "devices"void init_ieee1394_nodemgr(int disable_hotplug){ nodemgr_disable_hotplug = disable_hotplug;#ifdef CONFIG_PROC_FS if (!create_proc_read_entry(PROC_ENTRY, 0444, ieee1394_procfs_entry, raw1394_read_proc, NULL)) HPSB_ERR("Can't create devices procfs entry");#endif hl = hpsb_register_highlevel("Node manager", &nodemgr_ops); if (!hl) { HPSB_ERR("NodeMgr: out of memory during ieee1394 initialization"); }}void cleanup_ieee1394_nodemgr(void){ hpsb_unregister_highlevel(hl);#ifdef CONFIG_PROC_FS remove_proc_entry(PROC_ENTRY, ieee1394_procfs_entry);#endif}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -