From 24926dadc41cc566e974022b0e66231b82c6375f Mon Sep 17 00:00:00 2001 From: Mark Salyzyn Date: Thu, 1 Sep 2011 06:11:17 -0700 Subject: [SCSI] libsas: fix failure to revalidate domain for anything but the first expander child. In an enclosure model where there are chaining expanders to a large body of storage, it was discovered that libsas, responding to a broadcast event change, would only revalidate the domain of first child expander in the list. The issue is that the pointer value to the discovered source device was used to break out of the loop, rather than the content of the pointer. This still remains non-compliant as the revalidate domain code is supposed to loop through all child expanders, and not stop at the first one it finds that reports a change count. However, the design of this routine does not allow multiple device discoveries and that would be a more complicated set of patches reserved for another day. We are fixing the glaring bug rather than refactoring the code. Signed-off-by: Mark Salyzyn Cc: stable@kernel.org Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_expander.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/scsi/libsas/sas_expander.c') diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index f84084bba2f0..c9e3dc024bc3 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c @@ -1721,7 +1721,7 @@ static int sas_find_bcast_dev(struct domain_device *dev, list_for_each_entry(ch, &ex->children, siblings) { if (ch->dev_type == EDGE_DEV || ch->dev_type == FANOUT_DEV) { res = sas_find_bcast_dev(ch, src_dev); - if (src_dev) + if (*src_dev) return res; } } -- cgit v1.2.3 From ffaac8f45bfb2dffb78179baa5740de34058eef8 Mon Sep 17 00:00:00 2001 From: Luben Tuikov Date: Thu, 22 Sep 2011 09:41:36 -0700 Subject: [SCSI] libsas: Allow expander T-T attachments Allow expander table-to-table attachments for expanders that support it. Signed-off-by: Luben Tuikov Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_expander.c | 20 ++++++++++++++------ include/scsi/libsas.h | 3 +++ include/scsi/sas.h | 14 ++++++++++++-- 3 files changed, 29 insertions(+), 8 deletions(-) (limited to 'drivers/scsi/libsas/sas_expander.c') diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index f84084bba2f0..e8d0115a025a 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c @@ -329,6 +329,7 @@ static void ex_assign_report_general(struct domain_device *dev, dev->ex_dev.ex_change_count = be16_to_cpu(rg->change_count); dev->ex_dev.max_route_indexes = be16_to_cpu(rg->route_indexes); dev->ex_dev.num_phys = min(rg->num_phys, (u8)MAX_EXPANDER_PHYS); + dev->ex_dev.t2t_supp = rg->t2t_supp; dev->ex_dev.conf_route_table = rg->conf_route_table; dev->ex_dev.configuring = rg->configuring; memcpy(dev->ex_dev.enclosure_logical_id, rg->enclosure_logical_id, 8); @@ -1133,15 +1134,17 @@ static void sas_print_parent_topology_bug(struct domain_device *child, }; struct domain_device *parent = child->parent; - sas_printk("%s ex %016llx phy 0x%x <--> %s ex %016llx phy 0x%x " - "has %c:%c routing link!\n", + sas_printk("%s ex %016llx (T2T supp:%d) phy 0x%x <--> %s ex %016llx " + "(T2T supp:%d) phy 0x%x has %c:%c routing link!\n", ex_type[parent->dev_type], SAS_ADDR(parent->sas_addr), + parent->ex_dev.t2t_supp, parent_phy->phy_id, ex_type[child->dev_type], SAS_ADDR(child->sas_addr), + child->ex_dev.t2t_supp, child_phy->phy_id, ra_char[parent_phy->routing_attr], @@ -1238,10 +1241,15 @@ static int sas_check_parent_topology(struct domain_device *child) sas_print_parent_topology_bug(child, parent_phy, child_phy); res = -ENODEV; } - } else if (parent_phy->routing_attr == TABLE_ROUTING && - child_phy->routing_attr != SUBTRACTIVE_ROUTING) { - sas_print_parent_topology_bug(child, parent_phy, child_phy); - res = -ENODEV; + } else if (parent_phy->routing_attr == TABLE_ROUTING) { + if (child_phy->routing_attr == SUBTRACTIVE_ROUTING || + (child_phy->routing_attr == TABLE_ROUTING && + child_ex->t2t_supp && parent_ex->t2t_supp)) { + /* All good */; + } else { + sas_print_parent_topology_bug(child, parent_phy, child_phy); + res = -ENODEV; + } } break; case FANOUT_DEV: diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h index af0a1deac930..ac9d80c93c68 100644 --- a/include/scsi/libsas.h +++ b/include/scsi/libsas.h @@ -142,8 +142,11 @@ struct expander_device { u16 ex_change_count; u16 max_route_indexes; u8 num_phys; + + u8 t2t_supp:1; u8 configuring:1; u8 conf_route_table:1; + u8 enclosure_logical_id[8]; struct ex_phy *ex_phy; diff --git a/include/scsi/sas.h b/include/scsi/sas.h index a3001add0c66..07d504f3981c 100644 --- a/include/scsi/sas.h +++ b/include/scsi/sas.h @@ -349,7 +349,12 @@ struct report_general_resp { u8 conf_route_table:1; u8 configuring:1; - u8 _r_b:6; + u8 config_others:1; + u8 orej_retry_supp:1; + u8 stp_cont_awt:1; + u8 self_config:1; + u8 zone_config:1; + u8 t2t_supp:1; u8 _r_c; @@ -536,7 +541,12 @@ struct report_general_resp { u8 _r_a; u8 num_phys; - u8 _r_b:6; + u8 t2t_supp:1; + u8 zone_config:1; + u8 self_config:1; + u8 stp_cont_awt:1; + u8 orej_retry_supp:1; + u8 config_others:1; u8 configuring:1; u8 conf_route_table:1; -- cgit v1.2.3 From bb041a0e9c31229071b6e56e1d0d8374af0d2038 Mon Sep 17 00:00:00 2001 From: Jack Wang Date: Fri, 23 Sep 2011 14:32:32 +0800 Subject: [SCSI] libsas: set sas_address and device type of rphy Libsas forget to set the sas_address and device type of rphy lead to file under /sys/class/sas_x show wrong value, fix that. Signed-off-by: Jack Wang Tested-by: Crystal Yu Cc: stable@kernel.org Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_expander.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/scsi/libsas/sas_expander.c') diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index e8d0115a025a..b172f1773081 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c @@ -199,6 +199,8 @@ static void sas_set_ex_phy(struct domain_device *dev, int phy_id, phy->virtual = dr->virtual; phy->last_da_index = -1; + phy->phy->identify.sas_address = SAS_ADDR(phy->attached_sas_addr); + phy->phy->identify.device_type = phy->attached_dev_type; phy->phy->identify.initiator_port_protocols = phy->attached_iproto; phy->phy->identify.target_port_protocols = phy->attached_tproto; phy->phy->identify.phy_identifier = phy_id; -- cgit v1.2.3 From a73914c35b05d80f8ce78288e10056c91090b666 Mon Sep 17 00:00:00 2001 From: Mark Salyzyn Date: Thu, 22 Sep 2011 08:32:23 -0700 Subject: [SCSI] libsas: fix panic when single phy is disabled on a wide port When a wide port is being utilized to a target, if one disables only one of the phys, we get an OS crash: BUG: unable to handle kernel NULL pointer dereference at 0000000000000238 IP: [] mutex_lock+0x21/0x50 PGD 4103f5067 PUD 41dba9067 PMD 0 Oops: 0002 [#1] SMP last sysfs file: /sys/bus/pci/slots/5/address CPU 0 Modules linked in: pm8001(U) ses enclosure fuse nfsd exportfs autofs4 ipmi_devintf ipmi_si ipmi_msghandler nfs lockd fscache nfs_acl auth_rpcgss 8021q fcoe libfcoe garp libfc scsi_transport_fc stp scsi_tgt llc sunrpc cpufreq_ondemand acpi_cpufreq freq_table ipv6 sr_mod cdrom dm_mirror dm_region_hash dm_log uinput sg i2c_i801 i2c_core iTCO_wdt iTCO_vendor_support e1000e mlx4_ib ib_mad ib_core mlx4_en mlx4_core ext3 jbd mbcache sd_mod crc_t10dif usb_storage ata_generic pata_acpi ata_piix libsas(U) scsi_transport_sas dm_mod [last unloaded: pm8001] Modules linked in: pm8001(U) ses enclosure fuse nfsd exportfs autofs4 ipmi_devintf ipmi_si ipmi_msghandler nfs lockd fscache nfs_acl auth_rpcgss 8021q fcoe libfcoe garp libfc scsi_transport_fc stp scsi_tgt llc sunrpc cpufreq_ondemand acpi_cpufreq freq_table ipv6 sr_mod cdrom dm_mirror dm_region_hash dm_log uinput sg i2c_i801 i2c_core iTCO_wdt iTCO_vendor_support e1000e mlx4_ib ib_mad ib_core mlx4_en mlx4_core ext3 jbd mbcache sd_mod crc_t10dif usb_storage ata_generic pata_acpi ata_piix libsas(U) scsi_transport_sas dm_mod [last unloaded: pm8001] Pid: 5146, comm: scsi_wq_5 Not tainted 2.6.32-71.29.1.el6.lustre.7.x86_64 #1 Storage Server RIP: 0010:[] [] mutex_lock+0x21/0x50 RSP: 0018:ffff8803e4e33d30 EFLAGS: 00010246 RAX: 0000000000000000 RBX: 0000000000000238 RCX: 0000000000000000 RDX: 0000000000000000 RSI: ffff8803e664c800 RDI: 0000000000000238 RBP: ffff8803e4e33d40 R08: 0000000000000000 R09: 0000000000000000 R10: 0000000000000000 R11: 0000000000000001 R12: 0000000000000000 R13: 0000000000000238 R14: ffff88041acb7200 R15: ffff88041c51ada0 FS: 0000000000000000(0000) GS:ffff880028200000(0000) knlGS:0000000000000000 CS: 0010 DS: 0018 ES: 0018 CR0: 000000008005003b CR2: 0000000000000238 CR3: 0000000410143000 CR4: 00000000000006f0 DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400 Process scsi_wq_5 (pid: 5146, threadinfo ffff8803e4e32000, task ffff8803e4e294a0) Stack: ffff8803e664c800 0000000000000000 ffff8803e4e33d70 ffffffffa001f06e <0> ffff8803e4e33d60 ffff88041c51ada0 ffff88041acb7200 ffff88041bc0aa00 <0> ffff8803e4e33d90 ffffffffa0032b6c 0000000000000014 ffff88041acb7200 Call Trace: [] sas_port_delete_phy+0x2e/0xa0 [scsi_transport_sas] [] sas_unregister_devs_sas_addr+0xac/0xe0 [libsas] [] sas_ex_revalidate_domain+0x204/0x330 [libsas] [] ? sas_revalidate_domain+0x0/0x90 [libsas] [] sas_revalidate_domain+0x65/0x90 [libsas] [] worker_thread+0x170/0x2a0 [] ? autoremove_wake_function+0x0/0x40 [] ? worker_thread+0x0/0x2a0 [] kthread+0x96/0xa0 [] child_rip+0xa/0x20 [] ? kthread+0x0/0xa0 [] ? child_rip+0x0/0x20 Code: ff ff 85 c0 75 ed eb d6 66 90 55 48 89 e5 48 83 ec 10 48 89 1c 24 4c 89 64 24 08 0f 1f 44 00 00 48 89 fb e8 92 f4 ff ff 48 89 df ff 0f 79 05 e8 25 00 00 00 65 48 8b 04 25 08 cc 00 00 48 2d RIP [] mutex_lock+0x21/0x50 RSP CR2: 0000000000000238 The following patch is admittedly a band-aid, and does not solve the root cause, but it still is a good candidate for hardening as a pointer check before reference. Signed-off-by: Mark Salyzyn Tested-by: Jack Wang Cc: stable@kernel.org Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_expander.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'drivers/scsi/libsas/sas_expander.c') diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index c9e3dc024bc3..16ad97df5ba6 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c @@ -1769,10 +1769,12 @@ static void sas_unregister_devs_sas_addr(struct domain_device *parent, sas_disable_routing(parent, phy->attached_sas_addr); } memset(phy->attached_sas_addr, 0, SAS_ADDR_SIZE); - sas_port_delete_phy(phy->port, phy->phy); - if (phy->port->num_phys == 0) - sas_port_delete(phy->port); - phy->port = NULL; + if (phy->port) { + sas_port_delete_phy(phy->port, phy->phy); + if (phy->port->num_phys == 0) + sas_port_delete(phy->port); + phy->port = NULL; + } } static int sas_discover_bfs_by_root_level(struct domain_device *root, -- cgit v1.2.3 From 1a34c0640137eed8dabdac3a68a7a84116ac9e0d Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 21 Sep 2011 22:05:34 -0700 Subject: [SCSI] libsas: fix port->dev_list locking port->dev_list maintains a list of devices attached to a given sas root port. It needs to be mutated under a lock as contexts outside of the single-threaded-libsas-workqueue access the list via sas_find_dev_by_rphy(). Fixup locations where the list was being mutated without a lock. This is a follow-up to commit 5911e963 "[SCSI] libsas: remove expander from dev list on error", where Luben noted [1]: > 2/ We have unlocked list manipulations in sas_ex_discover_end_dev(), > sas_unregister_common_dev(), and sas_ex_discover_end_dev() Yes, I can see that and that is very unfortunate. [1]: http://marc.info/?l=linux-scsi&m=131480962006471&w=2 Signed-off-by: Dan Williams Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_discover.c | 13 ++++++++----- drivers/scsi/libsas/sas_expander.c | 15 +++++++++------ include/scsi/libsas.h | 2 +- 3 files changed, 18 insertions(+), 12 deletions(-) (limited to 'drivers/scsi/libsas/sas_expander.c') diff --git a/drivers/scsi/libsas/sas_discover.c b/drivers/scsi/libsas/sas_discover.c index f5831930df9b..54a5199ceb56 100644 --- a/drivers/scsi/libsas/sas_discover.c +++ b/drivers/scsi/libsas/sas_discover.c @@ -219,17 +219,20 @@ out_err2: /* ---------- Device registration and unregistration ---------- */ -static inline void sas_unregister_common_dev(struct domain_device *dev) +static void sas_unregister_common_dev(struct asd_sas_port *port, struct domain_device *dev) { sas_notify_lldd_dev_gone(dev); if (!dev->parent) dev->port->port_dev = NULL; else list_del_init(&dev->siblings); + + spin_lock_irq(&port->dev_list_lock); list_del_init(&dev->dev_list_node); + spin_unlock_irq(&port->dev_list_lock); } -void sas_unregister_dev(struct domain_device *dev) +void sas_unregister_dev(struct asd_sas_port *port, struct domain_device *dev) { if (dev->rphy) { sas_remove_children(&dev->rphy->dev); @@ -241,15 +244,15 @@ void sas_unregister_dev(struct domain_device *dev) kfree(dev->ex_dev.ex_phy); dev->ex_dev.ex_phy = NULL; } - sas_unregister_common_dev(dev); + sas_unregister_common_dev(port, dev); } void sas_unregister_domain_devices(struct asd_sas_port *port) { struct domain_device *dev, *n; - list_for_each_entry_safe_reverse(dev,n,&port->dev_list,dev_list_node) - sas_unregister_dev(dev); + list_for_each_entry_safe_reverse(dev, n, &port->dev_list, dev_list_node) + sas_unregister_dev(port, dev); port->port->rphy = NULL; diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index b172f1773081..88bbe8e1e844 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c @@ -754,7 +754,10 @@ static struct domain_device *sas_ex_discover_end_dev( out_list_del: sas_rphy_free(child->rphy); child->rphy = NULL; + + spin_lock_irq(&parent->port->dev_list_lock); list_del(&child->dev_list_node); + spin_unlock_irq(&parent->port->dev_list_lock); out_free: sas_port_delete(phy->port); out_err: @@ -1739,7 +1742,7 @@ out: return res; } -static void sas_unregister_ex_tree(struct domain_device *dev) +static void sas_unregister_ex_tree(struct asd_sas_port *port, struct domain_device *dev) { struct expander_device *ex = &dev->ex_dev; struct domain_device *child, *n; @@ -1748,11 +1751,11 @@ static void sas_unregister_ex_tree(struct domain_device *dev) child->gone = 1; if (child->dev_type == EDGE_DEV || child->dev_type == FANOUT_DEV) - sas_unregister_ex_tree(child); + sas_unregister_ex_tree(port, child); else - sas_unregister_dev(child); + sas_unregister_dev(port, child); } - sas_unregister_dev(dev); + sas_unregister_dev(port, dev); } static void sas_unregister_devs_sas_addr(struct domain_device *parent, @@ -1769,9 +1772,9 @@ static void sas_unregister_devs_sas_addr(struct domain_device *parent, child->gone = 1; if (child->dev_type == EDGE_DEV || child->dev_type == FANOUT_DEV) - sas_unregister_ex_tree(child); + sas_unregister_ex_tree(parent->port, child); else - sas_unregister_dev(child); + sas_unregister_dev(parent->port, child); break; } } diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h index c4b7cd0b85e5..6a308d42d98f 100644 --- a/include/scsi/libsas.h +++ b/include/scsi/libsas.h @@ -656,7 +656,7 @@ int sas_discover_event(struct asd_sas_port *, enum discover_event ev); int sas_discover_sata(struct domain_device *); int sas_discover_end_dev(struct domain_device *); -void sas_unregister_dev(struct domain_device *); +void sas_unregister_dev(struct asd_sas_port *port, struct domain_device *); void sas_init_dev(struct domain_device *); -- cgit v1.2.3