diff options
| author | Ben Collins <bcollins@debian.org> | 2003-11-25 20:15:46 -0800 |
|---|---|---|
| committer | Linus Torvalds <torvalds@home.osdl.org> | 2003-11-25 20:15:46 -0800 |
| commit | 9b67c27bdb9c0bf935fd45dd6601a126193bd2ec (patch) | |
| tree | e009f70f4fb9d9b7679e521b05759d32ede67457 | |
| parent | 66e7a091c556a42c6f252ed3deb4f53393a298ae (diff) | |
[PATCH] Lastminute IEEE-1394 fixes
I've got a lot more changes than what's included here. I've put this
down to the bear minimum to get things working sanely.
Mainly, I just want to get all the people hit by this a chance to use
2.6.0 without having to get our tree. Changes itemized:
- Fix deadlock possibility in csr.c:read_maps()
- Fix kmalloc to use ATOMIC in highlevel.c.
- s/in_interrupt/irqs_disabled/ in ieee1394_transactions.c to fix
warnings when transactions occured.
- Introduce a release callback for the host driver and use it correctly.
- Reorganize the nodemgr probe so we do an initial scan to discover
devices, check IRM/CycleMaster, then do a final full probe when things
are kosher. Fixes a problem where device registration and hotplug
would cause some serious problems when a bus reset was forced in the
middle of the probe.
| -rw-r--r-- | drivers/ieee1394/csr.c | 1 | ||||
| -rw-r--r-- | drivers/ieee1394/highlevel.c | 2 | ||||
| -rw-r--r-- | drivers/ieee1394/hosts.c | 2 | ||||
| -rw-r--r-- | drivers/ieee1394/ieee1394_transactions.c | 2 | ||||
| -rw-r--r-- | drivers/ieee1394/ieee1394_types.h | 3 | ||||
| -rw-r--r-- | drivers/ieee1394/nodemgr.c | 217 | ||||
| -rw-r--r-- | drivers/ieee1394/nodemgr.h | 1 |
7 files changed, 155 insertions, 73 deletions
diff --git a/drivers/ieee1394/csr.c b/drivers/ieee1394/csr.c index e69e6c6850f0..0dd7c389a130 100644 --- a/drivers/ieee1394/csr.c +++ b/drivers/ieee1394/csr.c @@ -202,6 +202,7 @@ static int read_maps(struct hpsb_host *host, int nodeid, quadlet_t *buffer, if (csraddr < CSR_TOPOLOGY_MAP) { if (csraddr + length > CSR_CONFIG_ROM + host->csr.rom_size) { + spin_unlock_irqrestore(&host->csr.lock, flags); return RCODE_ADDRESS_ERROR; } src = ((char *)host->csr.rom) + csraddr - CSR_CONFIG_ROM; diff --git a/drivers/ieee1394/highlevel.c b/drivers/ieee1394/highlevel.c index b61c16cbf833..c74c1320d564 100644 --- a/drivers/ieee1394/highlevel.c +++ b/drivers/ieee1394/highlevel.c @@ -98,7 +98,7 @@ void *hpsb_create_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host, return NULL; } - hi = kmalloc(sizeof(*hi) + data_size, GFP_KERNEL); + hi = kmalloc(sizeof(*hi) + data_size, GFP_ATOMIC); if (!hi) return NULL; diff --git a/drivers/ieee1394/hosts.c b/drivers/ieee1394/hosts.c index eb5ef09b5265..3faa21503cd4 100644 --- a/drivers/ieee1394/hosts.c +++ b/drivers/ieee1394/hosts.c @@ -92,7 +92,7 @@ void hpsb_unref_host(struct hpsb_host *host) down(&hpsb_hosts_lock); if (atomic_dec_and_test(&host->refcount) && host->is_shutdown) - kfree(host); + device_unregister(&host->device); up(&hpsb_hosts_lock); } diff --git a/drivers/ieee1394/ieee1394_transactions.c b/drivers/ieee1394/ieee1394_transactions.c index 6303261be6bd..6cdc8c679b3b 100644 --- a/drivers/ieee1394/ieee1394_transactions.c +++ b/drivers/ieee1394/ieee1394_transactions.c @@ -137,7 +137,7 @@ int hpsb_get_tlabel(struct hpsb_packet *packet) tp = &packet->host->tpool[packet->node_id & NODE_MASK]; - if (in_interrupt() || in_atomic()) { + if (irqs_disabled() || in_atomic()) { if (down_trylock(&tp->count)) return 1; } else { diff --git a/drivers/ieee1394/ieee1394_types.h b/drivers/ieee1394/ieee1394_types.h index d59d6f8d4401..552667142ce1 100644 --- a/drivers/ieee1394/ieee1394_types.h +++ b/drivers/ieee1394/ieee1394_types.h @@ -13,9 +13,6 @@ #include <asm/byteorder.h> -/* The great kdev_t changeover in 2.5.x */ -#include <linux/kdev_t.h> - /* Transaction Label handling */ struct hpsb_tlabel_pool { DECLARE_BITMAP(pool, 64); diff --git a/drivers/ieee1394/nodemgr.c b/drivers/ieee1394/nodemgr.c index b38f1e83d865..708450f02d9d 100644 --- a/drivers/ieee1394/nodemgr.c +++ b/drivers/ieee1394/nodemgr.c @@ -420,6 +420,12 @@ static void nodemgr_release_ne(struct device *dev) } +static void nodemgr_release_host(struct device *dev) +{ + kfree(container_of(dev, struct hpsb_host, device)); +} + + static void nodemgr_remove_ud(struct unit_directory *ud) { struct device *dev = &ud->device; @@ -513,6 +519,7 @@ static struct device nodemgr_dev_template_ne = { static struct device nodemgr_dev_template_host = { .bus = &ieee1394_bus_type, + .release = nodemgr_release_host, .driver = &nodemgr_driver_host, .driver_data = &nodemgr_driverdata_host, }; @@ -676,8 +683,27 @@ static struct node_entry *nodemgr_scan_root_directory } -static void nodemgr_process_config_rom(struct host_info *hi, - struct node_entry *ne, quadlet_t busoptions); +static void nodemgr_update_bus_options(struct node_entry *ne, + quadlet_t busoptions) +{ + 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 >> 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 gen=%d lspd=%d", + 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); +} + static struct node_entry *nodemgr_create_node(octlet_t guid, quadlet_t busoptions, struct host_info *hi, nodeid_t nodeid, @@ -694,6 +720,7 @@ static struct node_entry *nodemgr_create_node(octlet_t guid, quadlet_t busoption ne->host = host; ne->nodeid = nodeid; ne->generation = generation; + ne->needs_probe = 1; ne->guid = guid; ne->guid_vendor_id = (guid >> 40) & 0xffffff; @@ -711,7 +738,7 @@ static struct node_entry *nodemgr_create_node(octlet_t guid, quadlet_t busoption device_create_file(&ne->device, &dev_attr_ne_guid_vendor_oui); nodemgr_create_ne_dev_files(ne); - nodemgr_process_config_rom (hi, ne, busoptions); + nodemgr_update_bus_options(ne, busoptions); HPSB_DEBUG("%s added: ID:BUS[" NODE_BUS_FMT "] GUID[%016Lx]", (host->node_id == nodeid) ? "Host" : "Node", @@ -806,6 +833,7 @@ static struct unit_directory *nodemgr_scan_unit_directory if (nodemgr_read_quadlet(ne->host, ne->nodeid, ne->generation, address, &quad)) return NULL; + length = CONFIG_ROM_DIRECTORY_LENGTH(quad) ; address += 4; @@ -1055,7 +1083,7 @@ static struct unit_directory *nodemgr_process_unit_directory return ud; -unit_directory_error: +unit_directory_error: if (ud != NULL) kfree(ud); return NULL; @@ -1069,6 +1097,8 @@ static void nodemgr_process_root_directory(struct host_info *hi, struct node_ent int length; unsigned int ud_id = 0; + device_remove_file(&ne->device, &dev_attr_ne_vendor_oui); + address = CSR_REGISTER_BASE + CSR_CONFIG_ROM; if (nodemgr_read_quadlet(ne->host, ne->nodeid, ne->generation, @@ -1129,6 +1159,9 @@ static void nodemgr_process_root_directory(struct host_info *hi, struct node_ent break; } } + + if (ne->vendor_oui) + device_create_file(&ne->device, &dev_attr_ne_vendor_oui); } #ifdef CONFIG_HOTPLUG @@ -1221,34 +1254,6 @@ void hpsb_unregister_protocol(struct hpsb_protocol_driver *driver) driver_unregister(&driver->driver); } -static void nodemgr_process_config_rom(struct host_info *hi, - struct node_entry *ne, quadlet_t busoptions) -{ - 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 >> 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 gen=%d lspd=%d", - 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); - - device_remove_file(&ne->device, &dev_attr_ne_vendor_oui); - - nodemgr_process_root_directory(hi, ne); - - if (ne->vendor_oui) - device_create_file(&ne->device, &dev_attr_ne_vendor_oui); -} - /* Searches the list of ud's that match a ne as the parent. If the ud has * a driver associated with it, we call that driver's update function @@ -1303,18 +1308,14 @@ static void nodemgr_update_node(struct node_entry *ne, quadlet_t busoptions, * unregister all the unit directories. */ nodemgr_remove_node_uds(ne); - /* With all the ud's gone, mark the generation current, - * this way the probe will succeed. */ - ne->generation = generation; + nodemgr_update_bus_options(ne, busoptions); - /* This will re-register our unitdir's */ - nodemgr_process_config_rom (hi, ne, busoptions); - } else - ne->generation = generation; + /* Mark the node as new, so it gets re-probed */ + ne->needs_probe = 1; + } - /* Update unit_dirs with attached drivers */ - bus_for_each_dev(&ieee1394_bus_type, NULL, ne, - nodemgr_driver_search_cb); + /* Mark the node current */ + ne->generation = generation; } static int read_businfo_block(struct hpsb_host *host, nodeid_t nodeid, unsigned int generation, @@ -1385,10 +1386,8 @@ static int read_businfo_block(struct hpsb_host *host, nodeid_t nodeid, unsigned } -/* This is where we probe the nodes for their information and provided - * features. */ -static void nodemgr_node_probe_one(struct host_info *hi, - nodeid_t nodeid, int generation) +static void nodemgr_node_scan_one(struct host_info *hi, + nodeid_t nodeid, int generation) { struct hpsb_host *host = hi->host; struct node_entry *ne; @@ -1411,7 +1410,7 @@ static void nodemgr_node_probe_one(struct host_info *hi, * 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(host, nodeid), buffer[1]); + NODE_BUS_ARGS(host, nodeid), buffer[1]); } guid = ((u64)buffer[3] << 32) | buffer[4]; @@ -1453,24 +1452,84 @@ static int nodemgr_remove_node(struct device *dev, void *__data) return 0; } +struct ne_cb_data_struct { + struct host_info *hi; + struct node_entry *ne; +}; -static void nodemgr_node_probe(struct host_info *hi, int generation) +static int nodemgr_probe_ne_cb(struct device *dev, void *__data) { - int count; - struct hpsb_host *host = hi->host; - struct selfid *sid = (struct selfid *)host->topology_map; - nodeid_t nodeid = LOCAL_BUS; + struct ne_cb_data_struct *ne_cb_data = __data; + struct host_info *hi = ne_cb_data->hi; + struct node_entry *ne; - /* Scan each node on the bus */ - for (count = host->selfid_count; count; count--, sid++) { - if (sid->extended) - continue; + if (dev->driver_data != &nodemgr_driverdata_ne) + return 0; - if (!sid->link_active) { - nodeid++; - continue; + ne = ne_cb_data->ne = container_of(dev, struct node_entry, device); + + if (ne->host != hi->host) + return 0; + + /* We can't call nodemgr_process_root_directory() here because + * that can call device_register. Since this callback is under a + * rwsem, the device_register would deadlock. So, we signal back + * to the callback, and process things there. */ + + if (ne->needs_probe) { + ne->needs_probe = 0; + return 1; + } else { + /* Update unit_dirs with attached drivers */ + bus_for_each_dev(&ieee1394_bus_type, NULL, ne, + nodemgr_driver_search_cb); + } + return 0; +} + + +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; + + /* 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); + } +} + +static void nodemgr_node_probe(struct host_info *hi, int generation) +{ + struct hpsb_host *host = hi->host; + struct ne_cb_data_struct ne_cb_data; + + ne_cb_data.hi = hi; + ne_cb_data.ne = NULL; + + /* 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. */ + while (bus_for_each_dev(&ieee1394_bus_type, ne_cb_data.ne ? &ne_cb_data.ne->device : NULL, + &ne_cb_data, nodemgr_probe_ne_cb)) { + /* If we get in here, we've got a node that needs it's + * unit directories processed. */ + struct device *dev = get_device(&ne_cb_data.ne->device); + + if (dev) { + nodemgr_process_root_directory(hi, ne_cb_data.ne); + put_device(dev); } - nodemgr_node_probe_one(hi, nodeid++, generation); } /* If we had a bus reset while we were scanning the bus, it is @@ -1513,12 +1572,12 @@ static void nodemgr_node_probe(struct host_info *hi, int generation) /* 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. */ -static void nodemgr_do_irm_duties(struct hpsb_host *host) +static int nodemgr_do_irm_duties(struct hpsb_host *host, int cycles) { quadlet_t bc; if (!host->is_irm) - return; + return 1; host->csr.broadcast_channel |= 0x40000000; /* set validity bit */ @@ -1541,10 +1600,21 @@ static void nodemgr_do_irm_duties(struct hpsb_host *host) else { HPSB_DEBUG("The root node is not cycle master capable; " "selecting a new root node and resetting..."); + + if (cycles >= 5) { + /* Oh screw it! Just leave the bus as it is */ + HPSB_DEBUG("Stopping reset loop for IRM sanity"); + return 1; + } + hpsb_send_phy_config(host, NODEID_TO_NODE(host->node_id), -1); hpsb_reset_bus(host, LONG_RESET_FORCE_ROOT); + + return 0; } } + + return 1; } /* We need to ensure that if we are not the IRM, that the IRM node is capable of @@ -1608,8 +1678,10 @@ static int nodemgr_host_thread(void *__hi) * to make sure things settle down. */ for (i = 0; i < 4 ; i++) { set_current_state(TASK_INTERRUPTIBLE); - if (schedule_timeout(HZ/16)) + if (schedule_timeout(HZ/16)) { + up(&nodemgr_serialize); goto caught_signal; + } /* Now get the generation in which the node ID's we collect * are valid. During the bus scan we will use this generation @@ -1624,16 +1696,28 @@ static int nodemgr_host_thread(void *__hi) i = 0; } - if (!nodemgr_check_irm_capability(host, reset_cycles++)) { - /* Do nothing, we are resetting */ + if (!nodemgr_check_irm_capability(host, reset_cycles)) { + reset_cycles++; + up(&nodemgr_serialize); + continue; + } + + /* 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. */ + 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. */ nodemgr_node_probe(hi, generation); - nodemgr_do_irm_duties(host); /* Update some of our sysfs symlinks */ nodemgr_update_host_dev_links(host); @@ -1778,7 +1862,6 @@ static void nodemgr_remove_host(struct hpsb_host *host) kill_proc(hi->pid, SIGTERM, 1); wait_for_completion(&hi->exited); nodemgr_remove_host_dev(&host->device); - device_unregister(&host->device); } } else HPSB_ERR("NodeMgr: host %s does not exist, cannot remove", diff --git a/drivers/ieee1394/nodemgr.h b/drivers/ieee1394/nodemgr.h index b7c86b1e5f55..4600b6be2ba3 100644 --- a/drivers/ieee1394/nodemgr.h +++ b/drivers/ieee1394/nodemgr.h @@ -130,6 +130,7 @@ struct node_entry { struct hpsb_host *host; /* Host this node is attached to */ nodeid_t nodeid; /* NodeID */ struct bus_options busopt; /* Bus Options */ + int needs_probe; unsigned int generation; /* Synced with hpsb generation */ /* The following is read from the config rom */ |
