From 3c5c48011809045881d30e197577ef1dca9a3e72 Mon Sep 17 00:00:00 2001 From: Vikas Chaudhary Date: Thu, 19 Jan 2012 03:06:53 -0800 Subject: [SCSI] libiscsi: Added support to show targetalias in sysfs sysfs patch to view target alias: /sys/class/iscsi_session/session*/targetalias Signed-off-by: Vikas Chaudhary Reviewed-by: Mike Christie Signed-off-by: James Bottomley --- include/scsi/libiscsi.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include/scsi') diff --git a/include/scsi/libiscsi.h b/include/scsi/libiscsi.h index cedcff371c88..2e42e9a0e0b6 100644 --- a/include/scsi/libiscsi.h +++ b/include/scsi/libiscsi.h @@ -284,6 +284,7 @@ struct iscsi_session { char *password; char *password_in; char *targetname; + char *targetalias; char *ifacename; char *initiatorname; /* control data */ -- cgit v1.2.3 From aeddde2978f8931740032880134039fb937bb07c Mon Sep 17 00:00:00 2001 From: Vikas Chaudhary Date: Thu, 19 Jan 2012 03:06:55 -0800 Subject: [SCSI] scsi_transport_iscsi: Added support to show port_state and port_speed in sysfs sysfs patch to view port_state: /sys/class/iscsi_host/host*/port_state sysfs patch to view port_speed: /sys/class/iscsi_host/host*/port_speed Signed-off-by: Vikas Chaudhary Reviewed-by: Mike Christie Signed-off-by: James Bottomley --- drivers/scsi/scsi_transport_iscsi.c | 63 +++++++++++++++++++++++++++++++++++++ include/scsi/iscsi_if.h | 17 ++++++++++ include/scsi/scsi_transport_iscsi.h | 4 +++ 3 files changed, 84 insertions(+) (limited to 'include/scsi') diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c index cfd491437239..97832a2876bd 100644 --- a/drivers/scsi/scsi_transport_iscsi.c +++ b/drivers/scsi/scsi_transport_iscsi.c @@ -2476,12 +2476,16 @@ iscsi_host_attr(netdev, ISCSI_HOST_PARAM_NETDEV_NAME); iscsi_host_attr(hwaddress, ISCSI_HOST_PARAM_HWADDRESS); iscsi_host_attr(ipaddress, ISCSI_HOST_PARAM_IPADDRESS); iscsi_host_attr(initiatorname, ISCSI_HOST_PARAM_INITIATOR_NAME); +iscsi_host_attr(port_state, ISCSI_HOST_PARAM_PORT_STATE); +iscsi_host_attr(port_speed, ISCSI_HOST_PARAM_PORT_SPEED); static struct attribute *iscsi_host_attrs[] = { &dev_attr_host_netdev.attr, &dev_attr_host_hwaddress.attr, &dev_attr_host_ipaddress.attr, &dev_attr_host_initiatorname.attr, + &dev_attr_host_port_state.attr, + &dev_attr_host_port_speed.attr, NULL, }; @@ -2501,6 +2505,10 @@ static umode_t iscsi_host_attr_is_visible(struct kobject *kobj, param = ISCSI_HOST_PARAM_IPADDRESS; else if (attr == &dev_attr_host_initiatorname.attr) param = ISCSI_HOST_PARAM_INITIATOR_NAME; + else if (attr == &dev_attr_host_port_state.attr) + param = ISCSI_HOST_PARAM_PORT_STATE; + else if (attr == &dev_attr_host_port_speed.attr) + param = ISCSI_HOST_PARAM_PORT_SPEED; else { WARN_ONCE(1, "Invalid host attr"); return 0; @@ -2514,6 +2522,61 @@ static struct attribute_group iscsi_host_group = { .is_visible = iscsi_host_attr_is_visible, }; +/* convert iscsi_port_speed values to ascii string name */ +static const struct { + enum iscsi_port_speed value; + char *name; +} iscsi_port_speed_names[] = { + {ISCSI_PORT_SPEED_UNKNOWN, "Unknown" }, + {ISCSI_PORT_SPEED_10MBPS, "10 Mbps" }, + {ISCSI_PORT_SPEED_100MBPS, "100 Mbps" }, + {ISCSI_PORT_SPEED_1GBPS, "1 Gbps" }, + {ISCSI_PORT_SPEED_10GBPS, "10 Gbps" }, +}; + +char *iscsi_get_port_speed_name(struct Scsi_Host *shost) +{ + int i; + char *speed = "Unknown!"; + struct iscsi_cls_host *ihost = shost->shost_data; + uint32_t port_speed = ihost->port_speed; + + for (i = 0; i < ARRAY_SIZE(iscsi_port_speed_names); i++) { + if (iscsi_port_speed_names[i].value & port_speed) { + speed = iscsi_port_speed_names[i].name; + break; + } + } + return speed; +} +EXPORT_SYMBOL_GPL(iscsi_get_port_speed_name); + +/* convert iscsi_port_state values to ascii string name */ +static const struct { + enum iscsi_port_state value; + char *name; +} iscsi_port_state_names[] = { + {ISCSI_PORT_STATE_DOWN, "LINK DOWN" }, + {ISCSI_PORT_STATE_UP, "LINK UP" }, +}; + +char *iscsi_get_port_state_name(struct Scsi_Host *shost) +{ + int i; + char *state = "Unknown!"; + struct iscsi_cls_host *ihost = shost->shost_data; + uint32_t port_state = ihost->port_state; + + for (i = 0; i < ARRAY_SIZE(iscsi_port_state_names); i++) { + if (iscsi_port_state_names[i].value & port_state) { + state = iscsi_port_state_names[i].name; + break; + } + } + return state; +} +EXPORT_SYMBOL_GPL(iscsi_get_port_state_name); + static int iscsi_session_match(struct attribute_container *cont, struct device *dev) { diff --git a/include/scsi/iscsi_if.h b/include/scsi/iscsi_if.h index 2703e3bedbf5..e49b7c8dd217 100644 --- a/include/scsi/iscsi_if.h +++ b/include/scsi/iscsi_if.h @@ -416,9 +416,26 @@ enum iscsi_host_param { ISCSI_HOST_PARAM_INITIATOR_NAME, ISCSI_HOST_PARAM_NETDEV_NAME, ISCSI_HOST_PARAM_IPADDRESS, + ISCSI_HOST_PARAM_PORT_STATE, + ISCSI_HOST_PARAM_PORT_SPEED, ISCSI_HOST_PARAM_MAX, }; +/* iSCSI port Speed */ +enum iscsi_port_speed { + ISCSI_PORT_SPEED_UNKNOWN = 0x1, + ISCSI_PORT_SPEED_10MBPS = 0x2, + ISCSI_PORT_SPEED_100MBPS = 0x4, + ISCSI_PORT_SPEED_1GBPS = 0x8, + ISCSI_PORT_SPEED_10GBPS = 0x10, +}; + +/* iSCSI port state */ +enum iscsi_port_state { + ISCSI_PORT_STATE_DOWN = 0x1, + ISCSI_PORT_STATE_UP = 0x2, +}; + #define iscsi_ptr(_handle) ((void*)(unsigned long)_handle) #define iscsi_handle(_ptr) ((uint64_t)(unsigned long)_ptr) diff --git a/include/scsi/scsi_transport_iscsi.h b/include/scsi/scsi_transport_iscsi.h index 2c3a46d102fd..fa7ca4e16020 100644 --- a/include/scsi/scsi_transport_iscsi.h +++ b/include/scsi/scsi_transport_iscsi.h @@ -238,6 +238,8 @@ struct iscsi_cls_host { atomic_t nr_scans; struct mutex mutex; struct request_queue *bsg_q; + uint32_t port_speed; + uint32_t port_state; }; #define iscsi_job_to_shost(_job) \ @@ -307,5 +309,7 @@ extern struct iscsi_iface *iscsi_create_iface(struct Scsi_Host *shost, uint32_t iface_num, int dd_size); extern void iscsi_destroy_iface(struct iscsi_iface *iface); extern struct iscsi_iface *iscsi_lookup_iface(int handle); +extern char *iscsi_get_port_speed_name(struct Scsi_Host *shost); +extern char *iscsi_get_port_state_name(struct Scsi_Host *shost); #endif -- cgit v1.2.3 From bb8ef587a715c6a084f80b9c311136aa765ebfad Mon Sep 17 00:00:00 2001 From: Neerav Parikh Date: Sun, 22 Jan 2012 17:29:50 -0800 Subject: [SCSI] scsi_transport_fc: Add FDMI host attributes This adds FC-GS Fabric Device Management Interface (FDMI) related attributes to fc_host_attr structure. This is in preparation for allowing FDMI attributes to be registered via libfc. Signed-off-by: Neerav Parikh Tested-by: Ross Brattain Acked-by: Robert Love Signed-off-by: James Bottomley --- drivers/scsi/scsi_transport_fc.c | 30 +++++++++++++++++++++++++++++- include/scsi/scsi_transport_fc.h | 28 ++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 1 deletion(-) (limited to 'include/scsi') diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c index f59d4a05ecd7..80fbe2ac0b47 100644 --- a/drivers/scsi/scsi_transport_fc.c +++ b/drivers/scsi/scsi_transport_fc.c @@ -313,7 +313,7 @@ static void fc_scsi_scan_rport(struct work_struct *work); #define FC_STARGET_NUM_ATTRS 3 #define FC_RPORT_NUM_ATTRS 10 #define FC_VPORT_NUM_ATTRS 9 -#define FC_HOST_NUM_ATTRS 22 +#define FC_HOST_NUM_ATTRS 29 struct fc_internal { struct scsi_transport_template t; @@ -399,6 +399,20 @@ static int fc_host_setup(struct transport_container *tc, struct device *dev, fc_host->max_npiv_vports = 0; memset(fc_host->serial_number, 0, sizeof(fc_host->serial_number)); + memset(fc_host->manufacturer, 0, + sizeof(fc_host->manufacturer)); + memset(fc_host->model, 0, + sizeof(fc_host->model)); + memset(fc_host->model_description, 0, + sizeof(fc_host->model_description)); + memset(fc_host->hardware_version, 0, + sizeof(fc_host->hardware_version)); + memset(fc_host->driver_version, 0, + sizeof(fc_host->driver_version)); + memset(fc_host->firmware_version, 0, + sizeof(fc_host->firmware_version)); + memset(fc_host->optionrom_version, 0, + sizeof(fc_host->optionrom_version)); fc_host->port_id = -1; fc_host->port_type = FC_PORTTYPE_UNKNOWN; @@ -1513,6 +1527,13 @@ fc_private_host_rd_attr_cast(permanent_port_name, "0x%llx\n", 20, fc_private_host_rd_attr(maxframe_size, "%u bytes\n", 20); fc_private_host_rd_attr(max_npiv_vports, "%u\n", 20); fc_private_host_rd_attr(serial_number, "%s\n", (FC_SERIAL_NUMBER_SIZE +1)); +fc_private_host_rd_attr(manufacturer, "%s\n", FC_SERIAL_NUMBER_SIZE + 1); +fc_private_host_rd_attr(model, "%s\n", FC_SYMBOLIC_NAME_SIZE + 1); +fc_private_host_rd_attr(model_description, "%s\n", FC_SYMBOLIC_NAME_SIZE + 1); +fc_private_host_rd_attr(hardware_version, "%s\n", FC_VERSION_STRING_SIZE + 1); +fc_private_host_rd_attr(driver_version, "%s\n", FC_VERSION_STRING_SIZE + 1); +fc_private_host_rd_attr(firmware_version, "%s\n", FC_VERSION_STRING_SIZE + 1); +fc_private_host_rd_attr(optionrom_version, "%s\n", FC_VERSION_STRING_SIZE + 1); /* Dynamic Host Attributes */ @@ -2208,6 +2229,13 @@ fc_attach_transport(struct fc_function_template *ft) SETUP_HOST_ATTRIBUTE_RD_NS(npiv_vports_inuse); } SETUP_HOST_ATTRIBUTE_RD(serial_number); + SETUP_HOST_ATTRIBUTE_RD(manufacturer); + SETUP_HOST_ATTRIBUTE_RD(model); + SETUP_HOST_ATTRIBUTE_RD(model_description); + SETUP_HOST_ATTRIBUTE_RD(hardware_version); + SETUP_HOST_ATTRIBUTE_RD(driver_version); + SETUP_HOST_ATTRIBUTE_RD(firmware_version); + SETUP_HOST_ATTRIBUTE_RD(optionrom_version); SETUP_HOST_ATTRIBUTE_RD(port_id); SETUP_HOST_ATTRIBUTE_RD(port_type); diff --git a/include/scsi/scsi_transport_fc.h b/include/scsi/scsi_transport_fc.h index 2a65167a8f10..0135cbc08089 100644 --- a/include/scsi/scsi_transport_fc.h +++ b/include/scsi/scsi_transport_fc.h @@ -486,6 +486,13 @@ struct fc_host_attrs { u32 maxframe_size; u16 max_npiv_vports; char serial_number[FC_SERIAL_NUMBER_SIZE]; + char manufacturer[FC_SERIAL_NUMBER_SIZE]; + char model[FC_SYMBOLIC_NAME_SIZE]; + char model_description[FC_SYMBOLIC_NAME_SIZE]; + char hardware_version[FC_VERSION_STRING_SIZE]; + char driver_version[FC_VERSION_STRING_SIZE]; + char firmware_version[FC_VERSION_STRING_SIZE]; + char optionrom_version[FC_VERSION_STRING_SIZE]; /* Dynamic Attributes */ u32 port_id; @@ -541,6 +548,20 @@ struct fc_host_attrs { (((struct fc_host_attrs *)(x)->shost_data)->max_npiv_vports) #define fc_host_serial_number(x) \ (((struct fc_host_attrs *)(x)->shost_data)->serial_number) +#define fc_host_manufacturer(x) \ + (((struct fc_host_attrs *)(x)->shost_data)->manufacturer) +#define fc_host_model(x) \ + (((struct fc_host_attrs *)(x)->shost_data)->model) +#define fc_host_model_description(x) \ + (((struct fc_host_attrs *)(x)->shost_data)->model_description) +#define fc_host_hardware_version(x) \ + (((struct fc_host_attrs *)(x)->shost_data)->hardware_version) +#define fc_host_driver_version(x) \ + (((struct fc_host_attrs *)(x)->shost_data)->driver_version) +#define fc_host_firmware_version(x) \ + (((struct fc_host_attrs *)(x)->shost_data)->firmware_version) +#define fc_host_optionrom_version(x) \ + (((struct fc_host_attrs *)(x)->shost_data)->optionrom_version) #define fc_host_port_id(x) \ (((struct fc_host_attrs *)(x)->shost_data)->port_id) #define fc_host_port_type(x) \ @@ -700,6 +721,13 @@ struct fc_function_template { unsigned long show_host_supported_speeds:1; unsigned long show_host_maxframe_size:1; unsigned long show_host_serial_number:1; + unsigned long show_host_manufacturer:1; + unsigned long show_host_model:1; + unsigned long show_host_model_description:1; + unsigned long show_host_hardware_version:1; + unsigned long show_host_driver_version:1; + unsigned long show_host_firmware_version:1; + unsigned long show_host_optionrom_version:1; /* host dynamic attributes */ unsigned long show_host_port_id:1; unsigned long show_host_port_type:1; -- cgit v1.2.3 From a9277e7783651d4e0a849f7988340b1c1cf748a4 Mon Sep 17 00:00:00 2001 From: Neerav Parikh Date: Sun, 22 Jan 2012 17:29:55 -0800 Subject: [SCSI] scsi_transport_fc: Getting FC Port Speed in sync with FC-GS The values for the 4G and 10G speeds are not in sync with definitions in SM-HBA/FC-GS-x/etc. This patch brings them in sync to these specifications. The values are converted to strings when represented via sysfs attribute, hence that should cover for user space apps as they may not see any change. Signed-off-by: Neerav Parikh Tested-by: Ross Brattain Acked-by: Robert Love Signed-off-by: James Bottomley --- include/scsi/scsi_transport_fc.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include/scsi') diff --git a/include/scsi/scsi_transport_fc.h b/include/scsi/scsi_transport_fc.h index 0135cbc08089..719faf1863ad 100644 --- a/include/scsi/scsi_transport_fc.h +++ b/include/scsi/scsi_transport_fc.h @@ -126,8 +126,8 @@ enum fc_vport_state { incapable of reporting */ #define FC_PORTSPEED_1GBIT 1 #define FC_PORTSPEED_2GBIT 2 -#define FC_PORTSPEED_4GBIT 4 -#define FC_PORTSPEED_10GBIT 8 +#define FC_PORTSPEED_10GBIT 4 +#define FC_PORTSPEED_4GBIT 8 #define FC_PORTSPEED_8GBIT 0x10 #define FC_PORTSPEED_16GBIT 0x20 #define FC_PORTSPEED_NOT_NEGOTIATED (1 << 15) /* Speed not established */ -- cgit v1.2.3 From 1ea2c1daf4476ac798b1de8196f11dd36425b5ae Mon Sep 17 00:00:00 2001 From: Neerav Parikh Date: Sun, 22 Jan 2012 17:30:00 -0800 Subject: [SCSI] libfc: Make the libfc Common Transport(CT) code generic Currently the libfc Common Transport(CT) calls assume that the CT requests are Name Server specific only. This patch makes it more flexible to allow more FC-GS services to make use of these routines. Signed-off-by: Neerav Parikh Tested-by: Ross Brattain Acked-by: Robert Love Signed-off-by: James Bottomley --- drivers/scsi/libfc/fc_elsct.c | 3 +-- include/scsi/fc_encode.h | 60 ++++++++++++++++++++++++++++++++++--------- 2 files changed, 49 insertions(+), 14 deletions(-) (limited to 'include/scsi') diff --git a/drivers/scsi/libfc/fc_elsct.c b/drivers/scsi/libfc/fc_elsct.c index e17a28d324d0..c2384d501470 100644 --- a/drivers/scsi/libfc/fc_elsct.c +++ b/drivers/scsi/libfc/fc_elsct.c @@ -56,8 +56,7 @@ struct fc_seq *fc_elsct_send(struct fc_lport *lport, u32 did, rc = fc_els_fill(lport, did, fp, op, &r_ctl, &fh_type); else { /* CT requests */ - rc = fc_ct_fill(lport, did, fp, op, &r_ctl, &fh_type); - did = FC_FID_DIR_SERV; + rc = fc_ct_fill(lport, did, fp, op, &r_ctl, &fh_type, &did); } if (rc) { diff --git a/include/scsi/fc_encode.h b/include/scsi/fc_encode.h index be418d8448a5..73bc43329f86 100644 --- a/include/scsi/fc_encode.h +++ b/include/scsi/fc_encode.h @@ -97,7 +97,9 @@ static inline void fc_adisc_fill(struct fc_lport *lport, struct fc_frame *fp) * returns pointer to ct request. */ static inline struct fc_ct_req *fc_ct_hdr_fill(const struct fc_frame *fp, - unsigned int op, size_t req_size) + unsigned int op, size_t req_size, + enum fc_ct_fs_type fs_type, + u8 subtype) { struct fc_ct_req *ct; size_t ct_plen; @@ -106,14 +108,14 @@ static inline struct fc_ct_req *fc_ct_hdr_fill(const struct fc_frame *fp, ct = fc_frame_payload_get(fp, ct_plen); memset(ct, 0, ct_plen); ct->hdr.ct_rev = FC_CT_REV; - ct->hdr.ct_fs_type = FC_FST_DIR; - ct->hdr.ct_fs_subtype = FC_NS_SUBTYPE; + ct->hdr.ct_fs_type = fs_type; + ct->hdr.ct_fs_subtype = subtype; ct->hdr.ct_cmd = htons((u16) op); return ct; } /** - * fc_ct_fill() - Fill in a name service request frame + * fc_ct_ns_fill() - Fill in a name service request frame * @lport: local port. * @fc_id: FC_ID of non-destination rport for GPN_ID and similar inquiries. * @fp: frame to contain payload. @@ -121,7 +123,7 @@ static inline struct fc_ct_req *fc_ct_hdr_fill(const struct fc_frame *fp, * @r_ctl: pointer to FC header R_CTL. * @fh_type: pointer to FC-4 type. */ -static inline int fc_ct_fill(struct fc_lport *lport, +static inline int fc_ct_ns_fill(struct fc_lport *lport, u32 fc_id, struct fc_frame *fp, unsigned int op, enum fc_rctl *r_ctl, enum fc_fh_type *fh_type) @@ -131,23 +133,28 @@ static inline int fc_ct_fill(struct fc_lport *lport, switch (op) { case FC_NS_GPN_FT: - ct = fc_ct_hdr_fill(fp, op, sizeof(struct fc_ns_gid_ft)); + ct = fc_ct_hdr_fill(fp, op, sizeof(struct fc_ns_gid_ft), + FC_FST_DIR, FC_NS_SUBTYPE); ct->payload.gid.fn_fc4_type = FC_TYPE_FCP; break; case FC_NS_GPN_ID: - ct = fc_ct_hdr_fill(fp, op, sizeof(struct fc_ns_fid)); + ct = fc_ct_hdr_fill(fp, op, sizeof(struct fc_ns_fid), + FC_FST_DIR, FC_NS_SUBTYPE); + ct->payload.gid.fn_fc4_type = FC_TYPE_FCP; hton24(ct->payload.fid.fp_fid, fc_id); break; case FC_NS_RFT_ID: - ct = fc_ct_hdr_fill(fp, op, sizeof(struct fc_ns_rft)); + ct = fc_ct_hdr_fill(fp, op, sizeof(struct fc_ns_rft), + FC_FST_DIR, FC_NS_SUBTYPE); hton24(ct->payload.rft.fid.fp_fid, lport->port_id); ct->payload.rft.fts = lport->fcts; break; case FC_NS_RFF_ID: - ct = fc_ct_hdr_fill(fp, op, sizeof(struct fc_ns_rff_id)); + ct = fc_ct_hdr_fill(fp, op, sizeof(struct fc_ns_rff_id), + FC_FST_DIR, FC_NS_SUBTYPE); hton24(ct->payload.rff.fr_fid.fp_fid, lport->port_id); ct->payload.rff.fr_type = FC_TYPE_FCP; if (lport->service_params & FCP_SPPF_INIT_FCN) @@ -157,14 +164,16 @@ static inline int fc_ct_fill(struct fc_lport *lport, break; case FC_NS_RNN_ID: - ct = fc_ct_hdr_fill(fp, op, sizeof(struct fc_ns_rn_id)); + ct = fc_ct_hdr_fill(fp, op, sizeof(struct fc_ns_rn_id), + FC_FST_DIR, FC_NS_SUBTYPE); hton24(ct->payload.rn.fr_fid.fp_fid, lport->port_id); put_unaligned_be64(lport->wwnn, &ct->payload.rn.fr_wwn); break; case FC_NS_RSPN_ID: len = strnlen(fc_host_symbolic_name(lport->host), 255); - ct = fc_ct_hdr_fill(fp, op, sizeof(struct fc_ns_rspn) + len); + ct = fc_ct_hdr_fill(fp, op, sizeof(struct fc_ns_rspn) + len, + FC_FST_DIR, FC_NS_SUBTYPE); hton24(ct->payload.spn.fr_fid.fp_fid, lport->port_id); strncpy(ct->payload.spn.fr_name, fc_host_symbolic_name(lport->host), len); @@ -173,7 +182,8 @@ static inline int fc_ct_fill(struct fc_lport *lport, case FC_NS_RSNN_NN: len = strnlen(fc_host_symbolic_name(lport->host), 255); - ct = fc_ct_hdr_fill(fp, op, sizeof(struct fc_ns_rsnn) + len); + ct = fc_ct_hdr_fill(fp, op, sizeof(struct fc_ns_rsnn) + len, + FC_FST_DIR, FC_NS_SUBTYPE); put_unaligned_be64(lport->wwnn, &ct->payload.snn.fr_wwn); strncpy(ct->payload.snn.fr_name, fc_host_symbolic_name(lport->host), len); @@ -188,6 +198,32 @@ static inline int fc_ct_fill(struct fc_lport *lport, return 0; } +/** + * fc_ct_fill() - Fill in a common transport service request frame + * @lport: local port. + * @fc_id: FC_ID of non-destination rport for GPN_ID and similar inquiries. + * @fp: frame to contain payload. + * @op: CT opcode. + * @r_ctl: pointer to FC header R_CTL. + * @fh_type: pointer to FC-4 type. + */ +static inline int fc_ct_fill(struct fc_lport *lport, + u32 fc_id, struct fc_frame *fp, + unsigned int op, enum fc_rctl *r_ctl, + enum fc_fh_type *fh_type, u32 *did) +{ + int rc = -EINVAL; + + switch (fc_id) { + case FC_FID_DIR_SERV: + default: + rc = fc_ct_ns_fill(lport, fc_id, fp, op, r_ctl, fh_type); + *did = FC_FID_DIR_SERV; + break; + } + + return rc; +} /** * fc_plogi_fill - Fill in plogi request frame */ -- cgit v1.2.3 From d78c317f6cd701bda9f6dbfbfbcba72f39dd6ad7 Mon Sep 17 00:00:00 2001 From: Neerav Parikh Date: Sun, 22 Jan 2012 17:30:05 -0800 Subject: [SCSI] libfc: Add support for FDMI This patch adds support for Fabric Device Management Interface as per FC-GS-4 spec. in libfc. Any driver making use of libfc can enable fdmi state machine for a given lport. If lport has enabled FDMI support the lport state machine will transition into FDMI after completing the DNS states and before entering the SCR state. The FDMI state transition is such that if there is an error, it won't stop the lport state machine from transitioning and the it will behave as if there was no FDMI support. The FDMI HBA attributes are registed with the Management server via Register HBA (RHBA) command and the port attributes are reigstered using the Register Port(RPA) command. Signed-off-by: Neerav Parikh Tested-by: Ross Brattain Acked-by: Robert Love Signed-off-by: James Bottomley --- drivers/scsi/libfc/fc_lport.c | 227 ++++++++++++++++++++++++++++++- include/scsi/fc/fc_ms.h | 213 +++++++++++++++++++++++++++++ include/scsi/fc_encode.h | 303 ++++++++++++++++++++++++++++++++++++++++++ include/scsi/libfc.h | 11 ++ 4 files changed, 751 insertions(+), 3 deletions(-) create mode 100644 include/scsi/fc/fc_ms.h (limited to 'include/scsi') diff --git a/drivers/scsi/libfc/fc_lport.c b/drivers/scsi/libfc/fc_lport.c index 83750ebb527f..9a0b2a9caad6 100644 --- a/drivers/scsi/libfc/fc_lport.c +++ b/drivers/scsi/libfc/fc_lport.c @@ -116,6 +116,8 @@ static void fc_lport_enter_ns(struct fc_lport *, enum fc_lport_state); static void fc_lport_enter_scr(struct fc_lport *); static void fc_lport_enter_ready(struct fc_lport *); static void fc_lport_enter_logo(struct fc_lport *); +static void fc_lport_enter_fdmi(struct fc_lport *lport); +static void fc_lport_enter_ms(struct fc_lport *, enum fc_lport_state); static const char *fc_lport_state_names[] = { [LPORT_ST_DISABLED] = "disabled", @@ -126,6 +128,11 @@ static const char *fc_lport_state_names[] = { [LPORT_ST_RSPN_ID] = "RSPN_ID", [LPORT_ST_RFT_ID] = "RFT_ID", [LPORT_ST_RFF_ID] = "RFF_ID", + [LPORT_ST_FDMI] = "FDMI", + [LPORT_ST_RHBA] = "RHBA", + [LPORT_ST_RPA] = "RPA", + [LPORT_ST_DHBA] = "DHBA", + [LPORT_ST_DPRT] = "DPRT", [LPORT_ST_SCR] = "SCR", [LPORT_ST_READY] = "Ready", [LPORT_ST_LOGO] = "LOGO", @@ -183,11 +190,14 @@ static void fc_lport_rport_callback(struct fc_lport *lport, if (lport->state == LPORT_ST_DNS) { lport->dns_rdata = rdata; fc_lport_enter_ns(lport, LPORT_ST_RNN_ID); + } else if (lport->state == LPORT_ST_FDMI) { + lport->ms_rdata = rdata; + fc_lport_enter_ms(lport, LPORT_ST_DHBA); } else { FC_LPORT_DBG(lport, "Received an READY event " "on port (%6.6x) for the directory " "server, but the lport is not " - "in the DNS state, it's in the " + "in the DNS or FDMI state, it's in the " "%d state", rdata->ids.port_id, lport->state); lport->tt.rport_logoff(rdata); @@ -196,7 +206,10 @@ static void fc_lport_rport_callback(struct fc_lport *lport, case RPORT_EV_LOGO: case RPORT_EV_FAILED: case RPORT_EV_STOP: - lport->dns_rdata = NULL; + if (rdata->ids.port_id == FC_FID_DIR_SERV) + lport->dns_rdata = NULL; + else if (rdata->ids.port_id == FC_FID_MGMT_SERV) + lport->ms_rdata = NULL; break; case RPORT_EV_NONE: break; @@ -1148,7 +1161,10 @@ static void fc_lport_ns_resp(struct fc_seq *sp, struct fc_frame *fp, fc_lport_enter_ns(lport, LPORT_ST_RFF_ID); break; case LPORT_ST_RFF_ID: - fc_lport_enter_scr(lport); + if (lport->fdmi_enabled) + fc_lport_enter_fdmi(lport); + else + fc_lport_enter_scr(lport); break; default: /* should have already been caught by state checks */ @@ -1162,6 +1178,85 @@ err: mutex_unlock(&lport->lp_mutex); } +/** + * fc_lport_ms_resp() - Handle response to a management server + * exchange + * @sp: current sequence in exchange + * @fp: response frame + * @lp_arg: Fibre Channel host port instance + * + * Locking Note: This function will be called without the lport lock + * held, but it will lock, call an _enter_* function or fc_lport_error() + * and then unlock the lport. + */ +static void fc_lport_ms_resp(struct fc_seq *sp, struct fc_frame *fp, + void *lp_arg) +{ + struct fc_lport *lport = lp_arg; + struct fc_frame_header *fh; + struct fc_ct_hdr *ct; + + FC_LPORT_DBG(lport, "Received a ms %s\n", fc_els_resp_type(fp)); + + if (fp == ERR_PTR(-FC_EX_CLOSED)) + return; + + mutex_lock(&lport->lp_mutex); + + if (lport->state < LPORT_ST_RHBA || lport->state > LPORT_ST_DPRT) { + FC_LPORT_DBG(lport, "Received a management server response, " + "but in state %s\n", fc_lport_state(lport)); + if (IS_ERR(fp)) + goto err; + goto out; + } + + if (IS_ERR(fp)) { + fc_lport_error(lport, fp); + goto err; + } + + fh = fc_frame_header_get(fp); + ct = fc_frame_payload_get(fp, sizeof(*ct)); + + if (fh && ct && fh->fh_type == FC_TYPE_CT && + ct->ct_fs_type == FC_FST_MGMT && + ct->ct_fs_subtype == FC_FDMI_SUBTYPE) { + FC_LPORT_DBG(lport, "Received a management server response, " + "reason=%d explain=%d\n", + ct->ct_reason, + ct->ct_explan); + + switch (lport->state) { + case LPORT_ST_RHBA: + if (ntohs(ct->ct_cmd) == FC_FS_ACC) + fc_lport_enter_ms(lport, LPORT_ST_RPA); + else /* Error Skip RPA */ + fc_lport_enter_scr(lport); + break; + case LPORT_ST_RPA: + fc_lport_enter_scr(lport); + break; + case LPORT_ST_DPRT: + fc_lport_enter_ms(lport, LPORT_ST_RHBA); + break; + case LPORT_ST_DHBA: + fc_lport_enter_ms(lport, LPORT_ST_DPRT); + break; + default: + /* should have already been caught by state checks */ + break; + } + } else { + /* Invalid Frame? */ + fc_lport_error(lport, fp); + } +out: + fc_frame_free(fp); +err: + mutex_unlock(&lport->lp_mutex); +} + /** * fc_lport_scr_resp() - Handle response to State Change Register (SCR) request * @sp: current sequence in SCR exchange @@ -1338,6 +1433,123 @@ err: fc_lport_error(lport, NULL); } +/** + * fc_lport_enter_ms() - management server commands + * @lport: Fibre Channel local port to register + * + * Locking Note: The lport lock is expected to be held before calling + * this routine. + */ +static void fc_lport_enter_ms(struct fc_lport *lport, enum fc_lport_state state) +{ + struct fc_frame *fp; + enum fc_fdmi_req cmd; + int size = sizeof(struct fc_ct_hdr); + size_t len; + int numattrs; + + FC_LPORT_DBG(lport, "Entered %s state from %s state\n", + fc_lport_state_names[state], + fc_lport_state(lport)); + + fc_lport_state_enter(lport, state); + + switch (state) { + case LPORT_ST_RHBA: + cmd = FC_FDMI_RHBA; + /* Number of HBA Attributes */ + numattrs = 10; + len = sizeof(struct fc_fdmi_rhba); + len -= sizeof(struct fc_fdmi_attr_entry); + len += (numattrs * FC_FDMI_ATTR_ENTRY_HEADER_LEN); + len += FC_FDMI_HBA_ATTR_NODENAME_LEN; + len += FC_FDMI_HBA_ATTR_MANUFACTURER_LEN; + len += FC_FDMI_HBA_ATTR_SERIALNUMBER_LEN; + len += FC_FDMI_HBA_ATTR_MODEL_LEN; + len += FC_FDMI_HBA_ATTR_MODELDESCR_LEN; + len += FC_FDMI_HBA_ATTR_HARDWAREVERSION_LEN; + len += FC_FDMI_HBA_ATTR_DRIVERVERSION_LEN; + len += FC_FDMI_HBA_ATTR_OPTIONROMVERSION_LEN; + len += FC_FDMI_HBA_ATTR_FIRMWAREVERSION_LEN; + len += FC_FDMI_HBA_ATTR_OSNAMEVERSION_LEN; + + size += len; + break; + case LPORT_ST_RPA: + cmd = FC_FDMI_RPA; + /* Number of Port Attributes */ + numattrs = 6; + len = sizeof(struct fc_fdmi_rpa); + len -= sizeof(struct fc_fdmi_attr_entry); + len += (numattrs * FC_FDMI_ATTR_ENTRY_HEADER_LEN); + len += FC_FDMI_PORT_ATTR_FC4TYPES_LEN; + len += FC_FDMI_PORT_ATTR_SUPPORTEDSPEED_LEN; + len += FC_FDMI_PORT_ATTR_CURRENTPORTSPEED_LEN; + len += FC_FDMI_PORT_ATTR_MAXFRAMESIZE_LEN; + len += FC_FDMI_PORT_ATTR_OSDEVICENAME_LEN; + len += FC_FDMI_PORT_ATTR_HOSTNAME_LEN; + + size += len; + break; + case LPORT_ST_DPRT: + cmd = FC_FDMI_DPRT; + len = sizeof(struct fc_fdmi_dprt); + size += len; + break; + case LPORT_ST_DHBA: + cmd = FC_FDMI_DHBA; + len = sizeof(struct fc_fdmi_dhba); + size += len; + break; + default: + fc_lport_error(lport, NULL); + return; + } + + FC_LPORT_DBG(lport, "Cmd=0x%x Len %d size %d\n", + cmd, (int)len, size); + fp = fc_frame_alloc(lport, size); + if (!fp) { + fc_lport_error(lport, fp); + return; + } + + if (!lport->tt.elsct_send(lport, FC_FID_MGMT_SERV, fp, cmd, + fc_lport_ms_resp, + lport, 3 * lport->r_a_tov)) + fc_lport_error(lport, fp); +} + +/** + * fc_rport_enter_fdmi() - Create a fc_rport for the management server + * @lport: The local port requesting a remote port for the management server + * + * Locking Note: The lport lock is expected to be held before calling + * this routine. + */ +static void fc_lport_enter_fdmi(struct fc_lport *lport) +{ + struct fc_rport_priv *rdata; + + FC_LPORT_DBG(lport, "Entered FDMI state from %s state\n", + fc_lport_state(lport)); + + fc_lport_state_enter(lport, LPORT_ST_FDMI); + + mutex_lock(&lport->disc.disc_mutex); + rdata = lport->tt.rport_create(lport, FC_FID_MGMT_SERV); + mutex_unlock(&lport->disc.disc_mutex); + if (!rdata) + goto err; + + rdata->ops = &fc_lport_rport_ops; + lport->tt.rport_login(rdata); + return; + +err: + fc_lport_error(lport, NULL); +} + /** * fc_lport_timeout() - Handler for the retry_work timer * @work: The work struct of the local port @@ -1371,6 +1583,15 @@ static void fc_lport_timeout(struct work_struct *work) case LPORT_ST_RFF_ID: fc_lport_enter_ns(lport, lport->state); break; + case LPORT_ST_FDMI: + fc_lport_enter_fdmi(lport); + break; + case LPORT_ST_RHBA: + case LPORT_ST_RPA: + case LPORT_ST_DHBA: + case LPORT_ST_DPRT: + fc_lport_enter_ms(lport, lport->state); + break; case LPORT_ST_SCR: fc_lport_enter_scr(lport); break; diff --git a/include/scsi/fc/fc_ms.h b/include/scsi/fc/fc_ms.h new file mode 100644 index 000000000000..f52b921b5c70 --- /dev/null +++ b/include/scsi/fc/fc_ms.h @@ -0,0 +1,213 @@ +/* * Copyright(c) 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + * Maintained at www.Open-FCoE.org + */ + +#ifndef _FC_MS_H_ +#define _FC_MS_H_ + +#include + +/* + * Fibre Channel Services - Management Service (MS) + * From T11.org FC-GS-4 Rev 7.91 February 4, 2004 + */ + +/* + * Fabric Device Management Interface + */ + +/* + * Common-transport sub-type for FDMI + */ +#define FC_FDMI_SUBTYPE 0x10 /* fs_ct_hdr.ct_fs_subtype */ + +/* + * Management server FDMI Requests. + */ +enum fc_fdmi_req { + FC_FDMI_GRHL = 0x0100, /* Get Registered HBA List */ + FC_FDMI_GHAT = 0x0101, /* Get HBA Attributes */ + FC_FDMI_GRPL = 0x0102, /* Get Registered Port List */ + FC_FDMI_GPAT = 0x0110, /* Get Port Attributes */ + FC_FDMI_RHBA = 0x0200, /* Register HBA */ + FC_FDMI_RHAT = 0x0201, /* Register HBA Attributes */ + FC_FDMI_RPRT = 0x0210, /* Register Port */ + FC_FDMI_RPA = 0x0211, /* Register Port Attributes */ + FC_FDMI_DHBA = 0x0300, /* Deregister HBA */ + FC_FDMI_DHAT = 0x0301, /* Deregister HBA Attributes */ + FC_FDMI_DPRT = 0x0310, /* Deregister Port */ + FC_FDMI_DPA = 0x0311, /* Deregister Port Attributes */ +}; + +/* + * HBA Attribute Entry Type + */ +enum fc_fdmi_hba_attr_type { + FC_FDMI_HBA_ATTR_NODENAME = 0x0001, + FC_FDMI_HBA_ATTR_MANUFACTURER = 0x0002, + FC_FDMI_HBA_ATTR_SERIALNUMBER = 0x0003, + FC_FDMI_HBA_ATTR_MODEL = 0x0004, + FC_FDMI_HBA_ATTR_MODELDESCRIPTION = 0x0005, + FC_FDMI_HBA_ATTR_HARDWAREVERSION = 0x0006, + FC_FDMI_HBA_ATTR_DRIVERVERSION = 0x0007, + FC_FDMI_HBA_ATTR_OPTIONROMVERSION = 0x0008, + FC_FDMI_HBA_ATTR_FIRMWAREVERSION = 0x0009, + FC_FDMI_HBA_ATTR_OSNAMEVERSION = 0x000A, + FC_FDMI_HBA_ATTR_MAXCTPAYLOAD = 0x000B, +}; + +/* + * HBA Attribute Length + */ +#define FC_FDMI_HBA_ATTR_NODENAME_LEN 8 +#define FC_FDMI_HBA_ATTR_MANUFACTURER_LEN 64 +#define FC_FDMI_HBA_ATTR_SERIALNUMBER_LEN 64 +#define FC_FDMI_HBA_ATTR_MODEL_LEN 256 +#define FC_FDMI_HBA_ATTR_MODELDESCR_LEN 256 +#define FC_FDMI_HBA_ATTR_HARDWAREVERSION_LEN 256 +#define FC_FDMI_HBA_ATTR_DRIVERVERSION_LEN 256 +#define FC_FDMI_HBA_ATTR_OPTIONROMVERSION_LEN 256 +#define FC_FDMI_HBA_ATTR_FIRMWAREVERSION_LEN 256 +#define FC_FDMI_HBA_ATTR_OSNAMEVERSION_LEN 256 +#define FC_FDMI_HBA_ATTR_MAXCTPAYLOAD_LEN 4 + +/* + * Port Attribute Type + */ +enum fc_fdmi_port_attr_type { + FC_FDMI_PORT_ATTR_FC4TYPES = 0x0001, + FC_FDMI_PORT_ATTR_SUPPORTEDSPEED = 0x0002, + FC_FDMI_PORT_ATTR_CURRENTPORTSPEED = 0x0003, + FC_FDMI_PORT_ATTR_MAXFRAMESIZE = 0x0004, + FC_FDMI_PORT_ATTR_OSDEVICENAME = 0x0005, + FC_FDMI_PORT_ATTR_HOSTNAME = 0x0006, +}; + +/* + * Port Attribute Length + */ +#define FC_FDMI_PORT_ATTR_FC4TYPES_LEN 32 +#define FC_FDMI_PORT_ATTR_SUPPORTEDSPEED_LEN 4 +#define FC_FDMI_PORT_ATTR_CURRENTPORTSPEED_LEN 4 +#define FC_FDMI_PORT_ATTR_MAXFRAMESIZE_LEN 4 +#define FC_FDMI_PORT_ATTR_OSDEVICENAME_LEN 256 +#define FC_FDMI_PORT_ATTR_HOSTNAME_LEN 256 + +/* + * HBA Attribute ID + */ +struct fc_fdmi_hba_identifier { + __be64 id; +}; + +/* + * Port Name + */ +struct fc_fdmi_port_name { + __be64 portname; +}; + +/* + * Attribute Entry Block for HBA/Port Attributes + */ +#define FC_FDMI_ATTR_ENTRY_HEADER_LEN 4 +struct fc_fdmi_attr_entry { + __be16 type; + __be16 len; + __u8 value[1]; +} __attribute__((__packed__)); + +/* + * Common for HBA/Port Attributes + */ +struct fs_fdmi_attrs { + __be32 numattrs; + struct fc_fdmi_attr_entry attr[1]; +} __attribute__((__packed__)); + +/* + * Registered Port List + */ +struct fc_fdmi_rpl { + __be32 numport; + struct fc_fdmi_port_name port[1]; +} __attribute__((__packed__)); + +/* + * Register HBA (RHBA) + */ +struct fc_fdmi_rhba { + struct fc_fdmi_hba_identifier hbaid; + struct fc_fdmi_rpl port; + struct fs_fdmi_attrs hba_attrs; +} __attribute__((__packed__)); + +/* + * Register HBA Attributes (RHAT) + */ +struct fc_fdmi_rhat { + struct fc_fdmi_hba_identifier hbaid; + struct fs_fdmi_attrs hba_attrs; +} __attribute__((__packed__)); + +/* + * Register Port (RPRT) + */ +struct fc_fdmi_rprt { + struct fc_fdmi_hba_identifier hbaid; + struct fc_fdmi_port_name port; + struct fs_fdmi_attrs hba_attrs; +} __attribute__((__packed__)); + +/* + * Register Port Attributes (RPA) + */ +struct fc_fdmi_rpa { + struct fc_fdmi_port_name port; + struct fs_fdmi_attrs hba_attrs; +} __attribute__((__packed__)); + +/* + * Deregister Port (DPRT) + */ +struct fc_fdmi_dprt { + struct fc_fdmi_port_name port; +} __attribute__((__packed__)); + +/* + * Deregister Port Attributes (DPA) + */ +struct fc_fdmi_dpa { + struct fc_fdmi_port_name port; + struct fs_fdmi_attrs hba_attrs; +} __attribute__((__packed__)); + +/* + * Deregister HBA Attributes (DHAT) + */ +struct fc_fdmi_dhat { + struct fc_fdmi_hba_identifier hbaid; +} __attribute__((__packed__)); + +/* + * Deregister HBA (DHBA) + */ +struct fc_fdmi_dhba { + struct fc_fdmi_hba_identifier hbaid; +} __attribute__((__packed__)); + +#endif /* _FC_MS_H_ */ diff --git a/include/scsi/fc_encode.h b/include/scsi/fc_encode.h index 73bc43329f86..35fd4744f3e9 100644 --- a/include/scsi/fc_encode.h +++ b/include/scsi/fc_encode.h @@ -20,6 +20,7 @@ #ifndef _FC_ENCODE_H_ #define _FC_ENCODE_H_ #include +#include /* * F_CTL values for simple requests and responses. @@ -43,6 +44,10 @@ struct fc_ct_req { struct fc_ns_fid fid; struct fc_ns_rsnn snn; struct fc_ns_rspn spn; + struct fc_fdmi_rhba rhba; + struct fc_fdmi_rpa rpa; + struct fc_fdmi_dprt dprt; + struct fc_fdmi_dhba dhba; } payload; }; @@ -198,6 +203,300 @@ static inline int fc_ct_ns_fill(struct fc_lport *lport, return 0; } +/** + * fc_ct_ms_fill() - Fill in a mgmt service request frame + * @lport: local port. + * @fc_id: FC_ID of non-destination rport for GPN_ID and similar inquiries. + * @fp: frame to contain payload. + * @op: CT opcode. + * @r_ctl: pointer to FC header R_CTL. + * @fh_type: pointer to FC-4 type. + */ +static inline int fc_ct_ms_fill(struct fc_lport *lport, + u32 fc_id, struct fc_frame *fp, + unsigned int op, enum fc_rctl *r_ctl, + enum fc_fh_type *fh_type) +{ + struct fc_ct_req *ct; + size_t len; + struct fc_fdmi_attr_entry *entry; + struct fs_fdmi_attrs *hba_attrs; + int numattrs = 0; + + switch (op) { + case FC_FDMI_RHBA: + numattrs = 10; + len = sizeof(struct fc_fdmi_rhba); + len -= sizeof(struct fc_fdmi_attr_entry); + len += (numattrs * FC_FDMI_ATTR_ENTRY_HEADER_LEN); + len += FC_FDMI_HBA_ATTR_NODENAME_LEN; + len += FC_FDMI_HBA_ATTR_MANUFACTURER_LEN; + len += FC_FDMI_HBA_ATTR_SERIALNUMBER_LEN; + len += FC_FDMI_HBA_ATTR_MODEL_LEN; + len += FC_FDMI_HBA_ATTR_MODELDESCR_LEN; + len += FC_FDMI_HBA_ATTR_HARDWAREVERSION_LEN; + len += FC_FDMI_HBA_ATTR_DRIVERVERSION_LEN; + len += FC_FDMI_HBA_ATTR_OPTIONROMVERSION_LEN; + len += FC_FDMI_HBA_ATTR_FIRMWAREVERSION_LEN; + len += FC_FDMI_HBA_ATTR_OSNAMEVERSION_LEN; + ct = fc_ct_hdr_fill(fp, op, len, FC_FST_MGMT, + FC_FDMI_SUBTYPE); + + /* HBA Identifier */ + put_unaligned_be64(lport->wwpn, &ct->payload.rhba.hbaid.id); + /* Number of Ports - always 1 */ + put_unaligned_be32(1, &ct->payload.rhba.port.numport); + /* Port Name */ + put_unaligned_be64(lport->wwpn, + &ct->payload.rhba.port.port[0].portname); + + /* HBA Attributes */ + put_unaligned_be32(numattrs, + &ct->payload.rhba.hba_attrs.numattrs); + hba_attrs = &ct->payload.rhba.hba_attrs; + entry = (struct fc_fdmi_attr_entry *)hba_attrs->attr; + /* NodeName*/ + len = FC_FDMI_ATTR_ENTRY_HEADER_LEN; + len += FC_FDMI_HBA_ATTR_NODENAME_LEN; + put_unaligned_be16(FC_FDMI_HBA_ATTR_NODENAME, + &entry->type); + put_unaligned_be16(len, &entry->len); + put_unaligned_be64(lport->wwnn, + (__be64 *)&entry->value[0]); + + /* Manufacturer */ + entry = (struct fc_fdmi_attr_entry *)((char *)entry->value + + FC_FDMI_HBA_ATTR_NODENAME_LEN); + len = FC_FDMI_ATTR_ENTRY_HEADER_LEN; + len += FC_FDMI_HBA_ATTR_MANUFACTURER_LEN; + put_unaligned_be16(FC_FDMI_HBA_ATTR_MANUFACTURER, + &entry->type); + put_unaligned_be16(len, &entry->len); + strncpy((char *)&entry->value, + fc_host_manufacturer(lport->host), + FC_FDMI_HBA_ATTR_MANUFACTURER_LEN); + + /* SerialNumber */ + entry = (struct fc_fdmi_attr_entry *)((char *)entry->value + + FC_FDMI_HBA_ATTR_MANUFACTURER_LEN); + len = FC_FDMI_ATTR_ENTRY_HEADER_LEN; + len += FC_FDMI_HBA_ATTR_SERIALNUMBER_LEN; + put_unaligned_be16(FC_FDMI_HBA_ATTR_SERIALNUMBER, + &entry->type); + put_unaligned_be16(len, &entry->len); + strncpy((char *)&entry->value, + fc_host_serial_number(lport->host), + FC_FDMI_HBA_ATTR_SERIALNUMBER_LEN); + + /* Model */ + entry = (struct fc_fdmi_attr_entry *)((char *)entry->value + + FC_FDMI_HBA_ATTR_SERIALNUMBER_LEN); + len = FC_FDMI_ATTR_ENTRY_HEADER_LEN; + len += FC_FDMI_HBA_ATTR_MODEL_LEN; + put_unaligned_be16(FC_FDMI_HBA_ATTR_MODEL, + &entry->type); + put_unaligned_be16(len, &entry->len); + strncpy((char *)&entry->value, + fc_host_model(lport->host), + FC_FDMI_HBA_ATTR_MODEL_LEN); + + /* Model Description */ + entry = (struct fc_fdmi_attr_entry *)((char *)entry->value + + FC_FDMI_HBA_ATTR_MODEL_LEN); + len = FC_FDMI_ATTR_ENTRY_HEADER_LEN; + len += FC_FDMI_HBA_ATTR_MODELDESCR_LEN; + put_unaligned_be16(FC_FDMI_HBA_ATTR_MODELDESCRIPTION, + &entry->type); + put_unaligned_be16(len, &entry->len); + strncpy((char *)&entry->value, + fc_host_model_description(lport->host), + FC_FDMI_HBA_ATTR_MODELDESCR_LEN); + + /* Hardware Version */ + entry = (struct fc_fdmi_attr_entry *)((char *)entry->value + + FC_FDMI_HBA_ATTR_MODELDESCR_LEN); + len = FC_FDMI_ATTR_ENTRY_HEADER_LEN; + len += FC_FDMI_HBA_ATTR_HARDWAREVERSION_LEN; + put_unaligned_be16(FC_FDMI_HBA_ATTR_HARDWAREVERSION, + &entry->type); + put_unaligned_be16(len, &entry->len); + strncpy((char *)&entry->value, + fc_host_hardware_version(lport->host), + FC_FDMI_HBA_ATTR_HARDWAREVERSION_LEN); + + /* Driver Version */ + entry = (struct fc_fdmi_attr_entry *)((char *)entry->value + + FC_FDMI_HBA_ATTR_HARDWAREVERSION_LEN); + len = FC_FDMI_ATTR_ENTRY_HEADER_LEN; + len += FC_FDMI_HBA_ATTR_DRIVERVERSION_LEN; + put_unaligned_be16(FC_FDMI_HBA_ATTR_DRIVERVERSION, + &entry->type); + put_unaligned_be16(len, &entry->len); + strncpy((char *)&entry->value, + fc_host_driver_version(lport->host), + FC_FDMI_HBA_ATTR_DRIVERVERSION_LEN); + + /* OptionROM Version */ + entry = (struct fc_fdmi_attr_entry *)((char *)entry->value + + FC_FDMI_HBA_ATTR_DRIVERVERSION_LEN); + len = FC_FDMI_ATTR_ENTRY_HEADER_LEN; + len += FC_FDMI_HBA_ATTR_OPTIONROMVERSION_LEN; + put_unaligned_be16(FC_FDMI_HBA_ATTR_OPTIONROMVERSION, + &entry->type); + put_unaligned_be16(len, &entry->len); + strncpy((char *)&entry->value, + fc_host_optionrom_version(lport->host), + FC_FDMI_HBA_ATTR_OPTIONROMVERSION_LEN); + + /* Firmware Version */ + entry = (struct fc_fdmi_attr_entry *)((char *)entry->value + + FC_FDMI_HBA_ATTR_OPTIONROMVERSION_LEN); + len = FC_FDMI_ATTR_ENTRY_HEADER_LEN; + len += FC_FDMI_HBA_ATTR_FIRMWAREVERSION_LEN; + put_unaligned_be16(FC_FDMI_HBA_ATTR_FIRMWAREVERSION, + &entry->type); + put_unaligned_be16(len, &entry->len); + strncpy((char *)&entry->value, + fc_host_firmware_version(lport->host), + FC_FDMI_HBA_ATTR_FIRMWAREVERSION_LEN); + + /* OS Name and Version */ + entry = (struct fc_fdmi_attr_entry *)((char *)entry->value + + FC_FDMI_HBA_ATTR_FIRMWAREVERSION_LEN); + len = FC_FDMI_ATTR_ENTRY_HEADER_LEN; + len += FC_FDMI_HBA_ATTR_OSNAMEVERSION_LEN; + put_unaligned_be16(FC_FDMI_HBA_ATTR_OSNAMEVERSION, + &entry->type); + put_unaligned_be16(len, &entry->len); + snprintf((char *)&entry->value, + FC_FDMI_HBA_ATTR_OSNAMEVERSION_LEN, + "%s v%s", + init_utsname()->sysname, + init_utsname()->release); + break; + case FC_FDMI_RPA: + numattrs = 6; + len = sizeof(struct fc_fdmi_rpa); + len -= sizeof(struct fc_fdmi_attr_entry); + len += (numattrs * FC_FDMI_ATTR_ENTRY_HEADER_LEN); + len += FC_FDMI_PORT_ATTR_FC4TYPES_LEN; + len += FC_FDMI_PORT_ATTR_SUPPORTEDSPEED_LEN; + len += FC_FDMI_PORT_ATTR_CURRENTPORTSPEED_LEN; + len += FC_FDMI_PORT_ATTR_MAXFRAMESIZE_LEN; + len += FC_FDMI_PORT_ATTR_OSDEVICENAME_LEN; + len += FC_FDMI_PORT_ATTR_HOSTNAME_LEN; + ct = fc_ct_hdr_fill(fp, op, len, FC_FST_MGMT, + FC_FDMI_SUBTYPE); + + /* Port Name */ + put_unaligned_be64(lport->wwpn, + &ct->payload.rpa.port.portname); + + /* Port Attributes */ + put_unaligned_be32(numattrs, + &ct->payload.rpa.hba_attrs.numattrs); + + hba_attrs = &ct->payload.rpa.hba_attrs; + entry = (struct fc_fdmi_attr_entry *)hba_attrs->attr; + + /* FC4 types */ + len = FC_FDMI_ATTR_ENTRY_HEADER_LEN; + len += FC_FDMI_PORT_ATTR_FC4TYPES_LEN; + put_unaligned_be16(FC_FDMI_PORT_ATTR_FC4TYPES, + &entry->type); + put_unaligned_be16(len, &entry->len); + memcpy(&entry->value, fc_host_supported_fc4s(lport->host), + FC_FDMI_PORT_ATTR_FC4TYPES_LEN); + + /* Supported Speed */ + entry = (struct fc_fdmi_attr_entry *)((char *)entry->value + + FC_FDMI_PORT_ATTR_FC4TYPES_LEN); + len = FC_FDMI_ATTR_ENTRY_HEADER_LEN; + len += FC_FDMI_PORT_ATTR_SUPPORTEDSPEED_LEN; + put_unaligned_be16(FC_FDMI_PORT_ATTR_SUPPORTEDSPEED, + &entry->type); + put_unaligned_be16(len, &entry->len); + + put_unaligned_be32(fc_host_supported_speeds(lport->host), + &entry->value); + + /* Current Port Speed */ + entry = (struct fc_fdmi_attr_entry *)((char *)entry->value + + FC_FDMI_PORT_ATTR_SUPPORTEDSPEED_LEN); + len = FC_FDMI_ATTR_ENTRY_HEADER_LEN; + len += FC_FDMI_PORT_ATTR_CURRENTPORTSPEED_LEN; + put_unaligned_be16(FC_FDMI_PORT_ATTR_CURRENTPORTSPEED, + &entry->type); + put_unaligned_be16(len, &entry->len); + put_unaligned_be32(lport->link_speed, + &entry->value); + + /* Max Frame Size */ + entry = (struct fc_fdmi_attr_entry *)((char *)entry->value + + FC_FDMI_PORT_ATTR_CURRENTPORTSPEED_LEN); + len = FC_FDMI_ATTR_ENTRY_HEADER_LEN; + len += FC_FDMI_PORT_ATTR_MAXFRAMESIZE_LEN; + put_unaligned_be16(FC_FDMI_PORT_ATTR_MAXFRAMESIZE, + &entry->type); + put_unaligned_be16(len, &entry->len); + put_unaligned_be32(fc_host_maxframe_size(lport->host), + &entry->value); + + /* OS Device Name */ + entry = (struct fc_fdmi_attr_entry *)((char *)entry->value + + FC_FDMI_PORT_ATTR_MAXFRAMESIZE_LEN); + len = FC_FDMI_ATTR_ENTRY_HEADER_LEN; + len += FC_FDMI_PORT_ATTR_OSDEVICENAME_LEN; + put_unaligned_be16(FC_FDMI_PORT_ATTR_OSDEVICENAME, + &entry->type); + put_unaligned_be16(len, &entry->len); + /* Use the sysfs device name */ + strncpy((char *)&entry->value, + dev_name(&lport->host->shost_gendev), + strnlen(dev_name(&lport->host->shost_gendev), + FC_FDMI_PORT_ATTR_HOSTNAME_LEN)); + + /* Host Name */ + entry = (struct fc_fdmi_attr_entry *)((char *)entry->value + + FC_FDMI_PORT_ATTR_OSDEVICENAME_LEN); + len = FC_FDMI_ATTR_ENTRY_HEADER_LEN; + len += FC_FDMI_PORT_ATTR_HOSTNAME_LEN; + put_unaligned_be16(FC_FDMI_PORT_ATTR_HOSTNAME, + &entry->type); + put_unaligned_be16(len, &entry->len); + if (strlen(fc_host_system_hostname(lport->host))) + strncpy((char *)&entry->value, + fc_host_system_hostname(lport->host), + strnlen(fc_host_system_hostname(lport->host), + FC_FDMI_PORT_ATTR_HOSTNAME_LEN)); + else + strncpy((char *)&entry->value, + init_utsname()->nodename, + FC_FDMI_PORT_ATTR_HOSTNAME_LEN); + break; + case FC_FDMI_DPRT: + len = sizeof(struct fc_fdmi_dprt); + ct = fc_ct_hdr_fill(fp, op, len, FC_FST_MGMT, + FC_FDMI_SUBTYPE); + /* Port Name */ + put_unaligned_be64(lport->wwpn, + &ct->payload.dprt.port.portname); + break; + case FC_FDMI_DHBA: + len = sizeof(struct fc_fdmi_dhba); + ct = fc_ct_hdr_fill(fp, op, len, FC_FST_MGMT, + FC_FDMI_SUBTYPE); + /* HBA Identifier */ + put_unaligned_be64(lport->wwpn, &ct->payload.dhba.hbaid.id); + break; + default: + return -EINVAL; + } + *r_ctl = FC_RCTL_DD_UNSOL_CTL; + *fh_type = FC_TYPE_CT; + return 0; +} + /** * fc_ct_fill() - Fill in a common transport service request frame * @lport: local port. @@ -215,6 +514,10 @@ static inline int fc_ct_fill(struct fc_lport *lport, int rc = -EINVAL; switch (fc_id) { + case FC_FID_MGMT_SERV: + rc = fc_ct_ms_fill(lport, fc_id, fp, op, r_ctl, fh_type); + *did = FC_FID_MGMT_SERV; + break; case FC_FID_DIR_SERV: default: rc = fc_ct_ns_fill(lport, fc_id, fp, op, r_ctl, fh_type); diff --git a/include/scsi/libfc.h b/include/scsi/libfc.h index 6a3922fe0be0..8f9dfba3fcf0 100644 --- a/include/scsi/libfc.h +++ b/include/scsi/libfc.h @@ -30,6 +30,7 @@ #include #include +#include #include #include @@ -52,6 +53,8 @@ * @LPORT_ST_RPN_ID: Register port name by ID (RPN_ID) sent * @LPORT_ST_RFT_ID: Register Fibre Channel types by ID (RFT_ID) sent * @LPORT_ST_RFF_ID: Register FC-4 Features by ID (RFF_ID) sent + * @LPORT_ST_FDMI: Waiting for mgmt server rport to become ready + * @LPORT_ST_RHBA: * @LPORT_ST_SCR: State Change Register (SCR) sent * @LPORT_ST_READY: Ready for use * @LPORT_ST_LOGO: Local port logout (LOGO) sent @@ -66,6 +69,11 @@ enum fc_lport_state { LPORT_ST_RSPN_ID, LPORT_ST_RFT_ID, LPORT_ST_RFF_ID, + LPORT_ST_FDMI, + LPORT_ST_RHBA, + LPORT_ST_RPA, + LPORT_ST_DHBA, + LPORT_ST_DPRT, LPORT_ST_SCR, LPORT_ST_READY, LPORT_ST_LOGO, @@ -797,6 +805,7 @@ enum fc_lport_event { * @host: The SCSI host associated with a local port * @ema_list: Exchange manager anchor list * @dns_rdata: The directory server remote port + * @ms_rdata: The management server remote port * @ptp_rdata: Point to point remote port * @scsi_priv: FCP layer internal data * @disc: Discovery context @@ -842,6 +851,7 @@ struct fc_lport { struct Scsi_Host *host; struct list_head ema_list; struct fc_rport_priv *dns_rdata; + struct fc_rport_priv *ms_rdata; struct fc_rport_priv *ptp_rdata; void *scsi_priv; struct fc_disc disc; @@ -877,6 +887,7 @@ struct fc_lport { u32 does_npiv:1; u32 npiv_enabled:1; u32 point_to_multipoint:1; + u32 fdmi_enabled:1; u32 mfs; u8 max_retry_count; u8 max_rport_retry_count; -- cgit v1.2.3 From 3384db9eb8b1e4f94a02c2a0ce3c0efe6142f3ba Mon Sep 17 00:00:00 2001 From: Moger, Babu Date: Tue, 24 Jan 2012 20:38:42 +0000 Subject: [SCSI] Correctly set the scsi host/msg/status bytes Resubmitting as my previous post had format issues and did not go llinux-scsi. This patch changes the function to set_msg_byte, set_host_byte and set_driver_byte to correctly set the corresponding bytes appropriately. It will reset the original setting and correctly set it to the new value. The previous OR operation does not always set it back to new value. Look at patch 2/2 for an example. Signed-off-by: Babu Moger Signed-off-by: James Bottomley --- include/scsi/scsi_cmnd.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'include/scsi') diff --git a/include/scsi/scsi_cmnd.h b/include/scsi/scsi_cmnd.h index a5e885a111df..9be0128bf303 100644 --- a/include/scsi/scsi_cmnd.h +++ b/include/scsi/scsi_cmnd.h @@ -289,17 +289,17 @@ static inline struct scsi_data_buffer *scsi_prot(struct scsi_cmnd *cmd) static inline void set_msg_byte(struct scsi_cmnd *cmd, char status) { - cmd->result |= status << 8; + cmd->result = (cmd->result & 0xffff00ff) | (status << 8); } static inline void set_host_byte(struct scsi_cmnd *cmd, char status) { - cmd->result |= status << 16; + cmd->result = (cmd->result & 0xff00ffff) | (status << 16); } static inline void set_driver_byte(struct scsi_cmnd *cmd, char status) { - cmd->result |= status << 24; + cmd->result = (cmd->result & 0x00ffffff) | (status << 24); } #endif /* _SCSI_SCSI_CMND_H */ -- cgit v1.2.3 From 1304be5fe0efb42b7ec6a50dd8e1a9bce2adae17 Mon Sep 17 00:00:00 2001 From: Mike Christie Date: Thu, 26 Jan 2012 21:13:10 -0600 Subject: [SCSI] libiscsi_tcp: fix max_r2t manipulation Problem description from Xi Wang: A large max_r2t could lead to integer overflow in subsequent call to iscsi_tcp_r2tpool_alloc(), allocating a smaller buffer than expected and leading to out-of-bounds write. Signed-off-by: Mike Christie Signed-off-by: James Bottomley --- drivers/scsi/cxgbi/libcxgbi.c | 13 ++----------- drivers/scsi/iscsi_tcp.c | 13 +------------ drivers/scsi/libiscsi.c | 2 +- drivers/scsi/libiscsi_tcp.c | 18 ++++++++++++++++++ include/scsi/libiscsi.h | 2 +- include/scsi/libiscsi_tcp.h | 2 +- 6 files changed, 24 insertions(+), 26 deletions(-) (limited to 'include/scsi') diff --git a/drivers/scsi/cxgbi/libcxgbi.c b/drivers/scsi/cxgbi/libcxgbi.c index d3ff9cd40234..e6e6aa9289b8 100644 --- a/drivers/scsi/cxgbi/libcxgbi.c +++ b/drivers/scsi/cxgbi/libcxgbi.c @@ -2148,11 +2148,10 @@ int cxgbi_set_conn_param(struct iscsi_cls_conn *cls_conn, enum iscsi_param param, char *buf, int buflen) { struct iscsi_conn *conn = cls_conn->dd_data; - struct iscsi_session *session = conn->session; struct iscsi_tcp_conn *tcp_conn = conn->dd_data; struct cxgbi_conn *cconn = tcp_conn->dd_data; struct cxgbi_sock *csk = cconn->cep->csk; - int value, err = 0; + int err; log_debug(1 << CXGBI_DBG_ISCSI, "cls_conn 0x%p, param %d, buf(%d) %s.\n", @@ -2174,15 +2173,7 @@ int cxgbi_set_conn_param(struct iscsi_cls_conn *cls_conn, conn->datadgst_en, 0); break; case ISCSI_PARAM_MAX_R2T: - sscanf(buf, "%d", &value); - if (value <= 0 || !is_power_of_2(value)) - return -EINVAL; - if (session->max_r2t == value) - break; - iscsi_tcp_r2tpool_free(session); - err = iscsi_set_param(cls_conn, param, buf, buflen); - if (!err && iscsi_tcp_r2tpool_alloc(session)) - return -ENOMEM; + return iscsi_tcp_set_max_r2t(conn, buf); case ISCSI_PARAM_MAX_RECV_DLENGTH: err = iscsi_set_param(cls_conn, param, buf, buflen); if (!err) diff --git a/drivers/scsi/iscsi_tcp.c b/drivers/scsi/iscsi_tcp.c index db47158e0dde..453a740fa68e 100644 --- a/drivers/scsi/iscsi_tcp.c +++ b/drivers/scsi/iscsi_tcp.c @@ -684,10 +684,8 @@ static int iscsi_sw_tcp_conn_set_param(struct iscsi_cls_conn *cls_conn, int buflen) { struct iscsi_conn *conn = cls_conn->dd_data; - struct iscsi_session *session = conn->session; struct iscsi_tcp_conn *tcp_conn = conn->dd_data; struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data; - int value; switch(param) { case ISCSI_PARAM_HDRDGST_EN: @@ -699,16 +697,7 @@ static int iscsi_sw_tcp_conn_set_param(struct iscsi_cls_conn *cls_conn, sock_no_sendpage : tcp_sw_conn->sock->ops->sendpage; break; case ISCSI_PARAM_MAX_R2T: - sscanf(buf, "%d", &value); - if (value <= 0 || !is_power_of_2(value)) - return -EINVAL; - if (session->max_r2t == value) - break; - iscsi_tcp_r2tpool_free(session); - iscsi_set_param(cls_conn, param, buf, buflen); - if (iscsi_tcp_r2tpool_alloc(session)) - return -ENOMEM; - break; + return iscsi_tcp_set_max_r2t(conn, buf); default: return iscsi_set_param(cls_conn, param, buf, buflen); } diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c index 00592e3bb375..8582d7c25732 100644 --- a/drivers/scsi/libiscsi.c +++ b/drivers/scsi/libiscsi.c @@ -3201,7 +3201,7 @@ int iscsi_set_param(struct iscsi_cls_conn *cls_conn, sscanf(buf, "%d", &session->initial_r2t_en); break; case ISCSI_PARAM_MAX_R2T: - sscanf(buf, "%d", &session->max_r2t); + sscanf(buf, "%hu", &session->max_r2t); break; case ISCSI_PARAM_IMM_DATA_EN: sscanf(buf, "%d", &session->imm_data_en); diff --git a/drivers/scsi/libiscsi_tcp.c b/drivers/scsi/libiscsi_tcp.c index 5715a3d0a3d3..c4996b081999 100644 --- a/drivers/scsi/libiscsi_tcp.c +++ b/drivers/scsi/libiscsi_tcp.c @@ -1170,6 +1170,24 @@ void iscsi_tcp_r2tpool_free(struct iscsi_session *session) } EXPORT_SYMBOL_GPL(iscsi_tcp_r2tpool_free); +int iscsi_tcp_set_max_r2t(struct iscsi_conn *conn, char *buf) +{ + struct iscsi_session *session = conn->session; + unsigned short r2ts = 0; + + sscanf(buf, "%hu", &r2ts); + if (session->max_r2t == r2ts) + return 0; + + if (!r2ts || !is_power_of_2(r2ts)) + return -EINVAL; + + session->max_r2t = r2ts; + iscsi_tcp_r2tpool_free(session); + return iscsi_tcp_r2tpool_alloc(session); +} +EXPORT_SYMBOL_GPL(iscsi_tcp_set_max_r2t); + void iscsi_tcp_conn_get_stats(struct iscsi_cls_conn *cls_conn, struct iscsi_stats *stats) { diff --git a/include/scsi/libiscsi.h b/include/scsi/libiscsi.h index 2e42e9a0e0b6..6e33386a3898 100644 --- a/include/scsi/libiscsi.h +++ b/include/scsi/libiscsi.h @@ -268,7 +268,7 @@ struct iscsi_session { int lu_reset_timeout; int tgt_reset_timeout; int initial_r2t_en; - unsigned max_r2t; + unsigned short max_r2t; int imm_data_en; unsigned first_burst; unsigned max_burst; diff --git a/include/scsi/libiscsi_tcp.h b/include/scsi/libiscsi_tcp.h index ac0cc1d925ef..215469a9b801 100644 --- a/include/scsi/libiscsi_tcp.h +++ b/include/scsi/libiscsi_tcp.h @@ -128,7 +128,7 @@ extern void iscsi_tcp_conn_teardown(struct iscsi_cls_conn *cls_conn); /* misc helpers */ extern int iscsi_tcp_r2tpool_alloc(struct iscsi_session *session); extern void iscsi_tcp_r2tpool_free(struct iscsi_session *session); - +extern int iscsi_tcp_set_max_r2t(struct iscsi_conn *conn, char *buf); extern void iscsi_tcp_conn_get_stats(struct iscsi_cls_conn *cls_conn, struct iscsi_stats *stats); #endif /* LIBISCSI_TCP_H */ -- cgit v1.2.3 From a11e25459558421ec5c4adc3fc46fe320ab74bd3 Mon Sep 17 00:00:00 2001 From: Vikas Chaudhary Date: Mon, 13 Feb 2012 18:30:46 +0530 Subject: [SCSI] scsi_transport_iscsi: added support for host event Added support to post kernel host event to application using netlink interface. Signed-off-by: Vikas Chaudhary Reviewed-by: Mike Christie Signed-off-by: James Bottomley --- drivers/scsi/scsi_transport_iscsi.c | 31 +++++++++++++++++++++++++++++++ include/scsi/iscsi_if.h | 13 +++++++++++++ include/scsi/scsi_transport_iscsi.h | 6 ++++++ 3 files changed, 50 insertions(+) (limited to 'include/scsi') diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c index 787044828a70..38f0bf8ea91a 100644 --- a/drivers/scsi/scsi_transport_iscsi.c +++ b/drivers/scsi/scsi_transport_iscsi.c @@ -1476,6 +1476,37 @@ void iscsi_conn_login_event(struct iscsi_cls_conn *conn, } EXPORT_SYMBOL_GPL(iscsi_conn_login_event); +void iscsi_post_host_event(uint32_t host_no, struct iscsi_transport *transport, + enum iscsi_host_event_code code, uint32_t data_size, + uint8_t *data) +{ + struct nlmsghdr *nlh; + struct sk_buff *skb; + struct iscsi_uevent *ev; + int len = NLMSG_SPACE(sizeof(*ev) + data_size); + + skb = alloc_skb(len, GFP_KERNEL); + if (!skb) { + printk(KERN_ERR "gracefully ignored host event (%d):%d OOM\n", + host_no, code); + return; + } + + nlh = __nlmsg_put(skb, 0, 0, 0, (len - sizeof(*nlh)), 0); + ev = NLMSG_DATA(nlh); + ev->transport_handle = iscsi_handle(transport); + ev->type = ISCSI_KEVENT_HOST_EVENT; + ev->r.host_event.host_no = host_no; + ev->r.host_event.code = code; + ev->r.host_event.data_size = data_size; + + if (data_size) + memcpy((char *)ev + sizeof(*ev), data, data_size); + + iscsi_multicast_skb(skb, ISCSI_NL_GRP_ISCSID, GFP_KERNEL); +} +EXPORT_SYMBOL_GPL(iscsi_post_host_event); + static int iscsi_if_send_reply(uint32_t group, int seq, int type, int done, int multi, void *payload, int size) diff --git a/include/scsi/iscsi_if.h b/include/scsi/iscsi_if.h index e49b7c8dd217..3aac99155e80 100644 --- a/include/scsi/iscsi_if.h +++ b/include/scsi/iscsi_if.h @@ -72,6 +72,7 @@ enum iscsi_uevent_e { ISCSI_KEVENT_PATH_REQ = KEVENT_BASE + 7, ISCSI_KEVENT_IF_DOWN = KEVENT_BASE + 8, ISCSI_KEVENT_CONN_LOGIN_STATE = KEVENT_BASE + 9, + ISCSI_KEVENT_HOST_EVENT = KEVENT_BASE + 10, }; enum iscsi_tgt_dscvr { @@ -80,6 +81,13 @@ enum iscsi_tgt_dscvr { ISCSI_TGT_DSCVR_SLP = 3, }; +enum iscsi_host_event_code { + ISCSI_EVENT_LINKUP = 1, + ISCSI_EVENT_LINKDOWN, + /* must always be last */ + ISCSI_EVENT_MAX, +}; + struct iscsi_uevent { uint32_t type; /* k/u events type */ uint32_t iferror; /* carries interface or resource errors */ @@ -222,6 +230,11 @@ struct iscsi_uevent { struct msg_notify_if_down { uint32_t host_no; } notify_if_down; + struct msg_host_event { + uint32_t host_no; + uint32_t data_size; + enum iscsi_host_event_code code; + } host_event; } r; } __attribute__ ((aligned (sizeof(uint64_t)))); diff --git a/include/scsi/scsi_transport_iscsi.h b/include/scsi/scsi_transport_iscsi.h index fa7ca4e16020..7f047314281b 100644 --- a/include/scsi/scsi_transport_iscsi.h +++ b/include/scsi/scsi_transport_iscsi.h @@ -166,6 +166,12 @@ extern int iscsi_offload_mesg(struct Scsi_Host *shost, struct iscsi_transport *transport, uint32_t type, char *data, uint16_t data_size); +extern void iscsi_post_host_event(uint32_t host_no, + struct iscsi_transport *transport, + enum iscsi_host_event_code code, + uint32_t data_size, + uint8_t *data); + struct iscsi_cls_conn { struct list_head conn_list; /* item in connlist */ void *dd_data; /* LLD private data */ -- cgit v1.2.3 From ac20c7bf070df2b0feb410558ec4d75dbe59b701 Mon Sep 17 00:00:00 2001 From: Vikas Chaudhary Date: Mon, 13 Feb 2012 18:30:48 +0530 Subject: [SCSI] iscsi_transport: Added Ping support Added ping support for iscsi adapter, application can use this interface for diagnostic network connection. Signed-off-by: Vikas Chaudhary Reviewed-by: Mike Christie Signed-off-by: James Bottomley --- drivers/scsi/scsi_transport_iscsi.c | 59 +++++++++++++++++++++++++++++++++++++ include/scsi/iscsi_if.h | 17 +++++++++++ include/scsi/scsi_transport_iscsi.h | 8 +++++ 3 files changed, 84 insertions(+) (limited to 'include/scsi') diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c index 38f0bf8ea91a..a20f1813cb51 100644 --- a/drivers/scsi/scsi_transport_iscsi.c +++ b/drivers/scsi/scsi_transport_iscsi.c @@ -1507,6 +1507,35 @@ void iscsi_post_host_event(uint32_t host_no, struct iscsi_transport *transport, } EXPORT_SYMBOL_GPL(iscsi_post_host_event); +void iscsi_ping_comp_event(uint32_t host_no, struct iscsi_transport *transport, + uint32_t status, uint32_t pid, uint32_t data_size, + uint8_t *data) +{ + struct nlmsghdr *nlh; + struct sk_buff *skb; + struct iscsi_uevent *ev; + int len = NLMSG_SPACE(sizeof(*ev) + data_size); + + skb = alloc_skb(len, GFP_KERNEL); + if (!skb) { + printk(KERN_ERR "gracefully ignored ping comp: OOM\n"); + return; + } + + nlh = __nlmsg_put(skb, 0, 0, 0, (len - sizeof(*nlh)), 0); + ev = NLMSG_DATA(nlh); + ev->transport_handle = iscsi_handle(transport); + ev->type = ISCSI_KEVENT_PING_COMP; + ev->r.ping_comp.host_no = host_no; + ev->r.ping_comp.status = status; + ev->r.ping_comp.pid = pid; + ev->r.ping_comp.data_size = data_size; + memcpy((char *)ev + sizeof(*ev), data, data_size); + + iscsi_multicast_skb(skb, ISCSI_NL_GRP_ISCSID, GFP_KERNEL); +} +EXPORT_SYMBOL_GPL(iscsi_ping_comp_event); + static int iscsi_if_send_reply(uint32_t group, int seq, int type, int done, int multi, void *payload, int size) @@ -1945,6 +1974,33 @@ iscsi_set_iface_params(struct iscsi_transport *transport, return err; } +static int +iscsi_send_ping(struct iscsi_transport *transport, struct iscsi_uevent *ev) +{ + struct Scsi_Host *shost; + struct sockaddr *dst_addr; + int err; + + if (!transport->send_ping) + return -ENOSYS; + + shost = scsi_host_lookup(ev->u.iscsi_ping.host_no); + if (!shost) { + printk(KERN_ERR "iscsi_ping could not find host no %u\n", + ev->u.iscsi_ping.host_no); + return -ENODEV; + } + + dst_addr = (struct sockaddr *)((char *)ev + sizeof(*ev)); + err = transport->send_ping(shost, ev->u.iscsi_ping.iface_num, + ev->u.iscsi_ping.iface_type, + ev->u.iscsi_ping.payload_size, + ev->u.iscsi_ping.pid, + dst_addr); + scsi_host_put(shost); + return err; +} + static int iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, uint32_t *group) { @@ -2090,6 +2146,9 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, uint32_t *group) err = iscsi_set_iface_params(transport, ev, nlmsg_attrlen(nlh, sizeof(*ev))); break; + case ISCSI_UEVENT_PING: + err = iscsi_send_ping(transport, ev); + break; default: err = -ENOSYS; break; diff --git a/include/scsi/iscsi_if.h b/include/scsi/iscsi_if.h index 3aac99155e80..7ff9678b7e79 100644 --- a/include/scsi/iscsi_if.h +++ b/include/scsi/iscsi_if.h @@ -60,6 +60,7 @@ enum iscsi_uevent_e { ISCSI_UEVENT_PATH_UPDATE = UEVENT_BASE + 20, ISCSI_UEVENT_SET_IFACE_PARAMS = UEVENT_BASE + 21, + ISCSI_UEVENT_PING = UEVENT_BASE + 22, /* up events */ ISCSI_KEVENT_RECV_PDU = KEVENT_BASE + 1, @@ -73,6 +74,7 @@ enum iscsi_uevent_e { ISCSI_KEVENT_IF_DOWN = KEVENT_BASE + 8, ISCSI_KEVENT_CONN_LOGIN_STATE = KEVENT_BASE + 9, ISCSI_KEVENT_HOST_EVENT = KEVENT_BASE + 10, + ISCSI_KEVENT_PING_COMP = KEVENT_BASE + 11, }; enum iscsi_tgt_dscvr { @@ -186,6 +188,14 @@ struct iscsi_uevent { uint32_t host_no; uint32_t count; } set_iface_params; + struct msg_iscsi_ping { + uint32_t host_no; + uint32_t iface_num; + uint32_t iface_type; + uint32_t payload_size; + uint32_t pid; /* unique ping id associated + with each ping request */ + } iscsi_ping; } u; union { /* messages k -> u */ @@ -235,6 +245,13 @@ struct iscsi_uevent { uint32_t data_size; enum iscsi_host_event_code code; } host_event; + struct msg_ping_comp { + uint32_t host_no; + uint32_t status; + uint32_t pid; /* unique ping id associated + with each ping request */ + uint32_t data_size; + } ping_comp; } r; } __attribute__ ((aligned (sizeof(uint64_t)))); diff --git a/include/scsi/scsi_transport_iscsi.h b/include/scsi/scsi_transport_iscsi.h index 7f047314281b..aede513f99bd 100644 --- a/include/scsi/scsi_transport_iscsi.h +++ b/include/scsi/scsi_transport_iscsi.h @@ -144,6 +144,9 @@ struct iscsi_transport { int param, char *buf); umode_t (*attr_is_visible)(int param_type, int param); int (*bsg_request)(struct bsg_job *job); + int (*send_ping) (struct Scsi_Host *shost, uint32_t iface_num, + uint32_t iface_type, uint32_t payload_size, + uint32_t pid, struct sockaddr *dst_addr); }; /* @@ -172,6 +175,11 @@ extern void iscsi_post_host_event(uint32_t host_no, uint32_t data_size, uint8_t *data); +extern void iscsi_ping_comp_event(uint32_t host_no, + struct iscsi_transport *transport, + uint32_t status, uint32_t pid, + uint32_t data_size, uint8_t *data); + struct iscsi_cls_conn { struct list_head conn_list; /* item in connlist */ void *dd_data; /* LLD private data */ -- cgit v1.2.3 From 18a4d0a22ed6c54b67af7718c305cd010f09ddf8 Mon Sep 17 00:00:00 2001 From: Martin K. Petersen Date: Thu, 9 Feb 2012 13:48:53 -0500 Subject: [SCSI] Handle disk devices which can not process medium access commands We have experienced several devices which fail in a fashion we do not currently handle gracefully in SCSI. After a failure these devices will respond to the SCSI primary command set (INQUIRY, TEST UNIT READY, etc.) but any command accessing the storage medium will time out. The following patch adds an callback that can be used by upper level drivers to inspect the results of an error handling command. This in turn has been used to implement additional checking in the SCSI disk driver. If a medium access command fails twice but TEST UNIT READY succeeds both times in the subsequent error handling we will offline the device. The maximum number of failed commands required to take a device offline can be tweaked in sysfs. Also add a new error flag to scsi_debug which allows this scenario to be easily reproduced. [jejb: fix up integer parsing to use kstrtouint] Signed-off-by: Martin K. Petersen Signed-off-by: James Bottomley --- drivers/scsi/scsi.c | 6 ---- drivers/scsi/scsi_debug.c | 4 +++ drivers/scsi/scsi_error.c | 12 +++++-- drivers/scsi/sd.c | 82 ++++++++++++++++++++++++++++++++++++++++++++++ drivers/scsi/sd.h | 35 ++++++++++++++++++++ include/scsi/scsi_cmnd.h | 6 ++++ include/scsi/scsi_driver.h | 1 + 7 files changed, 137 insertions(+), 9 deletions(-) (limited to 'include/scsi') diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index 2aeb2e9c4d3b..07322ecff90d 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -782,12 +782,6 @@ static void scsi_done(struct scsi_cmnd *cmd) blk_complete_request(cmd->request); } -/* Move this to a header if it becomes more generally useful */ -static struct scsi_driver *scsi_cmd_to_driver(struct scsi_cmnd *cmd) -{ - return *(struct scsi_driver **)cmd->request->rq_disk->private_data; -} - /** * scsi_finish_command - cleanup and pass command back to upper layer * @cmd: the command diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c index d2fd0efca565..8917154d96c7 100644 --- a/drivers/scsi/scsi_debug.c +++ b/drivers/scsi/scsi_debug.c @@ -126,6 +126,7 @@ static const char * scsi_debug_version_date = "20100324"; #define SCSI_DEBUG_OPT_TRANSPORT_ERR 16 #define SCSI_DEBUG_OPT_DIF_ERR 32 #define SCSI_DEBUG_OPT_DIX_ERR 64 +#define SCSI_DEBUG_OPT_MAC_TIMEOUT 128 /* When "every_nth" > 0 then modulo "every_nth" commands: * - a no response is simulated if SCSI_DEBUG_OPT_TIMEOUT is set * - a RECOVERED_ERROR is simulated on successful read and write @@ -3615,6 +3616,9 @@ int scsi_debug_queuecommand_lck(struct scsi_cmnd *SCpnt, done_funct_t done) scsi_debug_every_nth = -1; if (SCSI_DEBUG_OPT_TIMEOUT & scsi_debug_opts) return 0; /* ignore command causing timeout */ + else if (SCSI_DEBUG_OPT_MAC_TIMEOUT & scsi_debug_opts && + scsi_medium_access_command(SCpnt)) + return 0; /* time out reads and writes */ else if (SCSI_DEBUG_OPT_RECOVERED_ERR & scsi_debug_opts) inj_recovered = 1; /* to reads and writes below */ else if (SCSI_DEBUG_OPT_TRANSPORT_ERR & scsi_debug_opts) diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index f66e90db3bee..2cfcbffa41fd 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -141,11 +142,11 @@ enum blk_eh_timer_return scsi_times_out(struct request *req) else if (host->hostt->eh_timed_out) rtn = host->hostt->eh_timed_out(scmd); + scmd->result |= DID_TIME_OUT << 16; + if (unlikely(rtn == BLK_EH_NOT_HANDLED && - !scsi_eh_scmd_add(scmd, SCSI_EH_CANCEL_CMD))) { - scmd->result |= DID_TIME_OUT << 16; + !scsi_eh_scmd_add(scmd, SCSI_EH_CANCEL_CMD))) rtn = BLK_EH_HANDLED; - } return rtn; } @@ -778,6 +779,7 @@ static int scsi_send_eh_cmnd(struct scsi_cmnd *scmd, unsigned char *cmnd, int cmnd_size, int timeout, unsigned sense_bytes) { struct scsi_device *sdev = scmd->device; + struct scsi_driver *sdrv = scsi_cmd_to_driver(scmd); struct Scsi_Host *shost = sdev->host; DECLARE_COMPLETION_ONSTACK(done); unsigned long timeleft; @@ -832,6 +834,10 @@ static int scsi_send_eh_cmnd(struct scsi_cmnd *scmd, unsigned char *cmnd, } scsi_eh_restore_cmnd(scmd, &ses); + + if (sdrv->eh_action) + rtn = sdrv->eh_action(scmd, cmnd, cmnd_size, rtn); + return rtn; } diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 8c525aa1b858..bd17cf8af013 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -107,6 +107,7 @@ static int sd_suspend(struct device *, pm_message_t state); static int sd_resume(struct device *); static void sd_rescan(struct device *); static int sd_done(struct scsi_cmnd *); +static int sd_eh_action(struct scsi_cmnd *, unsigned char *, int, int); static void sd_read_capacity(struct scsi_disk *sdkp, unsigned char *buffer); static void scsi_disk_release(struct device *cdev); static void sd_print_sense_hdr(struct scsi_disk *, struct scsi_sense_hdr *); @@ -346,6 +347,31 @@ sd_store_provisioning_mode(struct device *dev, struct device_attribute *attr, return count; } +static ssize_t +sd_show_max_medium_access_timeouts(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct scsi_disk *sdkp = to_scsi_disk(dev); + + return snprintf(buf, 20, "%u\n", sdkp->max_medium_access_timeouts); +} + +static ssize_t +sd_store_max_medium_access_timeouts(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct scsi_disk *sdkp = to_scsi_disk(dev); + int err; + + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + + err = kstrtouint(buf, 10, &sdkp->max_medium_access_timeouts); + + return err ? err : count; +} + static struct device_attribute sd_disk_attrs[] = { __ATTR(cache_type, S_IRUGO|S_IWUSR, sd_show_cache_type, sd_store_cache_type), @@ -360,6 +386,9 @@ static struct device_attribute sd_disk_attrs[] = { __ATTR(thin_provisioning, S_IRUGO, sd_show_thin_provisioning, NULL), __ATTR(provisioning_mode, S_IRUGO|S_IWUSR, sd_show_provisioning_mode, sd_store_provisioning_mode), + __ATTR(max_medium_access_timeouts, S_IRUGO|S_IWUSR, + sd_show_max_medium_access_timeouts, + sd_store_max_medium_access_timeouts), __ATTR_NULL, }; @@ -382,6 +411,7 @@ static struct scsi_driver sd_template = { }, .rescan = sd_rescan, .done = sd_done, + .eh_action = sd_eh_action, }; /* @@ -1313,6 +1343,55 @@ static const struct block_device_operations sd_fops = { .unlock_native_capacity = sd_unlock_native_capacity, }; +/** + * sd_eh_action - error handling callback + * @scmd: sd-issued command that has failed + * @eh_cmnd: The command that was sent during error handling + * @eh_cmnd_len: Length of eh_cmnd in bytes + * @eh_disp: The recovery disposition suggested by the midlayer + * + * This function is called by the SCSI midlayer upon completion of + * an error handling command (TEST UNIT READY, START STOP UNIT, + * etc.) The command sent to the device by the error handler is + * stored in eh_cmnd. The result of sending the eh command is + * passed in eh_disp. + **/ +static int sd_eh_action(struct scsi_cmnd *scmd, unsigned char *eh_cmnd, + int eh_cmnd_len, int eh_disp) +{ + struct scsi_disk *sdkp = scsi_disk(scmd->request->rq_disk); + + if (!scsi_device_online(scmd->device) || + !scsi_medium_access_command(scmd)) + return eh_disp; + + /* + * The device has timed out executing a medium access command. + * However, the TEST UNIT READY command sent during error + * handling completed successfully. Either the device is in the + * process of recovering or has it suffered an internal failure + * that prevents access to the storage medium. + */ + if (host_byte(scmd->result) == DID_TIME_OUT && eh_disp == SUCCESS && + eh_cmnd_len && eh_cmnd[0] == TEST_UNIT_READY) + sdkp->medium_access_timed_out++; + + /* + * If the device keeps failing read/write commands but TEST UNIT + * READY always completes successfully we assume that medium + * access is no longer possible and take the device offline. + */ + if (sdkp->medium_access_timed_out >= sdkp->max_medium_access_timeouts) { + scmd_printk(KERN_ERR, scmd, + "Medium access timeout failure. Offlining disk!\n"); + scsi_device_set_state(scmd->device, SDEV_OFFLINE); + + return FAILED; + } + + return eh_disp; +} + static unsigned int sd_completed_bytes(struct scsi_cmnd *scmd) { u64 start_lba = blk_rq_pos(scmd->request); @@ -1402,6 +1481,8 @@ static int sd_done(struct scsi_cmnd *SCpnt) (!sense_valid || sense_deferred)) goto out; + sdkp->medium_access_timed_out = 0; + switch (sshdr.sense_key) { case HARDWARE_ERROR: case MEDIUM_ERROR: @@ -2523,6 +2604,7 @@ static void sd_probe_async(void *data, async_cookie_t cookie) sdkp->RCD = 0; sdkp->ATO = 0; sdkp->first_scan = 1; + sdkp->max_medium_access_timeouts = SD_MAX_MEDIUM_TIMEOUTS; sd_revalidate_disk(gd); diff --git a/drivers/scsi/sd.h b/drivers/scsi/sd.h index 4163f2910e3d..f703f4827b6f 100644 --- a/drivers/scsi/sd.h +++ b/drivers/scsi/sd.h @@ -20,6 +20,7 @@ */ #define SD_MAX_RETRIES 5 #define SD_PASSTHROUGH_RETRIES 1 +#define SD_MAX_MEDIUM_TIMEOUTS 2 /* * Size of the initial data buffer for mode and read capacity data @@ -59,6 +60,8 @@ struct scsi_disk { u32 unmap_alignment; u32 index; unsigned int physical_block_size; + unsigned int max_medium_access_timeouts; + unsigned int medium_access_timed_out; u8 media_present; u8 write_prot; u8 protection_type;/* Data Integrity Field */ @@ -88,6 +91,38 @@ static inline struct scsi_disk *scsi_disk(struct gendisk *disk) (sdsk)->disk->disk_name, ##a) : \ sdev_printk(prefix, (sdsk)->device, fmt, ##a) +static inline int scsi_medium_access_command(struct scsi_cmnd *scmd) +{ + switch (scmd->cmnd[0]) { + case READ_6: + case READ_10: + case READ_12: + case READ_16: + case SYNCHRONIZE_CACHE: + case VERIFY: + case VERIFY_12: + case VERIFY_16: + case WRITE_6: + case WRITE_10: + case WRITE_12: + case WRITE_16: + case WRITE_SAME: + case WRITE_SAME_16: + case UNMAP: + return 1; + case VARIABLE_LENGTH_CMD: + switch (scmd->cmnd[9]) { + case READ_32: + case VERIFY_32: + case WRITE_32: + case WRITE_SAME_32: + return 1; + } + } + + return 0; +} + /* * A DIF-capable target device can be formatted with different * protection schemes. Currently 0 through 3 are defined: diff --git a/include/scsi/scsi_cmnd.h b/include/scsi/scsi_cmnd.h index 9be0128bf303..377df4a28512 100644 --- a/include/scsi/scsi_cmnd.h +++ b/include/scsi/scsi_cmnd.h @@ -10,6 +10,7 @@ struct Scsi_Host; struct scsi_device; +struct scsi_driver; /* * MAX_COMMAND_SIZE is: @@ -131,6 +132,11 @@ struct scsi_cmnd { unsigned char tag; /* SCSI-II queued command tag */ }; +static inline struct scsi_driver *scsi_cmd_to_driver(struct scsi_cmnd *cmd) +{ + return *(struct scsi_driver **)cmd->request->rq_disk->private_data; +} + extern struct scsi_cmnd *scsi_get_command(struct scsi_device *, gfp_t); extern struct scsi_cmnd *__scsi_get_command(struct Scsi_Host *, gfp_t); extern void scsi_put_command(struct scsi_cmnd *); diff --git a/include/scsi/scsi_driver.h b/include/scsi/scsi_driver.h index 9fd6702f02e2..d443aa06a722 100644 --- a/include/scsi/scsi_driver.h +++ b/include/scsi/scsi_driver.h @@ -16,6 +16,7 @@ struct scsi_driver { void (*rescan)(struct device *); int (*done)(struct scsi_cmnd *); + int (*eh_action)(struct scsi_cmnd *, unsigned char *, int, int); }; #define to_scsi_driver(drv) \ container_of((drv), struct scsi_driver, gendrv) -- cgit v1.2.3 From 95ac7fd189b7e81a200b4d00b2bb6669b31acf3a Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 17 Nov 2011 17:59:45 -0800 Subject: [SCSI] libsas: remove unused ata_task_resp fields Commit 1e34c838 "[SCSI] libsas: remove spurious sata control register read/write" removed the routines to fake the presence of the sata control registers, now remove the unused data structure fields to kill any remaining confusion. Acked-by: Jack Wang Signed-off-by: Dan Williams Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_ata.c | 4 ---- include/scsi/libsas.h | 7 ------- 2 files changed, 11 deletions(-) (limited to 'include/scsi') diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index db9238f2ecb8..83118d0b6d0c 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -121,10 +121,6 @@ static void sas_ata_task_done(struct sas_task *task) if (unlikely(link->eh_info.err_mask)) qc->flags |= ATA_QCFLAG_FAILED; } - - dev->sata_dev.sstatus = resp->sstatus; - dev->sata_dev.serror = resp->serror; - dev->sata_dev.scontrol = resp->scontrol; } else { ac = sas_to_ata_err(stat); if (ac) { diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h index 6a308d42d98f..6e64b038c649 100644 --- a/include/scsi/libsas.h +++ b/include/scsi/libsas.h @@ -171,9 +171,6 @@ struct sata_device { struct ata_port *ap; struct ata_host ata_host; struct ata_taskfile tf; - u32 sstatus; - u32 serror; - u32 scontrol; }; /* ---------- Domain device ---------- */ @@ -487,10 +484,6 @@ enum exec_status { struct ata_task_resp { u16 frame_len; u8 ending_fis[24]; /* dev to host or data-in */ - u32 sstatus; - u32 serror; - u32 scontrol; - u32 sactive; }; #define SAS_STATUS_BUF_SIZE 96 -- cgit v1.2.3 From 6f4e75a49fd07d707995865493b9f452302ae36b Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 17 Nov 2011 17:59:46 -0800 Subject: [SCSI] libsas: kill sas_slave_destroy Per commit 3e4ec344 "libata: kill ATA_FLAG_DISABLED" needing to set ATA_DEV_NONE is a holdover from before libsas converted to the "new-style" ata-eh. Signed-off-by: Dan Williams Signed-off-by: James Bottomley --- drivers/scsi/aic94xx/aic94xx_init.c | 1 - drivers/scsi/isci/init.c | 1 - drivers/scsi/libsas/sas_scsi_host.c | 9 --------- drivers/scsi/mvsas/mv_init.c | 1 - drivers/scsi/pm8001/pm8001_init.c | 1 - include/scsi/libsas.h | 1 - 6 files changed, 14 deletions(-) (limited to 'include/scsi') diff --git a/drivers/scsi/aic94xx/aic94xx_init.c b/drivers/scsi/aic94xx/aic94xx_init.c index d5ff142c93a2..8db4e727628a 100644 --- a/drivers/scsi/aic94xx/aic94xx_init.c +++ b/drivers/scsi/aic94xx/aic94xx_init.c @@ -68,7 +68,6 @@ static struct scsi_host_template aic94xx_sht = { .queuecommand = sas_queuecommand, .target_alloc = sas_target_alloc, .slave_configure = sas_slave_configure, - .slave_destroy = sas_slave_destroy, .scan_finished = asd_scan_finished, .scan_start = asd_scan_start, .change_queue_depth = sas_change_queue_depth, diff --git a/drivers/scsi/isci/init.c b/drivers/scsi/isci/init.c index 6b911e0aea3f..7ba236e9fab2 100644 --- a/drivers/scsi/isci/init.c +++ b/drivers/scsi/isci/init.c @@ -154,7 +154,6 @@ static struct scsi_host_template isci_sht = { .queuecommand = sas_queuecommand, .target_alloc = sas_target_alloc, .slave_configure = sas_slave_configure, - .slave_destroy = sas_slave_destroy, .scan_finished = isci_host_scan_finished, .scan_start = isci_host_scan_start, .change_queue_depth = sas_change_queue_depth, diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c index b6e233d9a0a1..e95e5e17bd88 100644 --- a/drivers/scsi/libsas/sas_scsi_host.c +++ b/drivers/scsi/libsas/sas_scsi_host.c @@ -797,14 +797,6 @@ int sas_slave_configure(struct scsi_device *scsi_dev) return 0; } -void sas_slave_destroy(struct scsi_device *scsi_dev) -{ - struct domain_device *dev = sdev_to_domain_dev(scsi_dev); - - if (dev_is_sata(dev)) - sas_to_ata_dev(dev)->class = ATA_DEV_NONE; -} - int sas_change_queue_depth(struct scsi_device *sdev, int depth, int reason) { struct domain_device *dev = sdev_to_domain_dev(sdev); @@ -1108,7 +1100,6 @@ EXPORT_SYMBOL_GPL(sas_request_addr); EXPORT_SYMBOL_GPL(sas_queuecommand); EXPORT_SYMBOL_GPL(sas_target_alloc); EXPORT_SYMBOL_GPL(sas_slave_configure); -EXPORT_SYMBOL_GPL(sas_slave_destroy); EXPORT_SYMBOL_GPL(sas_change_queue_depth); EXPORT_SYMBOL_GPL(sas_change_queue_type); EXPORT_SYMBOL_GPL(sas_bios_param); diff --git a/drivers/scsi/mvsas/mv_init.c b/drivers/scsi/mvsas/mv_init.c index 6f589195746c..d45878b31254 100644 --- a/drivers/scsi/mvsas/mv_init.c +++ b/drivers/scsi/mvsas/mv_init.c @@ -60,7 +60,6 @@ static struct scsi_host_template mvs_sht = { .queuecommand = sas_queuecommand, .target_alloc = sas_target_alloc, .slave_configure = sas_slave_configure, - .slave_destroy = sas_slave_destroy, .scan_finished = mvs_scan_finished, .scan_start = mvs_scan_start, .change_queue_depth = sas_change_queue_depth, diff --git a/drivers/scsi/pm8001/pm8001_init.c b/drivers/scsi/pm8001/pm8001_init.c index c21a2163f9f6..bd165ea61919 100644 --- a/drivers/scsi/pm8001/pm8001_init.c +++ b/drivers/scsi/pm8001/pm8001_init.c @@ -62,7 +62,6 @@ static struct scsi_host_template pm8001_sht = { .queuecommand = sas_queuecommand, .target_alloc = sas_target_alloc, .slave_configure = sas_slave_configure, - .slave_destroy = sas_slave_destroy, .scan_finished = pm8001_scan_finished, .scan_start = pm8001_scan_start, .change_queue_depth = sas_change_queue_depth, diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h index 6e64b038c649..2b14348336d6 100644 --- a/include/scsi/libsas.h +++ b/include/scsi/libsas.h @@ -625,7 +625,6 @@ extern int sas_queuecommand(struct Scsi_Host * ,struct scsi_cmnd *); extern int sas_target_alloc(struct scsi_target *); extern int sas_slave_alloc(struct scsi_device *); extern int sas_slave_configure(struct scsi_device *); -extern void sas_slave_destroy(struct scsi_device *); extern int sas_change_queue_depth(struct scsi_device *, int new_depth, int reason); extern int sas_change_queue_type(struct scsi_device *, int qt); -- cgit v1.2.3 From 735f7d2fedf57380214221be7bed7f62d729e262 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 17 Nov 2011 17:59:47 -0800 Subject: [SCSI] libsas: fix domain_device leak Arrange for the deallocation of a struct domain_device object when it no longer has: 1/ any children 2/ references by any scsi_targets 3/ references by a lldd The comment about domain_device lifetime in Documentation/scsi/libsas.txt is stale as it appears mainline never had a version of a struct domain_device that was registered as a kobject. We now manage domain_device reference counts on behalf of external agents. Reviewed-by: Jack Wang Signed-off-by: Dan Williams Signed-off-by: James Bottomley --- Documentation/scsi/libsas.txt | 15 --------------- drivers/scsi/libsas/sas_discover.c | 36 ++++++++++++++++++++++++------------ drivers/scsi/libsas/sas_expander.c | 10 ++++++---- drivers/scsi/libsas/sas_internal.h | 19 +++++++++++++++++++ drivers/scsi/libsas/sas_scsi_host.c | 16 ++++++---------- include/scsi/libsas.h | 1 + 6 files changed, 56 insertions(+), 41 deletions(-) (limited to 'include/scsi') diff --git a/Documentation/scsi/libsas.txt b/Documentation/scsi/libsas.txt index aa54f54c4a50..3cc9c7843e15 100644 --- a/Documentation/scsi/libsas.txt +++ b/Documentation/scsi/libsas.txt @@ -398,21 +398,6 @@ struct sas_task { task_done -- callback when the task has finished execution }; -When an external entity, entity other than the LLDD or the -SAS Layer, wants to work with a struct domain_device, it -_must_ call kobject_get() when getting a handle on the -device and kobject_put() when it is done with the device. - -This does two things: - A) implements proper kfree() for the device; - B) increments/decrements the kref for all players: - domain_device - all domain_device's ... (if past an expander) - port - host adapter - pci device - and up the ladder, etc. - DISCOVERY --------- diff --git a/drivers/scsi/libsas/sas_discover.c b/drivers/scsi/libsas/sas_discover.c index 54a5199ceb56..4e649306ef4e 100644 --- a/drivers/scsi/libsas/sas_discover.c +++ b/drivers/scsi/libsas/sas_discover.c @@ -36,8 +36,6 @@ void sas_init_dev(struct domain_device *dev) { - INIT_LIST_HEAD(&dev->siblings); - INIT_LIST_HEAD(&dev->dev_list_node); switch (dev->dev_type) { case SAS_END_DEV: break; @@ -73,14 +71,14 @@ static int sas_get_port_device(struct asd_sas_port *port) struct sas_rphy *rphy; struct domain_device *dev; - dev = kzalloc(sizeof(*dev), GFP_KERNEL); + dev = sas_alloc_device(); if (!dev) return -ENOMEM; spin_lock_irqsave(&port->phy_list_lock, flags); if (list_empty(&port->phy_list)) { spin_unlock_irqrestore(&port->phy_list_lock, flags); - kfree(dev); + sas_put_device(dev); return -ENODEV; } phy = container_of(port->phy_list.next, struct asd_sas_phy, port_phy_el); @@ -130,7 +128,7 @@ static int sas_get_port_device(struct asd_sas_port *port) } if (!rphy) { - kfree(dev); + sas_put_device(dev); return -ENODEV; } rphy->identify.phy_identifier = phy->phy->identify.phy_identifier; @@ -173,6 +171,7 @@ int sas_notify_lldd_dev_found(struct domain_device *dev) dev_name(sas_ha->dev), SAS_ADDR(dev->sas_addr), res); } + kref_get(&dev->kref); } return res; } @@ -184,8 +183,10 @@ void sas_notify_lldd_dev_gone(struct domain_device *dev) struct Scsi_Host *shost = sas_ha->core.shost; struct sas_internal *i = to_sas_internal(shost->transportt); - if (i->dft->lldd_dev_gone) + if (i->dft->lldd_dev_gone) { i->dft->lldd_dev_gone(dev); + sas_put_device(dev); + } } /* ---------- Common/dispatchers ---------- */ @@ -219,6 +220,20 @@ out_err2: /* ---------- Device registration and unregistration ---------- */ +void sas_free_device(struct kref *kref) +{ + struct domain_device *dev = container_of(kref, typeof(*dev), kref); + + if (dev->parent) + sas_put_device(dev->parent); + + /* remove the phys and ports, everything else should be gone */ + if (dev->dev_type == EDGE_DEV || dev->dev_type == FANOUT_DEV) + kfree(dev->ex_dev.ex_phy); + + kfree(dev); +} + static void sas_unregister_common_dev(struct asd_sas_port *port, struct domain_device *dev) { sas_notify_lldd_dev_gone(dev); @@ -230,6 +245,8 @@ static void sas_unregister_common_dev(struct asd_sas_port *port, struct domain_d spin_lock_irq(&port->dev_list_lock); list_del_init(&dev->dev_list_node); spin_unlock_irq(&port->dev_list_lock); + + sas_put_device(dev); } void sas_unregister_dev(struct asd_sas_port *port, struct domain_device *dev) @@ -239,11 +256,6 @@ void sas_unregister_dev(struct asd_sas_port *port, struct domain_device *dev) sas_rphy_delete(dev->rphy); dev->rphy = NULL; } - if (dev->dev_type == EDGE_DEV || dev->dev_type == FANOUT_DEV) { - /* remove the phys and ports, everything else should be gone */ - kfree(dev->ex_dev.ex_phy); - dev->ex_dev.ex_phy = NULL; - } sas_unregister_common_dev(port, dev); } @@ -322,7 +334,7 @@ static void sas_discover_domain(struct work_struct *work) list_del_init(&dev->dev_list_node); spin_unlock_irq(&port->dev_list_lock); - kfree(dev); /* not kobject_register-ed yet */ + sas_put_device(dev); port->port_dev = NULL; } diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index 1b831c55ec6e..15d2239a378b 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c @@ -657,10 +657,11 @@ static struct domain_device *sas_ex_discover_end_dev( if (phy->attached_sata_host || phy->attached_sata_ps) return NULL; - child = kzalloc(sizeof(*child), GFP_KERNEL); + child = sas_alloc_device(); if (!child) return NULL; + kref_get(&parent->kref); child->parent = parent; child->port = parent->port; child->iproto = phy->attached_iproto; @@ -762,7 +763,7 @@ static struct domain_device *sas_ex_discover_end_dev( sas_port_delete(phy->port); out_err: phy->port = NULL; - kfree(child); + sas_put_device(child); return NULL; } @@ -809,7 +810,7 @@ static struct domain_device *sas_ex_discover_expander( phy->attached_phy_id); return NULL; } - child = kzalloc(sizeof(*child), GFP_KERNEL); + child = sas_alloc_device(); if (!child) return NULL; @@ -835,6 +836,7 @@ static struct domain_device *sas_ex_discover_expander( child->rphy = rphy; edev = rphy_to_expander_device(rphy); child->dev_type = phy->attached_dev_type; + kref_get(&parent->kref); child->parent = parent; child->port = port; child->iproto = phy->attached_iproto; @@ -858,7 +860,7 @@ static struct domain_device *sas_ex_discover_expander( spin_lock_irq(&parent->port->dev_list_lock); list_del(&child->dev_list_node); spin_unlock_irq(&parent->port->dev_list_lock); - kfree(child); + sas_put_device(child); return NULL; } list_add_tail(&child->siblings, &parent->ex_dev.children); diff --git a/drivers/scsi/libsas/sas_internal.h b/drivers/scsi/libsas/sas_internal.h index 14e21b5fb8ba..0d43408196f9 100644 --- a/drivers/scsi/libsas/sas_internal.h +++ b/drivers/scsi/libsas/sas_internal.h @@ -76,6 +76,8 @@ struct domain_device *sas_find_dev_by_rphy(struct sas_rphy *rphy); void sas_hae_reset(struct work_struct *work); +void sas_free_device(struct kref *kref); + #ifdef CONFIG_SCSI_SAS_HOST_SMP extern int sas_smp_host_handler(struct Scsi_Host *shost, struct request *req, struct request *rsp); @@ -161,4 +163,21 @@ static inline void sas_add_parent_port(struct domain_device *dev, int phy_id) sas_port_add_phy(ex->parent_port, ex_phy->phy); } +static inline struct domain_device *sas_alloc_device(void) +{ + struct domain_device *dev = kzalloc(sizeof(*dev), GFP_KERNEL); + + if (dev) { + INIT_LIST_HEAD(&dev->siblings); + INIT_LIST_HEAD(&dev->dev_list_node); + kref_init(&dev->kref); + } + return dev; +} + +static inline void sas_put_device(struct domain_device *dev) +{ + kref_put(&dev->kref, sas_free_device); +} + #endif /* _SAS_INTERNAL_H_ */ diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c index e95e5e17bd88..2a163c73fd8b 100644 --- a/drivers/scsi/libsas/sas_scsi_host.c +++ b/drivers/scsi/libsas/sas_scsi_host.c @@ -737,16 +737,10 @@ struct domain_device *sas_find_dev_by_rphy(struct sas_rphy *rphy) return found_dev; } -static inline struct domain_device *sas_find_target(struct scsi_target *starget) -{ - struct sas_rphy *rphy = dev_to_rphy(starget->dev.parent); - - return sas_find_dev_by_rphy(rphy); -} - int sas_target_alloc(struct scsi_target *starget) { - struct domain_device *found_dev = sas_find_target(starget); + struct sas_rphy *rphy = dev_to_rphy(starget->dev.parent); + struct domain_device *found_dev = sas_find_dev_by_rphy(rphy); int res; if (!found_dev) @@ -758,6 +752,7 @@ int sas_target_alloc(struct scsi_target *starget) return res; } + kref_get(&found_dev->kref); starget->hostdata = found_dev; return 0; } @@ -1047,7 +1042,7 @@ int sas_slave_alloc(struct scsi_device *scsi_dev) void sas_target_destroy(struct scsi_target *starget) { - struct domain_device *found_dev = sas_find_target(starget); + struct domain_device *found_dev = starget->hostdata; if (!found_dev) return; @@ -1055,7 +1050,8 @@ void sas_target_destroy(struct scsi_target *starget) if (dev_is_sata(found_dev)) ata_sas_port_destroy(found_dev->sata_dev.ap); - return; + starget->hostdata = NULL; + sas_put_device(found_dev); } static void sas_parse_addr(u8 *sas_addr, const char *p) diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h index 2b14348336d6..7ecb5c1c0851 100644 --- a/include/scsi/libsas.h +++ b/include/scsi/libsas.h @@ -206,6 +206,7 @@ struct domain_device { void *lldd_dev; int gone; + struct kref kref; }; struct sas_discovery_event { -- cgit v1.2.3 From 756f173fb5fa90ec15222e80fb579288be7794fd Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 17 Nov 2011 17:59:48 -0800 Subject: [SCSI] libsas: fix leak of dev->sata_dev.identify_[packet_]device These are never freed in the nominal path. A domain_device has a different lifetime than a sas_rphy we need a dev->rphy independent way of identifying sata devices. Reviewed-by: Jack Wang Signed-off-by: Dan Williams Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_discover.c | 6 ++++++ include/scsi/sas_ata.h | 3 ++- 2 files changed, 8 insertions(+), 1 deletion(-) (limited to 'include/scsi') diff --git a/drivers/scsi/libsas/sas_discover.c b/drivers/scsi/libsas/sas_discover.c index 4e649306ef4e..dc52b1fa218e 100644 --- a/drivers/scsi/libsas/sas_discover.c +++ b/drivers/scsi/libsas/sas_discover.c @@ -30,6 +30,7 @@ #include #include +#include #include "../scsi_sas_internal.h" /* ---------- Basic task processing for discovery purposes ---------- */ @@ -231,6 +232,11 @@ void sas_free_device(struct kref *kref) if (dev->dev_type == EDGE_DEV || dev->dev_type == FANOUT_DEV) kfree(dev->ex_dev.ex_phy); + if (dev_is_sata(dev)) { + kfree(dev->sata_dev.identify_device); + kfree(dev->sata_dev.identify_packet_device); + } + kfree(dev); } diff --git a/include/scsi/sas_ata.h b/include/scsi/sas_ata.h index 9c159f74c6d0..7d5013f8653d 100644 --- a/include/scsi/sas_ata.h +++ b/include/scsi/sas_ata.h @@ -32,7 +32,8 @@ static inline int dev_is_sata(struct domain_device *dev) { - return (dev->rphy->identify.target_port_protocols & SAS_PROTOCOL_SATA); + return dev->dev_type == SATA_DEV || dev->dev_type == SATA_PM || + dev->dev_type == SATA_PM_PORT; } int sas_ata_init_host_and_port(struct domain_device *found_dev, -- cgit v1.2.3 From b15ebe0b5d0b95aeb1d84cae3649df1e0e065e9b Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 17 Nov 2011 17:59:49 -0800 Subject: [SCSI] libsas: replace event locks with atomic bitops The locks only served to make sure the pending event bitmask was updated consistently. Signed-off-by: Dan Williams Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_discover.c | 10 +++------- drivers/scsi/libsas/sas_event.c | 8 +++----- drivers/scsi/libsas/sas_init.c | 3 +-- drivers/scsi/libsas/sas_internal.h | 32 +++++++------------------------- drivers/scsi/libsas/sas_phy.c | 12 ++++-------- drivers/scsi/libsas/sas_port.c | 15 +++++---------- include/scsi/libsas.h | 3 --- 7 files changed, 23 insertions(+), 60 deletions(-) (limited to 'include/scsi') diff --git a/drivers/scsi/libsas/sas_discover.c b/drivers/scsi/libsas/sas_discover.c index dc52b1fa218e..ed041189e764 100644 --- a/drivers/scsi/libsas/sas_discover.c +++ b/drivers/scsi/libsas/sas_discover.c @@ -295,8 +295,7 @@ static void sas_discover_domain(struct work_struct *work) container_of(work, struct sas_discovery_event, work); struct asd_sas_port *port = ev->port; - sas_begin_event(DISCE_DISCOVER_DOMAIN, &port->disc.disc_event_lock, - &port->disc.pending); + clear_bit(DISCE_DISCOVER_DOMAIN, &port->disc.pending); if (port->port_dev) return; @@ -355,8 +354,7 @@ static void sas_revalidate_domain(struct work_struct *work) container_of(work, struct sas_discovery_event, work); struct asd_sas_port *port = ev->port; - sas_begin_event(DISCE_REVALIDATE_DOMAIN, &port->disc.disc_event_lock, - &port->disc.pending); + clear_bit(DISCE_REVALIDATE_DOMAIN, &port->disc.pending); SAS_DPRINTK("REVALIDATING DOMAIN on port %d, pid:%d\n", port->id, task_pid_nr(current)); @@ -379,8 +377,7 @@ int sas_discover_event(struct asd_sas_port *port, enum discover_event ev) BUG_ON(ev >= DISC_NUM_EVENTS); - sas_queue_event(ev, &disc->disc_event_lock, &disc->pending, - &disc->disc_work[ev].work, port->ha); + sas_queue_event(ev, &disc->pending, &disc->disc_work[ev].work, port->ha); return 0; } @@ -400,7 +397,6 @@ void sas_init_disc(struct sas_discovery *disc, struct asd_sas_port *port) [DISCE_REVALIDATE_DOMAIN] = sas_revalidate_domain, }; - spin_lock_init(&disc->disc_event_lock); disc->pending = 0; for (i = 0; i < DISC_NUM_EVENTS; i++) { INIT_WORK(&disc->disc_work[i].work, sas_event_fns[i]); diff --git a/drivers/scsi/libsas/sas_event.c b/drivers/scsi/libsas/sas_event.c index 9db30fb5caf2..9c084bc09bbd 100644 --- a/drivers/scsi/libsas/sas_event.c +++ b/drivers/scsi/libsas/sas_event.c @@ -30,7 +30,7 @@ static void notify_ha_event(struct sas_ha_struct *sas_ha, enum ha_event event) { BUG_ON(event >= HA_NUM_EVENTS); - sas_queue_event(event, &sas_ha->event_lock, &sas_ha->pending, + sas_queue_event(event, &sas_ha->pending, &sas_ha->ha_events[event].work, sas_ha); } @@ -40,7 +40,7 @@ static void notify_port_event(struct asd_sas_phy *phy, enum port_event event) BUG_ON(event >= PORT_NUM_EVENTS); - sas_queue_event(event, &ha->event_lock, &phy->port_events_pending, + sas_queue_event(event, &phy->port_events_pending, &phy->port_events[event].work, ha); } @@ -50,7 +50,7 @@ static void notify_phy_event(struct asd_sas_phy *phy, enum phy_event event) BUG_ON(event >= PHY_NUM_EVENTS); - sas_queue_event(event, &ha->event_lock, &phy->phy_events_pending, + sas_queue_event(event, &phy->phy_events_pending, &phy->phy_events[event].work, ha); } @@ -62,8 +62,6 @@ int sas_init_events(struct sas_ha_struct *sas_ha) int i; - spin_lock_init(&sas_ha->event_lock); - for (i = 0; i < HA_NUM_EVENTS; i++) { INIT_WORK(&sas_ha->ha_events[i].work, sas_ha_event_fns[i]); sas_ha->ha_events[i].ha = sas_ha; diff --git a/drivers/scsi/libsas/sas_init.c b/drivers/scsi/libsas/sas_init.c index d81c3b1989f7..a435876f1f77 100644 --- a/drivers/scsi/libsas/sas_init.c +++ b/drivers/scsi/libsas/sas_init.c @@ -97,8 +97,7 @@ void sas_hae_reset(struct work_struct *work) container_of(work, struct sas_ha_event, work); struct sas_ha_struct *ha = ev->ha; - sas_begin_event(HAE_RESET, &ha->event_lock, - &ha->pending); + clear_bit(HAE_RESET, &ha->pending); } int sas_register_ha(struct sas_ha_struct *sas_ha) diff --git a/drivers/scsi/libsas/sas_internal.h b/drivers/scsi/libsas/sas_internal.h index 0d43408196f9..7fe4eded2866 100644 --- a/drivers/scsi/libsas/sas_internal.h +++ b/drivers/scsi/libsas/sas_internal.h @@ -92,36 +92,18 @@ static inline int sas_smp_host_handler(struct Scsi_Host *shost, } #endif -static inline void sas_queue_event(int event, spinlock_t *lock, - unsigned long *pending, +static inline void sas_queue_event(int event, unsigned long *pending, struct work_struct *work, struct sas_ha_struct *sas_ha) { - unsigned long flags; + if (!test_and_set_bit(event, pending)) { + unsigned long flags; - spin_lock_irqsave(lock, flags); - if (test_bit(event, pending)) { - spin_unlock_irqrestore(lock, flags); - return; + spin_lock_irqsave(&sas_ha->state_lock, flags); + if (sas_ha->state != SAS_HA_UNREGISTERED) + scsi_queue_work(sas_ha->core.shost, work); + spin_unlock_irqrestore(&sas_ha->state_lock, flags); } - __set_bit(event, pending); - spin_unlock_irqrestore(lock, flags); - - spin_lock_irqsave(&sas_ha->state_lock, flags); - if (sas_ha->state != SAS_HA_UNREGISTERED) { - scsi_queue_work(sas_ha->core.shost, work); - } - spin_unlock_irqrestore(&sas_ha->state_lock, flags); -} - -static inline void sas_begin_event(int event, spinlock_t *lock, - unsigned long *pending) -{ - unsigned long flags; - - spin_lock_irqsave(lock, flags); - __clear_bit(event, pending); - spin_unlock_irqrestore(lock, flags); } static inline void sas_fill_in_rphy(struct domain_device *dev, diff --git a/drivers/scsi/libsas/sas_phy.c b/drivers/scsi/libsas/sas_phy.c index e0f5018e9071..dcfd4a9105c5 100644 --- a/drivers/scsi/libsas/sas_phy.c +++ b/drivers/scsi/libsas/sas_phy.c @@ -36,8 +36,7 @@ static void sas_phye_loss_of_signal(struct work_struct *work) container_of(work, struct asd_sas_event, work); struct asd_sas_phy *phy = ev->phy; - sas_begin_event(PHYE_LOSS_OF_SIGNAL, &phy->ha->event_lock, - &phy->phy_events_pending); + clear_bit(PHYE_LOSS_OF_SIGNAL, &phy->phy_events_pending); phy->error = 0; sas_deform_port(phy, 1); } @@ -48,8 +47,7 @@ static void sas_phye_oob_done(struct work_struct *work) container_of(work, struct asd_sas_event, work); struct asd_sas_phy *phy = ev->phy; - sas_begin_event(PHYE_OOB_DONE, &phy->ha->event_lock, - &phy->phy_events_pending); + clear_bit(PHYE_OOB_DONE, &phy->phy_events_pending); phy->error = 0; } @@ -63,8 +61,7 @@ static void sas_phye_oob_error(struct work_struct *work) struct sas_internal *i = to_sas_internal(sas_ha->core.shost->transportt); - sas_begin_event(PHYE_OOB_ERROR, &phy->ha->event_lock, - &phy->phy_events_pending); + clear_bit(PHYE_OOB_ERROR, &phy->phy_events_pending); sas_deform_port(phy, 1); @@ -95,8 +92,7 @@ static void sas_phye_spinup_hold(struct work_struct *work) struct sas_internal *i = to_sas_internal(sas_ha->core.shost->transportt); - sas_begin_event(PHYE_SPINUP_HOLD, &phy->ha->event_lock, - &phy->phy_events_pending); + clear_bit(PHYE_SPINUP_HOLD, &phy->phy_events_pending); phy->error = 0; i->dft->lldd_control_phy(phy, PHY_FUNC_RELEASE_SPINUP_HOLD, NULL); diff --git a/drivers/scsi/libsas/sas_port.c b/drivers/scsi/libsas/sas_port.c index 42fd1f25b664..a47c7a75327b 100644 --- a/drivers/scsi/libsas/sas_port.c +++ b/drivers/scsi/libsas/sas_port.c @@ -213,8 +213,7 @@ void sas_porte_bytes_dmaed(struct work_struct *work) container_of(work, struct asd_sas_event, work); struct asd_sas_phy *phy = ev->phy; - sas_begin_event(PORTE_BYTES_DMAED, &phy->ha->event_lock, - &phy->port_events_pending); + clear_bit(PORTE_BYTES_DMAED, &phy->port_events_pending); sas_form_port(phy); } @@ -227,8 +226,7 @@ void sas_porte_broadcast_rcvd(struct work_struct *work) unsigned long flags; u32 prim; - sas_begin_event(PORTE_BROADCAST_RCVD, &phy->ha->event_lock, - &phy->port_events_pending); + clear_bit(PORTE_BROADCAST_RCVD, &phy->port_events_pending); spin_lock_irqsave(&phy->sas_prim_lock, flags); prim = phy->sas_prim; @@ -244,8 +242,7 @@ void sas_porte_link_reset_err(struct work_struct *work) container_of(work, struct asd_sas_event, work); struct asd_sas_phy *phy = ev->phy; - sas_begin_event(PORTE_LINK_RESET_ERR, &phy->ha->event_lock, - &phy->port_events_pending); + clear_bit(PORTE_LINK_RESET_ERR, &phy->port_events_pending); sas_deform_port(phy, 1); } @@ -256,8 +253,7 @@ void sas_porte_timer_event(struct work_struct *work) container_of(work, struct asd_sas_event, work); struct asd_sas_phy *phy = ev->phy; - sas_begin_event(PORTE_TIMER_EVENT, &phy->ha->event_lock, - &phy->port_events_pending); + clear_bit(PORTE_TIMER_EVENT, &phy->port_events_pending); sas_deform_port(phy, 1); } @@ -268,8 +264,7 @@ void sas_porte_hard_reset(struct work_struct *work) container_of(work, struct asd_sas_event, work); struct asd_sas_phy *phy = ev->phy; - sas_begin_event(PORTE_HARD_RESET, &phy->ha->event_lock, - &phy->port_events_pending); + clear_bit(PORTE_HARD_RESET, &phy->port_events_pending); sas_deform_port(phy, 1); } diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h index 7ecb5c1c0851..de63a664b5e5 100644 --- a/include/scsi/libsas.h +++ b/include/scsi/libsas.h @@ -215,7 +215,6 @@ struct sas_discovery_event { }; struct sas_discovery { - spinlock_t disc_event_lock; struct sas_discovery_event disc_work[DISC_NUM_EVENTS]; unsigned long pending; u8 fanout_sas_addr[8]; @@ -272,7 +271,6 @@ struct asd_sas_event { */ struct asd_sas_phy { /* private: */ - /* protected by ha->event_lock */ struct asd_sas_event port_events[PORT_NUM_EVENTS]; struct asd_sas_event phy_events[PHY_NUM_EVENTS]; @@ -337,7 +335,6 @@ enum sas_ha_state { struct sas_ha_struct { /* private: */ - spinlock_t event_lock; struct sas_ha_event ha_events[HA_NUM_EVENTS]; unsigned long pending; -- cgit v1.2.3 From f8daa6e6d83f60a721752cb53433bfdc1503b45f Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Mon, 19 Dec 2011 17:02:25 -0800 Subject: [SCSI] libsas: convert ha->state to flags In preparation for adding new states (SAS_HA_DRAINING, SAS_HA_FROZEN), convert ha->state into a set of flags. Signed-off-by: Dan Williams Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_init.c | 4 ++-- drivers/scsi/libsas/sas_internal.h | 2 +- include/scsi/libsas.h | 3 +-- 3 files changed, 4 insertions(+), 5 deletions(-) (limited to 'include/scsi') diff --git a/drivers/scsi/libsas/sas_init.c b/drivers/scsi/libsas/sas_init.c index a435876f1f77..da244e68fe6f 100644 --- a/drivers/scsi/libsas/sas_init.c +++ b/drivers/scsi/libsas/sas_init.c @@ -112,7 +112,7 @@ int sas_register_ha(struct sas_ha_struct *sas_ha) else if (sas_ha->lldd_queue_size == -1) sas_ha->lldd_queue_size = 128; /* Sanity */ - sas_ha->state = SAS_HA_REGISTERED; + set_bit(SAS_HA_REGISTERED, &sas_ha->state); spin_lock_init(&sas_ha->state_lock); error = sas_register_phys(sas_ha); @@ -160,7 +160,7 @@ int sas_unregister_ha(struct sas_ha_struct *sas_ha) /* Set the state to unregistered to avoid further * events to be queued */ spin_lock_irqsave(&sas_ha->state_lock, flags); - sas_ha->state = SAS_HA_UNREGISTERED; + clear_bit(SAS_HA_REGISTERED, &sas_ha->state); spin_unlock_irqrestore(&sas_ha->state_lock, flags); scsi_flush_work(sas_ha->core.shost); diff --git a/drivers/scsi/libsas/sas_internal.h b/drivers/scsi/libsas/sas_internal.h index 7fe4eded2866..1fd84b3f091f 100644 --- a/drivers/scsi/libsas/sas_internal.h +++ b/drivers/scsi/libsas/sas_internal.h @@ -100,7 +100,7 @@ static inline void sas_queue_event(int event, unsigned long *pending, unsigned long flags; spin_lock_irqsave(&sas_ha->state_lock, flags); - if (sas_ha->state != SAS_HA_UNREGISTERED) + if (test_bit(SAS_HA_REGISTERED, &sas_ha->state)) scsi_queue_work(sas_ha->core.shost, work); spin_unlock_irqrestore(&sas_ha->state_lock, flags); } diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h index de63a664b5e5..8e402d5a0640 100644 --- a/include/scsi/libsas.h +++ b/include/scsi/libsas.h @@ -330,7 +330,6 @@ struct sas_ha_event { enum sas_ha_state { SAS_HA_REGISTERED, - SAS_HA_UNREGISTERED }; struct sas_ha_struct { @@ -338,7 +337,7 @@ struct sas_ha_struct { struct sas_ha_event ha_events[HA_NUM_EVENTS]; unsigned long pending; - enum sas_ha_state state; + unsigned long state; spinlock_t state_lock; struct scsi_core core; -- cgit v1.2.3 From b1124cd3ec97406c767b90bf7e93ecd2d2915592 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Mon, 19 Dec 2011 16:42:34 -0800 Subject: [SCSI] libsas: introduce sas_drain_work() When an lldd invokes ->notify_port_event() it can trigger a chain of libsas events to: 1/ form the port and find the direct attached device 2/ if the attached device is an expander perform domain discovery A call to flush_workqueue() will only flush the initial port formation work. Currently libsas users need to call scsi_flush_work() up to the max depth of chain (which will grow from 2 to 3 when ata discovery is moved to its own discovery event). Instead of open coding multiple calls switch to use drain_workqueue() to flush sas work. drain_workqueue() does not handle new work submitted during the drain so libsas needs a bit of infrastructure to hold off unchained work submissions while a drain is in flight. A lldd ->notify() event is considered 'unchained' while a sas_discover_event() is 'chained'. As Tejun notes: "For now, I think it would be best to add private wrapper in libsas to support deferring unchained work items while draining." Signed-off-by: Dan Williams Signed-off-by: James Bottomley --- drivers/scsi/aic94xx/aic94xx_init.c | 2 +- drivers/scsi/isci/host.c | 8 ++---- drivers/scsi/libsas/sas_discover.c | 21 +++++++++++++- drivers/scsi/libsas/sas_event.c | 55 +++++++++++++++++++++++++++++++++++++ drivers/scsi/libsas/sas_init.c | 9 ++++-- drivers/scsi/libsas/sas_internal.h | 14 ---------- drivers/scsi/mvsas/mv_sas.c | 2 +- drivers/scsi/pm8001/pm8001_sas.c | 4 ++- include/scsi/libsas.h | 4 +++ 9 files changed, 93 insertions(+), 26 deletions(-) (limited to 'include/scsi') diff --git a/drivers/scsi/aic94xx/aic94xx_init.c b/drivers/scsi/aic94xx/aic94xx_init.c index 8db4e727628a..2b3717f6d22c 100644 --- a/drivers/scsi/aic94xx/aic94xx_init.c +++ b/drivers/scsi/aic94xx/aic94xx_init.c @@ -971,7 +971,7 @@ static int asd_scan_finished(struct Scsi_Host *shost, unsigned long time) if (time < HZ) return 0; /* Wait for discovery to finish */ - scsi_flush_work(shost); + sas_drain_work(SHOST_TO_SAS_HA(shost)); return 1; } diff --git a/drivers/scsi/isci/host.c b/drivers/scsi/isci/host.c index 508aa8ce25b4..e3cf3832c5b6 100644 --- a/drivers/scsi/isci/host.c +++ b/drivers/scsi/isci/host.c @@ -650,15 +650,13 @@ static void isci_host_start_complete(struct isci_host *ihost, enum sci_status co int isci_host_scan_finished(struct Scsi_Host *shost, unsigned long time) { - struct isci_host *ihost = SHOST_TO_SAS_HA(shost)->lldd_ha; + struct sas_ha_struct *ha = SHOST_TO_SAS_HA(shost); + struct isci_host *ihost = ha->lldd_ha; if (test_bit(IHOST_START_PENDING, &ihost->flags)) return 0; - /* todo: use sas_flush_discovery once it is upstream */ - scsi_flush_work(shost); - - scsi_flush_work(shost); + sas_drain_work(ha); dev_dbg(&ihost->pdev->dev, "%s: ihost->status = %d, time = %ld\n", diff --git a/drivers/scsi/libsas/sas_discover.c b/drivers/scsi/libsas/sas_discover.c index ed041189e764..32e011766046 100644 --- a/drivers/scsi/libsas/sas_discover.c +++ b/drivers/scsi/libsas/sas_discover.c @@ -367,6 +367,25 @@ static void sas_revalidate_domain(struct work_struct *work) /* ---------- Events ---------- */ +static void sas_chain_work(struct sas_ha_struct *ha, struct work_struct *work) +{ + /* chained work is not subject to SA_HA_DRAINING or SAS_HA_REGISTERED */ + scsi_queue_work(ha->core.shost, work); +} + +static void sas_chain_event(int event, unsigned long *pending, + struct work_struct *work, + struct sas_ha_struct *ha) +{ + if (!test_and_set_bit(event, pending)) { + unsigned long flags; + + spin_lock_irqsave(&ha->state_lock, flags); + sas_chain_work(ha, work); + spin_unlock_irqrestore(&ha->state_lock, flags); + } +} + int sas_discover_event(struct asd_sas_port *port, enum discover_event ev) { struct sas_discovery *disc; @@ -377,7 +396,7 @@ int sas_discover_event(struct asd_sas_port *port, enum discover_event ev) BUG_ON(ev >= DISC_NUM_EVENTS); - sas_queue_event(ev, &disc->pending, &disc->disc_work[ev].work, port->ha); + sas_chain_event(ev, &disc->pending, &disc->disc_work[ev].work, port->ha); return 0; } diff --git a/drivers/scsi/libsas/sas_event.c b/drivers/scsi/libsas/sas_event.c index 9c084bc09bbd..e5035aa4c2a6 100644 --- a/drivers/scsi/libsas/sas_event.c +++ b/drivers/scsi/libsas/sas_event.c @@ -22,10 +22,65 @@ * */ +#include #include #include "sas_internal.h" #include "sas_dump.h" +static void sas_queue_work(struct sas_ha_struct *ha, struct work_struct *work) +{ + if (!test_bit(SAS_HA_REGISTERED, &ha->state)) + return; + + if (test_bit(SAS_HA_DRAINING, &ha->state)) + list_add(&work->entry, &ha->defer_q); + else + scsi_queue_work(ha->core.shost, work); +} + +static void sas_queue_event(int event, unsigned long *pending, + struct work_struct *work, + struct sas_ha_struct *ha) +{ + if (!test_and_set_bit(event, pending)) { + unsigned long flags; + + spin_lock_irqsave(&ha->state_lock, flags); + sas_queue_work(ha, work); + spin_unlock_irqrestore(&ha->state_lock, flags); + } +} + +int sas_drain_work(struct sas_ha_struct *ha) +{ + struct workqueue_struct *wq = ha->core.shost->work_q; + struct work_struct *w, *_w; + int err; + + err = mutex_lock_interruptible(&ha->drain_mutex); + if (err) + return err; + + set_bit(SAS_HA_DRAINING, &ha->state); + /* flush submitters */ + spin_lock_irq(&ha->state_lock); + spin_unlock_irq(&ha->state_lock); + + drain_workqueue(wq); + + spin_lock_irq(&ha->state_lock); + clear_bit(SAS_HA_DRAINING, &ha->state); + list_for_each_entry_safe(w, _w, &ha->defer_q, entry) { + list_del_init(&w->entry); + sas_queue_work(ha, w); + } + spin_unlock_irq(&ha->state_lock); + mutex_unlock(&ha->drain_mutex); + + return 0; +} +EXPORT_SYMBOL_GPL(sas_drain_work); + static void notify_ha_event(struct sas_ha_struct *sas_ha, enum ha_event event) { BUG_ON(event >= HA_NUM_EVENTS); diff --git a/drivers/scsi/libsas/sas_init.c b/drivers/scsi/libsas/sas_init.c index da244e68fe6f..572b943d7603 100644 --- a/drivers/scsi/libsas/sas_init.c +++ b/drivers/scsi/libsas/sas_init.c @@ -114,6 +114,8 @@ int sas_register_ha(struct sas_ha_struct *sas_ha) set_bit(SAS_HA_REGISTERED, &sas_ha->state); spin_lock_init(&sas_ha->state_lock); + mutex_init(&sas_ha->drain_mutex); + INIT_LIST_HEAD(&sas_ha->defer_q); error = sas_register_phys(sas_ha); if (error) { @@ -157,12 +159,13 @@ int sas_unregister_ha(struct sas_ha_struct *sas_ha) { unsigned long flags; - /* Set the state to unregistered to avoid further - * events to be queued */ + /* Set the state to unregistered to avoid further unchained + * events to be queued + */ spin_lock_irqsave(&sas_ha->state_lock, flags); clear_bit(SAS_HA_REGISTERED, &sas_ha->state); spin_unlock_irqrestore(&sas_ha->state_lock, flags); - scsi_flush_work(sas_ha->core.shost); + sas_drain_work(sas_ha); sas_unregister_ports(sas_ha); diff --git a/drivers/scsi/libsas/sas_internal.h b/drivers/scsi/libsas/sas_internal.h index 1fd84b3f091f..948ea64cc2eb 100644 --- a/drivers/scsi/libsas/sas_internal.h +++ b/drivers/scsi/libsas/sas_internal.h @@ -92,20 +92,6 @@ static inline int sas_smp_host_handler(struct Scsi_Host *shost, } #endif -static inline void sas_queue_event(int event, unsigned long *pending, - struct work_struct *work, - struct sas_ha_struct *sas_ha) -{ - if (!test_and_set_bit(event, pending)) { - unsigned long flags; - - spin_lock_irqsave(&sas_ha->state_lock, flags); - if (test_bit(SAS_HA_REGISTERED, &sas_ha->state)) - scsi_queue_work(sas_ha->core.shost, work); - spin_unlock_irqrestore(&sas_ha->state_lock, flags); - } -} - static inline void sas_fill_in_rphy(struct domain_device *dev, struct sas_rphy *rphy) { diff --git a/drivers/scsi/mvsas/mv_sas.c b/drivers/scsi/mvsas/mv_sas.c index a4884a57cf79..b118e632bc7d 100644 --- a/drivers/scsi/mvsas/mv_sas.c +++ b/drivers/scsi/mvsas/mv_sas.c @@ -308,7 +308,7 @@ int mvs_scan_finished(struct Scsi_Host *shost, unsigned long time) if (mvs_prv->scan_finished == 0) return 0; - scsi_flush_work(shost); + sas_drain_work(sha); return 1; } diff --git a/drivers/scsi/pm8001/pm8001_sas.c b/drivers/scsi/pm8001/pm8001_sas.c index 9589fc941a8b..50837933a1e5 100644 --- a/drivers/scsi/pm8001/pm8001_sas.c +++ b/drivers/scsi/pm8001/pm8001_sas.c @@ -256,12 +256,14 @@ void pm8001_scan_start(struct Scsi_Host *shost) int pm8001_scan_finished(struct Scsi_Host *shost, unsigned long time) { + struct sas_ha_struct *ha = SHOST_TO_SAS_HA(shost); + /* give the phy enabling interrupt event time to come in (1s * is empirically about all it takes) */ if (time < HZ) return 0; /* Wait for discovery to finish */ - scsi_flush_work(shost); + sas_drain_work(ha); return 1; } diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h index 8e402d5a0640..42900fa95a03 100644 --- a/include/scsi/libsas.h +++ b/include/scsi/libsas.h @@ -330,6 +330,7 @@ struct sas_ha_event { enum sas_ha_state { SAS_HA_REGISTERED, + SAS_HA_DRAINING, }; struct sas_ha_struct { @@ -337,6 +338,8 @@ struct sas_ha_struct { struct sas_ha_event ha_events[HA_NUM_EVENTS]; unsigned long pending; + struct list_head defer_q; /* work queued while draining */ + struct mutex drain_mutex; unsigned long state; spinlock_t state_lock; @@ -657,6 +660,7 @@ int sas_eh_bus_reset_handler(struct scsi_cmnd *cmd); extern void sas_target_destroy(struct scsi_target *); extern int sas_slave_alloc(struct scsi_device *); extern int sas_ioctl(struct scsi_device *sdev, int cmd, void __user *arg); +extern int sas_drain_work(struct sas_ha_struct *ha); extern int sas_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy, struct request *req); -- cgit v1.2.3 From e139942d77a6e3ac83bc322e826668054a8601d6 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Sat, 7 Jan 2012 08:52:39 +0000 Subject: [SCSI] libsas: convert dev->gone to flags In preparation for adding tracking of another device state "destroy". Signed-off-by: Dan Williams Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_ata.c | 2 +- drivers/scsi/libsas/sas_expander.c | 6 +++--- drivers/scsi/libsas/sas_port.c | 2 +- drivers/scsi/libsas/sas_scsi_host.c | 2 +- include/scsi/libsas.h | 7 +++++-- 5 files changed, 11 insertions(+), 8 deletions(-) (limited to 'include/scsi') diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index 81ce39d166d1..2fc5a3961ca6 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -184,7 +184,7 @@ static unsigned int sas_ata_qc_issue(struct ata_queued_cmd *qc) spin_unlock(ap->lock); /* If the device fell off, no sense in issuing commands */ - if (dev->gone) + if (test_bit(SAS_DEV_GONE, &dev->state)) goto out; task = sas_alloc_task(GFP_ATOMIC); diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index 15d2239a378b..f33d0c9911c4 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c @@ -1750,7 +1750,7 @@ static void sas_unregister_ex_tree(struct asd_sas_port *port, struct domain_devi struct domain_device *child, *n; list_for_each_entry_safe(child, n, &ex->children, siblings) { - child->gone = 1; + set_bit(SAS_DEV_GONE, &child->state); if (child->dev_type == EDGE_DEV || child->dev_type == FANOUT_DEV) sas_unregister_ex_tree(port, child); @@ -1771,7 +1771,7 @@ static void sas_unregister_devs_sas_addr(struct domain_device *parent, &ex_dev->children, siblings) { if (SAS_ADDR(child->sas_addr) == SAS_ADDR(phy->attached_sas_addr)) { - child->gone = 1; + set_bit(SAS_DEV_GONE, &child->state); if (child->dev_type == EDGE_DEV || child->dev_type == FANOUT_DEV) sas_unregister_ex_tree(parent->port, child); @@ -1780,7 +1780,7 @@ static void sas_unregister_devs_sas_addr(struct domain_device *parent, break; } } - parent->gone = 1; + set_bit(SAS_DEV_GONE, &parent->state); sas_disable_routing(parent, phy->attached_sas_addr); } memset(phy->attached_sas_addr, 0, SAS_ADDR_SIZE); diff --git a/drivers/scsi/libsas/sas_port.c b/drivers/scsi/libsas/sas_port.c index a47c7a75327b..d88e55f9732b 100644 --- a/drivers/scsi/libsas/sas_port.c +++ b/drivers/scsi/libsas/sas_port.c @@ -171,7 +171,7 @@ void sas_deform_port(struct asd_sas_phy *phy, int gone) if (port->num_phys == 1) { if (dev && gone) - dev->gone = 1; + set_bit(SAS_DEV_GONE, &dev->state); sas_unregister_domain_devices(port); sas_port_delete(port->port); port->port = NULL; diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c index fd60465d4b2d..15533a17eb97 100644 --- a/drivers/scsi/libsas/sas_scsi_host.c +++ b/drivers/scsi/libsas/sas_scsi_host.c @@ -192,7 +192,7 @@ int sas_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd) int res = 0; /* If the device fell off, no sense in issuing commands */ - if (dev->gone) { + if (test_bit(SAS_DEV_GONE, &dev->state)) { cmd->result = DID_BAD_TARGET << 16; goto out_done; } diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h index 42900fa95a03..d792b13cfcf5 100644 --- a/include/scsi/libsas.h +++ b/include/scsi/libsas.h @@ -173,7 +173,10 @@ struct sata_device { struct ata_taskfile tf; }; -/* ---------- Domain device ---------- */ +enum { + SAS_DEV_GONE, +}; + struct domain_device { enum sas_dev_type dev_type; @@ -205,7 +208,7 @@ struct domain_device { }; void *lldd_dev; - int gone; + unsigned long state; struct kref kref; }; -- cgit v1.2.3 From 87c8331fcf72e501c3a3c0cdc5c9391ec72f7cf2 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 17 Nov 2011 17:59:51 -0800 Subject: [SCSI] libsas: prevent domain rediscovery competing with ata error handling libata error handling provides for a timeout for link recovery. libsas must not rescan for previously known devices in this interval otherwise it may remove a device that is simply waiting for its link to recover. Let libata-eh make the determination of when the link is stable and prevent libsas (host workqueue) from taking action while this determination is pending. Using a mutex (ha->disco_mutex) to flush and disable revalidation while eh is running requires any discovery action that may block on eh be moved to its own context outside the lock. Probing ATA devices explicitly waits on ata-eh and the cache-flush-io issued during device removal may also pend awaiting eh completion. Essentially any rphy add/remove activity needs to run outside the lock. This adds two new cleanup states for sas_unregister_domain_devices() 'allocated-but-not-probed', and 'flagged-for-destruction'. In the 'allocated-but-not-probed' state dev->rphy points to a rphy that is known to have not been through a sas_rphy_add() event. At domain teardown check if this device is still pending probe and cleanup accordingly. Similarly if a device has already been queued for removal then sas_unregister_domain_devices has nothing to do. Signed-off-by: Dan Williams Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_ata.c | 55 ++++++++++++++++++++++++++++++--- drivers/scsi/libsas/sas_discover.c | 63 ++++++++++++++++++++++++++++++++++---- drivers/scsi/libsas/sas_event.c | 26 ++++++++++++++++ drivers/scsi/libsas/sas_expander.c | 5 ++- drivers/scsi/libsas/sas_init.c | 2 ++ drivers/scsi/libsas/sas_internal.h | 3 ++ drivers/scsi/libsas/sas_port.c | 2 ++ drivers/scsi/scsi_transport_sas.c | 18 +++++++++-- include/scsi/libsas.h | 12 ++++++-- include/scsi/sas_ata.h | 5 +++ include/scsi/scsi_transport_sas.h | 1 + 11 files changed, 174 insertions(+), 18 deletions(-) (limited to 'include/scsi') diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index 2fc5a3961ca6..4b6365c6410f 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -758,6 +758,35 @@ static int sas_discover_sata_pm(struct domain_device *dev) return -ENODEV; } +void sas_probe_sata(struct work_struct *work) +{ + struct domain_device *dev, *n; + struct sas_discovery_event *ev = + container_of(work, struct sas_discovery_event, work); + struct asd_sas_port *port = ev->port; + + clear_bit(DISCE_PROBE, &port->disc.pending); + + list_for_each_entry_safe(dev, n, &port->disco_list, disco_list_node) { + int err; + + spin_lock_irq(&port->dev_list_lock); + list_add_tail(&dev->dev_list_node, &port->dev_list); + spin_unlock_irq(&port->dev_list_lock); + + err = sas_rphy_add(dev->rphy); + + if (err) { + SAS_DPRINTK("%s: for %s device %16llx returned %d\n", + __func__, dev->parent ? "exp-attached" : + "direct-attached", + SAS_ADDR(dev->sas_addr), err); + sas_unregister_dev(port, dev); + } else + list_del_init(&dev->disco_list_node); + } +} + /** * sas_discover_sata -- discover an STP/SATA domain device * @dev: pointer to struct domain_device of interest @@ -794,10 +823,15 @@ int sas_discover_sata(struct domain_device *dev) break; } sas_notify_lldd_dev_gone(dev); - if (!res) { - sas_notify_lldd_dev_found(dev); - res = sas_rphy_add(dev->rphy); - } + + if (res) + return res; + + res = sas_notify_lldd_dev_found(dev); + if (res) + return res; + + sas_discover_event(dev->port, DISCE_PROBE); return res; } @@ -805,6 +839,17 @@ int sas_discover_sata(struct domain_device *dev) void sas_ata_strategy_handler(struct Scsi_Host *shost) { struct scsi_device *sdev; + struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost); + + /* it's ok to defer revalidation events during ata eh, these + * disks are in one of three states: + * 1/ present for initial domain discovery, and these + * resets will cause bcn flutters + * 2/ hot removed, we'll discover that after eh fails + * 3/ hot added after initial discovery, lost the race, and need + * to catch the next train. + */ + sas_disable_revalidation(sas_ha); shost_for_each_device(sdev, shost) { struct domain_device *ddev = sdev_to_domain_dev(sdev); @@ -816,6 +861,8 @@ void sas_ata_strategy_handler(struct Scsi_Host *shost) ata_port_printk(ap, KERN_DEBUG, "sas eh calling libata port error handler"); ata_scsi_port_error_handler(shost, ap); } + + sas_enable_revalidation(sas_ha); } int sas_ata_timed_out(struct scsi_cmnd *cmd, struct sas_task *task, diff --git a/drivers/scsi/libsas/sas_discover.c b/drivers/scsi/libsas/sas_discover.c index 32e011766046..7e8fdcb202b7 100644 --- a/drivers/scsi/libsas/sas_discover.c +++ b/drivers/scsi/libsas/sas_discover.c @@ -148,9 +148,14 @@ static int sas_get_port_device(struct asd_sas_port *port) port->disc.max_level = 0; dev->rphy = rphy; - spin_lock_irq(&port->dev_list_lock); - list_add_tail(&dev->dev_list_node, &port->dev_list); - spin_unlock_irq(&port->dev_list_lock); + + if (dev_is_sata(dev)) + list_add_tail(&dev->disco_list_node, &port->disco_list); + else { + spin_lock_irq(&port->dev_list_lock); + list_add_tail(&dev->dev_list_node, &port->dev_list); + spin_unlock_irq(&port->dev_list_lock); + } return 0; } @@ -255,14 +260,43 @@ static void sas_unregister_common_dev(struct asd_sas_port *port, struct domain_d sas_put_device(dev); } -void sas_unregister_dev(struct asd_sas_port *port, struct domain_device *dev) +static void sas_destruct_devices(struct work_struct *work) { - if (dev->rphy) { + struct domain_device *dev, *n; + struct sas_discovery_event *ev = + container_of(work, struct sas_discovery_event, work); + struct asd_sas_port *port = ev->port; + + clear_bit(DISCE_DESTRUCT, &port->disc.pending); + + list_for_each_entry_safe(dev, n, &port->destroy_list, disco_list_node) { + list_del_init(&dev->disco_list_node); + sas_remove_children(&dev->rphy->dev); sas_rphy_delete(dev->rphy); dev->rphy = NULL; + sas_unregister_common_dev(port, dev); + + sas_put_device(dev); + } +} + +void sas_unregister_dev(struct asd_sas_port *port, struct domain_device *dev) +{ + if (!test_bit(SAS_DEV_DESTROY, &dev->state) && + !list_empty(&dev->disco_list_node)) { + /* this rphy never saw sas_rphy_add */ + list_del_init(&dev->disco_list_node); + sas_rphy_free(dev->rphy); + dev->rphy = NULL; + sas_unregister_common_dev(port, dev); + } + + if (dev->rphy && !test_and_set_bit(SAS_DEV_DESTROY, &dev->state)) { + sas_rphy_unlink(dev->rphy); + list_move_tail(&dev->disco_list_node, &port->destroy_list); + sas_discover_event(dev->port, DISCE_DESTRUCT); } - sas_unregister_common_dev(port, dev); } void sas_unregister_domain_devices(struct asd_sas_port *port) @@ -271,6 +305,8 @@ void sas_unregister_domain_devices(struct asd_sas_port *port) list_for_each_entry_safe_reverse(dev, n, &port->dev_list, dev_list_node) sas_unregister_dev(port, dev); + list_for_each_entry_safe(dev, n, &port->disco_list, disco_list_node) + sas_unregister_dev(port, dev); port->port->rphy = NULL; @@ -335,6 +371,7 @@ static void sas_discover_domain(struct work_struct *work) sas_rphy_free(dev->rphy); dev->rphy = NULL; + list_del_init(&dev->disco_list_node); spin_lock_irq(&port->dev_list_lock); list_del_init(&dev->dev_list_node); spin_unlock_irq(&port->dev_list_lock); @@ -353,16 +390,28 @@ static void sas_revalidate_domain(struct work_struct *work) struct sas_discovery_event *ev = container_of(work, struct sas_discovery_event, work); struct asd_sas_port *port = ev->port; + struct sas_ha_struct *ha = port->ha; + + /* prevent revalidation from finding sata links in recovery */ + mutex_lock(&ha->disco_mutex); + if (test_bit(SAS_HA_ATA_EH_ACTIVE, &ha->state)) { + SAS_DPRINTK("REVALIDATION DEFERRED on port %d, pid:%d\n", + port->id, task_pid_nr(current)); + goto out; + } clear_bit(DISCE_REVALIDATE_DOMAIN, &port->disc.pending); SAS_DPRINTK("REVALIDATING DOMAIN on port %d, pid:%d\n", port->id, task_pid_nr(current)); + if (port->port_dev) res = sas_ex_revalidate_domain(port->port_dev); SAS_DPRINTK("done REVALIDATING DOMAIN on port %d, pid:%d, res 0x%x\n", port->id, task_pid_nr(current), res); + out: + mutex_unlock(&ha->disco_mutex); } /* ---------- Events ---------- */ @@ -414,6 +463,8 @@ void sas_init_disc(struct sas_discovery *disc, struct asd_sas_port *port) static const work_func_t sas_event_fns[DISC_NUM_EVENTS] = { [DISCE_DISCOVER_DOMAIN] = sas_discover_domain, [DISCE_REVALIDATE_DOMAIN] = sas_revalidate_domain, + [DISCE_PROBE] = sas_probe_sata, + [DISCE_DESTRUCT] = sas_destruct_devices, }; disc->pending = 0; diff --git a/drivers/scsi/libsas/sas_event.c b/drivers/scsi/libsas/sas_event.c index e5035aa4c2a6..933d757499b5 100644 --- a/drivers/scsi/libsas/sas_event.c +++ b/drivers/scsi/libsas/sas_event.c @@ -81,6 +81,32 @@ int sas_drain_work(struct sas_ha_struct *ha) } EXPORT_SYMBOL_GPL(sas_drain_work); +void sas_disable_revalidation(struct sas_ha_struct *ha) +{ + mutex_lock(&ha->disco_mutex); + set_bit(SAS_HA_ATA_EH_ACTIVE, &ha->state); + mutex_unlock(&ha->disco_mutex); +} + +void sas_enable_revalidation(struct sas_ha_struct *ha) +{ + int i; + + mutex_lock(&ha->disco_mutex); + clear_bit(SAS_HA_ATA_EH_ACTIVE, &ha->state); + for (i = 0; i < ha->num_phys; i++) { + struct asd_sas_port *port = ha->sas_port[i]; + const int ev = DISCE_REVALIDATE_DOMAIN; + struct sas_discovery *d = &port->disc; + + if (!test_and_clear_bit(ev, &d->pending)) + continue; + + sas_queue_event(ev, &d->pending, &d->disc_work[ev].work, ha); + } + mutex_unlock(&ha->disco_mutex); +} + static void notify_ha_event(struct sas_ha_struct *sas_ha, enum ha_event event) { BUG_ON(event >= HA_NUM_EVENTS); diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index f33d0c9911c4..e45b259dac4c 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c @@ -704,9 +704,7 @@ static struct domain_device *sas_ex_discover_end_dev( child->rphy = rphy; - spin_lock_irq(&parent->port->dev_list_lock); - list_add_tail(&child->dev_list_node, &parent->port->dev_list); - spin_unlock_irq(&parent->port->dev_list_lock); + list_add_tail(&child->disco_list_node, &parent->port->disco_list); res = sas_discover_sata(child); if (res) { @@ -756,6 +754,7 @@ static struct domain_device *sas_ex_discover_end_dev( sas_rphy_free(child->rphy); child->rphy = NULL; + list_del(&child->disco_list_node); spin_lock_irq(&parent->port->dev_list_lock); list_del(&child->dev_list_node); spin_unlock_irq(&parent->port->dev_list_lock); diff --git a/drivers/scsi/libsas/sas_init.c b/drivers/scsi/libsas/sas_init.c index 572b943d7603..52cd11d76664 100644 --- a/drivers/scsi/libsas/sas_init.c +++ b/drivers/scsi/libsas/sas_init.c @@ -104,6 +104,7 @@ int sas_register_ha(struct sas_ha_struct *sas_ha) { int error = 0; + mutex_init(&sas_ha->disco_mutex); spin_lock_init(&sas_ha->phy_port_lock); sas_hash_addr(sas_ha->hashed_sas_addr, sas_ha->sas_addr); @@ -168,6 +169,7 @@ int sas_unregister_ha(struct sas_ha_struct *sas_ha) sas_drain_work(sas_ha); sas_unregister_ports(sas_ha); + sas_drain_work(sas_ha); if (sas_ha->lldd_max_execute_num > 1) { sas_shutdown_queue(sas_ha); diff --git a/drivers/scsi/libsas/sas_internal.h b/drivers/scsi/libsas/sas_internal.h index 948ea64cc2eb..ebe9b81ddef5 100644 --- a/drivers/scsi/libsas/sas_internal.h +++ b/drivers/scsi/libsas/sas_internal.h @@ -56,6 +56,8 @@ enum blk_eh_timer_return sas_scsi_timed_out(struct scsi_cmnd *); int sas_init_queue(struct sas_ha_struct *sas_ha); int sas_init_events(struct sas_ha_struct *sas_ha); void sas_shutdown_queue(struct sas_ha_struct *sas_ha); +void sas_disable_revalidation(struct sas_ha_struct *ha); +void sas_enable_revalidation(struct sas_ha_struct *ha); void sas_deform_port(struct asd_sas_phy *phy, int gone); @@ -138,6 +140,7 @@ static inline struct domain_device *sas_alloc_device(void) if (dev) { INIT_LIST_HEAD(&dev->siblings); INIT_LIST_HEAD(&dev->dev_list_node); + INIT_LIST_HEAD(&dev->disco_list_node); kref_init(&dev->kref); } return dev; diff --git a/drivers/scsi/libsas/sas_port.c b/drivers/scsi/libsas/sas_port.c index d88e55f9732b..2980bde4e34a 100644 --- a/drivers/scsi/libsas/sas_port.c +++ b/drivers/scsi/libsas/sas_port.c @@ -277,6 +277,8 @@ static void sas_init_port(struct asd_sas_port *port, memset(port, 0, sizeof(*port)); port->id = i; INIT_LIST_HEAD(&port->dev_list); + INIT_LIST_HEAD(&port->disco_list); + INIT_LIST_HEAD(&port->destroy_list); spin_lock_init(&port->phy_list_lock); INIT_LIST_HEAD(&port->phy_list); port->ha = sas_ha; diff --git a/drivers/scsi/scsi_transport_sas.c b/drivers/scsi/scsi_transport_sas.c index 9d9330ae4213..9421bae8af1a 100644 --- a/drivers/scsi/scsi_transport_sas.c +++ b/drivers/scsi/scsi_transport_sas.c @@ -1602,6 +1602,20 @@ sas_rphy_delete(struct sas_rphy *rphy) } EXPORT_SYMBOL(sas_rphy_delete); +/** + * sas_rphy_unlink - unlink SAS remote PHY + * @rphy: SAS remote phy to unlink from its parent port + * + * Removes port reference to an rphy + */ +void sas_rphy_unlink(struct sas_rphy *rphy) +{ + struct sas_port *parent = dev_to_sas_port(rphy->dev.parent); + + parent->rphy = NULL; +} +EXPORT_SYMBOL(sas_rphy_unlink); + /** * sas_rphy_remove - remove SAS remote PHY * @rphy: SAS remote phy to remove @@ -1612,7 +1626,6 @@ void sas_rphy_remove(struct sas_rphy *rphy) { struct device *dev = &rphy->dev; - struct sas_port *parent = dev_to_sas_port(dev->parent); switch (rphy->identify.device_type) { case SAS_END_DEVICE: @@ -1626,10 +1639,9 @@ sas_rphy_remove(struct sas_rphy *rphy) break; } + sas_rphy_unlink(rphy); transport_remove_device(dev); device_del(dev); - - parent->rphy = NULL; } EXPORT_SYMBOL(sas_rphy_remove); diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h index d792b13cfcf5..bd6e89ece2ab 100644 --- a/include/scsi/libsas.h +++ b/include/scsi/libsas.h @@ -86,7 +86,9 @@ enum discover_event { DISCE_DISCOVER_DOMAIN = 0U, DISCE_REVALIDATE_DOMAIN = 1, DISCE_PORT_GONE = 2, - DISC_NUM_EVENTS = 3, + DISCE_PROBE = 3, + DISCE_DESTRUCT = 4, + DISC_NUM_EVENTS = 5, }; /* ---------- Expander Devices ---------- */ @@ -175,6 +177,7 @@ struct sata_device { enum { SAS_DEV_GONE, + SAS_DEV_DESTROY, }; struct domain_device { @@ -191,6 +194,7 @@ struct domain_device { struct asd_sas_port *port; /* shortcut to root of the tree */ struct list_head dev_list_node; + struct list_head disco_list_node; /* awaiting probe or destruct */ enum sas_protocol iproto; enum sas_protocol tproto; @@ -226,7 +230,6 @@ struct sas_discovery { int max_level; }; - /* The port struct is Class:RW, driver:RO */ struct asd_sas_port { /* private: */ @@ -236,6 +239,8 @@ struct asd_sas_port { struct domain_device *port_dev; spinlock_t dev_list_lock; struct list_head dev_list; + struct list_head disco_list; + struct list_head destroy_list; enum sas_linkrate linkrate; struct sas_phy *phy; @@ -334,6 +339,7 @@ struct sas_ha_event { enum sas_ha_state { SAS_HA_REGISTERED, SAS_HA_DRAINING, + SAS_HA_ATA_EH_ACTIVE, }; struct sas_ha_struct { @@ -346,6 +352,8 @@ struct sas_ha_struct { unsigned long state; spinlock_t state_lock; + struct mutex disco_mutex; + struct scsi_core core; /* public: */ diff --git a/include/scsi/sas_ata.h b/include/scsi/sas_ata.h index 7d5013f8653d..557fc9a8559b 100644 --- a/include/scsi/sas_ata.h +++ b/include/scsi/sas_ata.h @@ -45,6 +45,7 @@ int sas_ata_timed_out(struct scsi_cmnd *cmd, struct sas_task *task, enum blk_eh_timer_return *rtn); int sas_ata_eh(struct Scsi_Host *shost, struct list_head *work_q, struct list_head *done_q); +void sas_probe_sata(struct work_struct *work); #else @@ -78,6 +79,10 @@ static inline int sas_ata_eh(struct Scsi_Host *shost, struct list_head *work_q, return 0; } +static inline void sas_probe_sata(struct work_struct *work) +{ +} + #endif #endif /* _SAS_ATA_H_ */ diff --git a/include/scsi/scsi_transport_sas.h b/include/scsi/scsi_transport_sas.h index ffeebc34a4f7..6d14daac7589 100644 --- a/include/scsi/scsi_transport_sas.h +++ b/include/scsi/scsi_transport_sas.h @@ -194,6 +194,7 @@ void sas_rphy_free(struct sas_rphy *); extern int sas_rphy_add(struct sas_rphy *); extern void sas_rphy_remove(struct sas_rphy *); extern void sas_rphy_delete(struct sas_rphy *); +extern void sas_rphy_unlink(struct sas_rphy *); extern int scsi_is_sas_rphy(const struct device *); struct sas_port *sas_port_alloc(struct device *, int); -- cgit v1.2.3 From b91bb296188118eea9fdc6093cfcf76bbe8589ba Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 17 Nov 2011 17:59:52 -0800 Subject: [SCSI] libsas: use ->set_dmamode to notify lldds of NCQ parameters sas_discover_sata() notifies lldds of sata devices twice. Once to allow the 'identify' to be sent, and a second time to allow aic94xx (the only libsas driver that cares about sata_dev.identify) to setup NCQ parameters before the device becomes known to the midlayer. Replace this double notification and intervening 'identify' with an explicit ->lldd_ata_set_dmamode notification. With this change all ata internal commands are issued by libata, so we no longer need sas_issue_ata_cmd(). The data from the identify command only needs to be cached in one location so ata_device.id replaces domain_device.sata_dev.identify. Signed-off-by: Dan Williams Signed-off-by: James Bottomley --- drivers/scsi/aic94xx/aic94xx.h | 2 + drivers/scsi/aic94xx/aic94xx_dev.c | 38 +++-- drivers/scsi/aic94xx/aic94xx_init.c | 2 + drivers/scsi/libsas/sas_ata.c | 324 +++--------------------------------- drivers/scsi/libsas/sas_discover.c | 5 - include/scsi/libsas.h | 4 +- 6 files changed, 49 insertions(+), 326 deletions(-) (limited to 'include/scsi') diff --git a/drivers/scsi/aic94xx/aic94xx.h b/drivers/scsi/aic94xx/aic94xx.h index 2863a9d22851..66cda669b417 100644 --- a/drivers/scsi/aic94xx/aic94xx.h +++ b/drivers/scsi/aic94xx/aic94xx.h @@ -80,6 +80,8 @@ void asd_invalidate_edb(struct asd_ascb *ascb, int edb_id); int asd_execute_task(struct sas_task *, int num, gfp_t gfp_flags); +void asd_set_dmamode(struct domain_device *dev); + /* ---------- TMFs ---------- */ int asd_abort_task(struct sas_task *); int asd_abort_task_set(struct domain_device *, u8 *lun); diff --git a/drivers/scsi/aic94xx/aic94xx_dev.c b/drivers/scsi/aic94xx/aic94xx_dev.c index 2e2ddec9c0b6..64136c56e706 100644 --- a/drivers/scsi/aic94xx/aic94xx_dev.c +++ b/drivers/scsi/aic94xx/aic94xx_dev.c @@ -109,26 +109,37 @@ static int asd_init_sata_tag_ddb(struct domain_device *dev) return 0; } -static int asd_init_sata(struct domain_device *dev) +void asd_set_dmamode(struct domain_device *dev) { struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha; + struct ata_device *ata_dev = sas_to_ata_dev(dev); int ddb = (int) (unsigned long) dev->lldd_dev; u32 qdepth = 0; - int res = 0; - asd_ddbsite_write_word(asd_ha, ddb, ATA_CMD_SCBPTR, 0xFFFF); - if ((dev->dev_type == SATA_DEV || dev->dev_type == SATA_PM_PORT) && - dev->sata_dev.identify_device && - dev->sata_dev.identify_device[10] != 0) { - u16 w75 = le16_to_cpu(dev->sata_dev.identify_device[75]); - u16 w76 = le16_to_cpu(dev->sata_dev.identify_device[76]); - - if (w76 & 0x100) /* NCQ? */ - qdepth = (w75 & 0x1F) + 1; + if (dev->dev_type == SATA_DEV || dev->dev_type == SATA_PM_PORT) { + if (ata_id_has_ncq(ata_dev->id)) + qdepth = ata_id_queue_depth(ata_dev->id); asd_ddbsite_write_dword(asd_ha, ddb, SATA_TAG_ALLOC_MASK, (1ULL< 0) + if (asd_init_sata_tag_ddb(dev) != 0) { + unsigned long flags; + + spin_lock_irqsave(dev->sata_dev.ap->lock, flags); + ata_dev->flags |= ATA_DFLAG_NCQ_OFF; + spin_unlock_irqrestore(dev->sata_dev.ap->lock, flags); + } +} + +static int asd_init_sata(struct domain_device *dev) +{ + struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha; + int ddb = (int) (unsigned long) dev->lldd_dev; + + asd_ddbsite_write_word(asd_ha, ddb, ATA_CMD_SCBPTR, 0xFFFF); if (dev->dev_type == SATA_DEV || dev->dev_type == SATA_PM || dev->dev_type == SATA_PM_PORT) { struct dev_to_host_fis *fis = (struct dev_to_host_fis *) @@ -136,9 +147,8 @@ static int asd_init_sata(struct domain_device *dev) asd_ddbsite_write_byte(asd_ha, ddb, SATA_STATUS, fis->status); } asd_ddbsite_write_word(asd_ha, ddb, NCQ_DATA_SCB_PTR, 0xFFFF); - if (qdepth > 0) - res = asd_init_sata_tag_ddb(dev); - return res; + + return 0; } static int asd_init_target_ddb(struct domain_device *dev) diff --git a/drivers/scsi/aic94xx/aic94xx_init.c b/drivers/scsi/aic94xx/aic94xx_init.c index 2b3717f6d22c..eea988a04f92 100644 --- a/drivers/scsi/aic94xx/aic94xx_init.c +++ b/drivers/scsi/aic94xx/aic94xx_init.c @@ -1009,6 +1009,8 @@ static struct sas_domain_function_template aic94xx_transport_functions = { .lldd_clear_nexus_ha = asd_clear_nexus_ha, .lldd_control_phy = asd_control_phy, + + .lldd_ata_set_dmamode = asd_set_dmamode, }; static const struct pci_device_id aic94xx_pci_table[] __devinitdata = { diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index 4b6365c6410f..71af919b856c 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -367,6 +367,17 @@ static void sas_ata_post_internal(struct ata_queued_cmd *qc) } } + +static void sas_ata_set_dmamode(struct ata_port *ap, struct ata_device *ata_dev) +{ + struct domain_device *dev = ap->private_data; + struct sas_internal *i = + to_sas_internal(dev->port->ha->core.shost->transportt); + + if (i->dft->lldd_ata_set_dmamode) + i->dft->lldd_ata_set_dmamode(dev); +} + static struct ata_port_operations sas_sata_ops = { .prereset = ata_std_prereset, .softreset = sas_ata_soft_reset, @@ -380,6 +391,7 @@ static struct ata_port_operations sas_sata_ops = { .qc_fill_rtf = sas_ata_qc_fill_rtf, .port_start = ata_sas_port_start, .port_stop = ata_sas_port_stop, + .set_dmamode = sas_ata_set_dmamode, }; static struct ata_port_info sata_port_info = { @@ -442,163 +454,6 @@ void sas_ata_task_abort(struct sas_task *task) complete(waiting); } -static void sas_task_timedout(unsigned long _task) -{ - struct sas_task *task = (void *) _task; - unsigned long flags; - - spin_lock_irqsave(&task->task_state_lock, flags); - if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) - task->task_state_flags |= SAS_TASK_STATE_ABORTED; - spin_unlock_irqrestore(&task->task_state_lock, flags); - - complete(&task->completion); -} - -static void sas_disc_task_done(struct sas_task *task) -{ - if (!del_timer(&task->timer)) - return; - complete(&task->completion); -} - -#define SAS_DEV_TIMEOUT 10 - -/** - * sas_execute_task -- Basic task processing for discovery - * @task: the task to be executed - * @buffer: pointer to buffer to do I/O - * @size: size of @buffer - * @dma_dir: DMA direction. DMA_xxx - */ -static int sas_execute_task(struct sas_task *task, void *buffer, int size, - enum dma_data_direction dma_dir) -{ - int res = 0; - struct scatterlist *scatter = NULL; - struct task_status_struct *ts = &task->task_status; - int num_scatter = 0; - int retries = 0; - struct sas_internal *i = - to_sas_internal(task->dev->port->ha->core.shost->transportt); - - if (dma_dir != DMA_NONE) { - scatter = kzalloc(sizeof(*scatter), GFP_KERNEL); - if (!scatter) - goto out; - - sg_init_one(scatter, buffer, size); - num_scatter = 1; - } - - task->task_proto = task->dev->tproto; - task->scatter = scatter; - task->num_scatter = num_scatter; - task->total_xfer_len = size; - task->data_dir = dma_dir; - task->task_done = sas_disc_task_done; - if (dma_dir != DMA_NONE && - sas_protocol_ata(task->task_proto)) { - task->num_scatter = dma_map_sg(task->dev->port->ha->dev, - task->scatter, - task->num_scatter, - task->data_dir); - } - - for (retries = 0; retries < 5; retries++) { - task->task_state_flags = SAS_TASK_STATE_PENDING; - init_completion(&task->completion); - - task->timer.data = (unsigned long) task; - task->timer.function = sas_task_timedout; - task->timer.expires = jiffies + SAS_DEV_TIMEOUT*HZ; - add_timer(&task->timer); - - res = i->dft->lldd_execute_task(task, 1, GFP_KERNEL); - if (res) { - del_timer(&task->timer); - SAS_DPRINTK("executing SAS discovery task failed:%d\n", - res); - goto ex_err; - } - wait_for_completion(&task->completion); - res = -ECOMM; - if (task->task_state_flags & SAS_TASK_STATE_ABORTED) { - int res2; - SAS_DPRINTK("task aborted, flags:0x%x\n", - task->task_state_flags); - res2 = i->dft->lldd_abort_task(task); - SAS_DPRINTK("came back from abort task\n"); - if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) { - if (res2 == TMF_RESP_FUNC_COMPLETE) - continue; /* Retry the task */ - else - goto ex_err; - } - } - if (task->task_status.stat == SAM_STAT_BUSY || - task->task_status.stat == SAM_STAT_TASK_SET_FULL || - task->task_status.stat == SAS_QUEUE_FULL) { - SAS_DPRINTK("task: q busy, sleeping...\n"); - schedule_timeout_interruptible(HZ); - } else if (task->task_status.stat == SAM_STAT_CHECK_CONDITION) { - struct scsi_sense_hdr shdr; - - if (!scsi_normalize_sense(ts->buf, ts->buf_valid_size, - &shdr)) { - SAS_DPRINTK("couldn't normalize sense\n"); - continue; - } - if ((shdr.sense_key == 6 && shdr.asc == 0x29) || - (shdr.sense_key == 2 && shdr.asc == 4 && - shdr.ascq == 1)) { - SAS_DPRINTK("device %016llx LUN: %016llx " - "powering up or not ready yet, " - "sleeping...\n", - SAS_ADDR(task->dev->sas_addr), - SAS_ADDR(task->ssp_task.LUN)); - - schedule_timeout_interruptible(5*HZ); - } else if (shdr.sense_key == 1) { - res = 0; - break; - } else if (shdr.sense_key == 5) { - break; - } else { - SAS_DPRINTK("dev %016llx LUN: %016llx " - "sense key:0x%x ASC:0x%x ASCQ:0x%x" - "\n", - SAS_ADDR(task->dev->sas_addr), - SAS_ADDR(task->ssp_task.LUN), - shdr.sense_key, - shdr.asc, shdr.ascq); - } - } else if (task->task_status.resp != SAS_TASK_COMPLETE || - task->task_status.stat != SAM_STAT_GOOD) { - SAS_DPRINTK("task finished with resp:0x%x, " - "stat:0x%x\n", - task->task_status.resp, - task->task_status.stat); - goto ex_err; - } else { - res = 0; - break; - } - } -ex_err: - if (dma_dir != DMA_NONE) { - if (sas_protocol_ata(task->task_proto)) - dma_unmap_sg(task->dev->port->ha->dev, - task->scatter, task->num_scatter, - task->data_dir); - kfree(scatter); - } -out: - return res; -} - -/* ---------- SATA ---------- */ - static void sas_get_ata_command_set(struct domain_device *dev) { struct dev_to_host_fis *fis = @@ -642,122 +497,6 @@ static void sas_get_ata_command_set(struct domain_device *dev) dev->sata_dev.command_set = ATAPI_COMMAND_SET; } -/** - * sas_issue_ata_cmd -- Basic SATA command processing for discovery - * @dev: the device to send the command to - * @command: the command register - * @features: the features register - * @buffer: pointer to buffer to do I/O - * @size: size of @buffer - * @dma_dir: DMA direction. DMA_xxx - */ -static int sas_issue_ata_cmd(struct domain_device *dev, u8 command, - u8 features, void *buffer, int size, - enum dma_data_direction dma_dir) -{ - int res = 0; - struct sas_task *task; - struct dev_to_host_fis *d2h_fis = (struct dev_to_host_fis *) - &dev->frame_rcvd[0]; - - res = -ENOMEM; - task = sas_alloc_task(GFP_KERNEL); - if (!task) - goto out; - - task->dev = dev; - - task->ata_task.fis.fis_type = 0x27; - task->ata_task.fis.command = command; - task->ata_task.fis.features = features; - task->ata_task.fis.device = d2h_fis->device; - task->ata_task.retry_count = 1; - - res = sas_execute_task(task, buffer, size, dma_dir); - - sas_free_task(task); -out: - return res; -} - -#define ATA_IDENTIFY_DEV 0xEC -#define ATA_IDENTIFY_PACKET_DEV 0xA1 -#define ATA_SET_FEATURES 0xEF -#define ATA_FEATURE_PUP_STBY_SPIN_UP 0x07 - -/** - * sas_discover_sata_dev -- discover a STP/SATA device (SATA_DEV) - * @dev: STP/SATA device of interest (ATA/ATAPI) - * - * The LLDD has already been notified of this device, so that we can - * send FISes to it. Here we try to get IDENTIFY DEVICE or IDENTIFY - * PACKET DEVICE, if ATAPI device, so that the LLDD can fine-tune its - * performance for this device. - */ -static int sas_discover_sata_dev(struct domain_device *dev) -{ - int res; - __le16 *identify_x; - u8 command; - - identify_x = kzalloc(512, GFP_KERNEL); - if (!identify_x) - return -ENOMEM; - - if (dev->sata_dev.command_set == ATA_COMMAND_SET) { - dev->sata_dev.identify_device = identify_x; - command = ATA_IDENTIFY_DEV; - } else { - dev->sata_dev.identify_packet_device = identify_x; - command = ATA_IDENTIFY_PACKET_DEV; - } - - res = sas_issue_ata_cmd(dev, command, 0, identify_x, 512, - DMA_FROM_DEVICE); - if (res) - goto out_err; - - /* lives on the media? */ - if (le16_to_cpu(identify_x[0]) & 4) { - /* incomplete response */ - SAS_DPRINTK("sending SET FEATURE/PUP_STBY_SPIN_UP to " - "dev %llx\n", SAS_ADDR(dev->sas_addr)); - if (!(identify_x[83] & cpu_to_le16(1<<6))) - goto cont1; - res = sas_issue_ata_cmd(dev, ATA_SET_FEATURES, - ATA_FEATURE_PUP_STBY_SPIN_UP, - NULL, 0, DMA_NONE); - if (res) - goto cont1; - - schedule_timeout_interruptible(5*HZ); /* More time? */ - res = sas_issue_ata_cmd(dev, command, 0, identify_x, 512, - DMA_FROM_DEVICE); - if (res) - goto out_err; - } -cont1: - /* XXX Hint: register this SATA device with SATL. - When this returns, dev->sata_dev->lu is alive and - present. - sas_satl_register_dev(dev); - */ - - sas_fill_in_rphy(dev, dev->rphy); - - return 0; -out_err: - dev->sata_dev.identify_packet_device = NULL; - dev->sata_dev.identify_device = NULL; - kfree(identify_x); - return res; -} - -static int sas_discover_sata_pm(struct domain_device *dev) -{ - return -ENODEV; -} - void sas_probe_sata(struct work_struct *work) { struct domain_device *dev, *n; @@ -791,49 +530,26 @@ void sas_probe_sata(struct work_struct *work) * sas_discover_sata -- discover an STP/SATA domain device * @dev: pointer to struct domain_device of interest * - * First we notify the LLDD of this device, so we can send frames to - * it. Then depending on the type of device we call the appropriate - * discover functions. Once device discover is done, we notify the - * LLDD so that it can fine-tune its parameters for the device, by - * removing it and then adding it. That is, the second time around, - * the driver would have certain fields, that it is looking at, set. - * Finally we initialize the kobj so that the device can be added to - * the system at registration time. Devices directly attached to a HA - * port, have no parents. All other devices do, and should have their - * "parent" pointer set appropriately before calling this function. + * Devices directly attached to a HA port, have no parents. All other + * devices do, and should have their "parent" pointer set appropriately + * before calling this function. */ int sas_discover_sata(struct domain_device *dev) { int res; - sas_get_ata_command_set(dev); - - res = sas_notify_lldd_dev_found(dev); - if (res) - return res; - - switch (dev->dev_type) { - case SATA_DEV: - res = sas_discover_sata_dev(dev); - break; - case SATA_PM: - res = sas_discover_sata_pm(dev); - break; - default: - break; - } - sas_notify_lldd_dev_gone(dev); + if (dev->dev_type == SATA_PM) + return -ENODEV; - if (res) - return res; + sas_get_ata_command_set(dev); + sas_fill_in_rphy(dev, dev->rphy); res = sas_notify_lldd_dev_found(dev); if (res) return res; sas_discover_event(dev->port, DISCE_PROBE); - - return res; + return 0; } void sas_ata_strategy_handler(struct Scsi_Host *shost) diff --git a/drivers/scsi/libsas/sas_discover.c b/drivers/scsi/libsas/sas_discover.c index 7e8fdcb202b7..bad5eba4a92b 100644 --- a/drivers/scsi/libsas/sas_discover.c +++ b/drivers/scsi/libsas/sas_discover.c @@ -237,11 +237,6 @@ void sas_free_device(struct kref *kref) if (dev->dev_type == EDGE_DEV || dev->dev_type == FANOUT_DEV) kfree(dev->ex_dev.ex_phy); - if (dev_is_sata(dev)) { - kfree(dev->sata_dev.identify_device); - kfree(dev->sata_dev.identify_packet_device); - } - kfree(dev); } diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h index bd6e89ece2ab..9c13a5c0bb3a 100644 --- a/include/scsi/libsas.h +++ b/include/scsi/libsas.h @@ -164,9 +164,6 @@ enum ata_command_set { struct sata_device { enum ata_command_set command_set; struct smp_resp rps_resp; /* report_phy_sata_resp */ - __le16 *identify_device; - __le16 *identify_packet_device; - u8 port_no; /* port number, if this is a PM (Port) */ struct list_head children; /* PM Ports if this is a PM */ @@ -609,6 +606,7 @@ struct sas_domain_function_template { int (*lldd_clear_task_set)(struct domain_device *, u8 *lun); int (*lldd_I_T_nexus_reset)(struct domain_device *); int (*lldd_ata_soft_reset)(struct domain_device *); + void (*lldd_ata_set_dmamode)(struct domain_device *); int (*lldd_lu_reset)(struct domain_device *, u8 *lun); int (*lldd_query_task)(struct sas_task *); -- cgit v1.2.3 From 3dff5721e4f67e6231dfc419d30aaa7563bfffd4 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Mon, 28 Nov 2011 12:08:22 -0800 Subject: [SCSI] libsas: close error handling vs sas_ata_task_done() race Since sas_ata does not implement ->freeze(), completions for scmds and internal commands can still arrive concurrent with ata_scsi_cmd_error_handler() and sas_ata_post_internal() respectively. By the time either of those is called libata has committed to completing the qc, and the ATA_PFLAG_FROZEN flag tells sas_ata_task_done() it has lost the race. In the sas_ata_post_internal() case we take on the additional responsibility of freeing the sas_task to close the race with sas_ata_task_done() freeing the the task while sas_ata_post_internal() is in the process of invoking ->lldd_abort_task(). Signed-off-by: Dan Williams Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_ata.c | 84 +++++++++++++++++++++++++++++++++---- drivers/scsi/libsas/sas_scsi_host.c | 44 ------------------- include/scsi/libsas.h | 1 - 3 files changed, 75 insertions(+), 54 deletions(-) (limited to 'include/scsi') diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index 5cb0a2ae5924..903bb441b9f9 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -100,15 +100,31 @@ static void sas_ata_task_done(struct sas_task *task) enum ata_completion_errors ac; unsigned long flags; struct ata_link *link; + struct ata_port *ap; if (!qc) goto qc_already_gone; - dev = qc->ap->private_data; + ap = qc->ap; + dev = ap->private_data; sas_ha = dev->port->ha; - link = &dev->sata_dev.ap->link; + link = &ap->link; + + spin_lock_irqsave(ap->lock, flags); + /* check if we lost the race with libata/sas_ata_post_internal() */ + if (unlikely(ap->pflags & ATA_PFLAG_FROZEN)) { + spin_unlock_irqrestore(ap->lock, flags); + if (qc->scsicmd) + goto qc_already_gone; + else { + /* if eh is not involved and the port is frozen then the + * ata internal abort process has taken responsibility + * for this sas_task + */ + return; + } + } - spin_lock_irqsave(dev->sata_dev.ap->lock, flags); if (stat->stat == SAS_PROTO_RESPONSE || stat->stat == SAM_STAT_GOOD || ((stat->stat == SAM_STAT_CHECK_CONDITION && dev->sata_dev.command_set == ATAPI_COMMAND_SET))) { @@ -143,7 +159,7 @@ static void sas_ata_task_done(struct sas_task *task) if (qc->scsicmd) ASSIGN_SAS_TASK(qc->scsicmd, NULL); ata_qc_complete(qc); - spin_unlock_irqrestore(dev->sata_dev.ap->lock, flags); + spin_unlock_irqrestore(ap->lock, flags); qc_already_gone: list_del_init(&task->list); @@ -325,6 +341,54 @@ static int sas_ata_soft_reset(struct ata_link *link, unsigned int *class, return ret; } +/* + * notify the lldd to forget the sas_task for this internal ata command + * that bypasses scsi-eh + */ +static void sas_ata_internal_abort(struct sas_task *task) +{ + struct sas_internal *si = + to_sas_internal(task->dev->port->ha->core.shost->transportt); + unsigned long flags; + int res; + + spin_lock_irqsave(&task->task_state_lock, flags); + if (task->task_state_flags & SAS_TASK_STATE_ABORTED || + task->task_state_flags & SAS_TASK_STATE_DONE) { + spin_unlock_irqrestore(&task->task_state_lock, flags); + SAS_DPRINTK("%s: Task %p already finished.\n", __func__, + task); + goto out; + } + task->task_state_flags |= SAS_TASK_STATE_ABORTED; + spin_unlock_irqrestore(&task->task_state_lock, flags); + + res = si->dft->lldd_abort_task(task); + + spin_lock_irqsave(&task->task_state_lock, flags); + if (task->task_state_flags & SAS_TASK_STATE_DONE || + res == TMF_RESP_FUNC_COMPLETE) { + spin_unlock_irqrestore(&task->task_state_lock, flags); + goto out; + } + + /* XXX we are not prepared to deal with ->lldd_abort_task() + * failures. TODO: lldds need to unconditionally forget about + * aborted ata tasks, otherwise we (likely) leak the sas task + * here + */ + SAS_DPRINTK("%s: Task %p leaked.\n", __func__, task); + + if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) + task->task_state_flags &= ~SAS_TASK_STATE_ABORTED; + spin_unlock_irqrestore(&task->task_state_lock, flags); + + return; + out: + list_del_init(&task->list); + sas_free_task(task); +} + static void sas_ata_post_internal(struct ata_queued_cmd *qc) { if (qc->flags & ATA_QCFLAG_FAILED) @@ -332,10 +396,12 @@ static void sas_ata_post_internal(struct ata_queued_cmd *qc) if (qc->err_mask) { /* - * Find the sas_task and kill it. By this point, - * libata has decided to kill the qc, so we needn't - * bother with sas_ata_task_done. But we still - * ought to abort the task. + * Find the sas_task and kill it. By this point, libata + * has decided to kill the qc and has frozen the port. + * In this state sas_ata_task_done() will no longer free + * the sas_task, so we need to notify the lldd (via + * ->lldd_abort_task) that the task is dead and free it + * ourselves. */ struct sas_task *task = qc->lldd_task; unsigned long flags; @@ -348,7 +414,7 @@ static void sas_ata_post_internal(struct ata_queued_cmd *qc) spin_unlock_irqrestore(&task->task_state_lock, flags); task->uldd_task = NULL; - __sas_task_abort(task); + sas_ata_internal_abort(task); } } } diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c index 15533a17eb97..ba5876ccd29a 100644 --- a/drivers/scsi/libsas/sas_scsi_host.c +++ b/drivers/scsi/libsas/sas_scsi_host.c @@ -956,49 +956,6 @@ void sas_shutdown_queue(struct sas_ha_struct *sas_ha) spin_unlock_irqrestore(&core->task_queue_lock, flags); } -/* - * Call the LLDD task abort routine directly. This function is intended for - * use by upper layers that need to tell the LLDD to abort a task. - */ -int __sas_task_abort(struct sas_task *task) -{ - struct sas_internal *si = - to_sas_internal(task->dev->port->ha->core.shost->transportt); - unsigned long flags; - int res; - - spin_lock_irqsave(&task->task_state_lock, flags); - if (task->task_state_flags & SAS_TASK_STATE_ABORTED || - task->task_state_flags & SAS_TASK_STATE_DONE) { - spin_unlock_irqrestore(&task->task_state_lock, flags); - SAS_DPRINTK("%s: Task %p already finished.\n", __func__, - task); - return 0; - } - task->task_state_flags |= SAS_TASK_STATE_ABORTED; - spin_unlock_irqrestore(&task->task_state_lock, flags); - - if (!si->dft->lldd_abort_task) - return -ENODEV; - - res = si->dft->lldd_abort_task(task); - - spin_lock_irqsave(&task->task_state_lock, flags); - if ((task->task_state_flags & SAS_TASK_STATE_DONE) || - (res == TMF_RESP_FUNC_COMPLETE)) - { - spin_unlock_irqrestore(&task->task_state_lock, flags); - task->task_done(task); - return 0; - } - - if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) - task->task_state_flags &= ~SAS_TASK_STATE_ABORTED; - spin_unlock_irqrestore(&task->task_state_lock, flags); - - return -EAGAIN; -} - /* * Tell an upper layer that it needs to initiate an abort for a given task. * This should only ever be called by an LLDD. @@ -1097,7 +1054,6 @@ EXPORT_SYMBOL_GPL(sas_slave_configure); EXPORT_SYMBOL_GPL(sas_change_queue_depth); EXPORT_SYMBOL_GPL(sas_change_queue_type); EXPORT_SYMBOL_GPL(sas_bios_param); -EXPORT_SYMBOL_GPL(__sas_task_abort); EXPORT_SYMBOL_GPL(sas_task_abort); EXPORT_SYMBOL_GPL(sas_phy_reset); EXPORT_SYMBOL_GPL(sas_phy_enable); diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h index 9c13a5c0bb3a..10eb2ea74431 100644 --- a/include/scsi/libsas.h +++ b/include/scsi/libsas.h @@ -662,7 +662,6 @@ void sas_unregister_dev(struct asd_sas_port *port, struct domain_device *); void sas_init_dev(struct domain_device *); void sas_task_abort(struct sas_task *); -int __sas_task_abort(struct sas_task *); int sas_eh_device_reset_handler(struct scsi_cmnd *cmd); int sas_eh_bus_reset_handler(struct scsi_cmnd *cmd); -- cgit v1.2.3 From a3a142524aa4b1539a64a55087bf12ffa4b1f94e Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 6 Dec 2011 23:24:42 -0800 Subject: [SCSI] libsas: prevent double completion of scmds from eh We invoke task->task_done() to free the task in the eh case, but at this point we are prepared for scsi_eh_flush_done_q() to finish off the scmd. Introduce sas_end_task() to capture the final response status from the lldd and free the task. Also take the opportunity to kill this warning. drivers/scsi/libsas/sas_scsi_host.c: In function ‘sas_end_task’: drivers/scsi/libsas/sas_scsi_host.c:102:3: warning: case value ‘2’ not in enumerated type ‘enum exec_status’ [-Wswitch] Signed-off-by: Dan Williams Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_scsi_host.c | 61 ++++++++++++++++++++----------------- include/scsi/libsas.h | 5 ++- 2 files changed, 37 insertions(+), 29 deletions(-) (limited to 'include/scsi') diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c index ba5876ccd29a..50db8f971a06 100644 --- a/drivers/scsi/libsas/sas_scsi_host.c +++ b/drivers/scsi/libsas/sas_scsi_host.c @@ -49,27 +49,12 @@ #include #include -/* ---------- SCSI Host glue ---------- */ - -static void sas_scsi_task_done(struct sas_task *task) +/* record final status and free the task */ +static void sas_end_task(struct scsi_cmnd *sc, struct sas_task *task) { struct task_status_struct *ts = &task->task_status; - struct scsi_cmnd *sc = task->uldd_task; int hs = 0, stat = 0; - if (unlikely(task->task_state_flags & SAS_TASK_STATE_ABORTED)) { - /* Aborted tasks will be completed by the error handler */ - SAS_DPRINTK("task done but aborted\n"); - return; - } - - if (unlikely(!sc)) { - SAS_DPRINTK("task_done called with non existing SCSI cmnd!\n"); - list_del_init(&task->list); - sas_free_task(task); - return; - } - if (ts->resp == SAS_TASK_UNDELIVERED) { /* transport error */ hs = DID_NO_CONNECT; @@ -124,10 +109,32 @@ static void sas_scsi_task_done(struct sas_task *task) break; } } - ASSIGN_SAS_TASK(sc, NULL); + sc->result = (hs << 16) | stat; + ASSIGN_SAS_TASK(sc, NULL); list_del_init(&task->list); sas_free_task(task); +} + +static void sas_scsi_task_done(struct sas_task *task) +{ + struct scsi_cmnd *sc = task->uldd_task; + + if (unlikely(task->task_state_flags & SAS_TASK_STATE_ABORTED)) { + /* Aborted tasks will be completed by the error handler */ + SAS_DPRINTK("task done but aborted\n"); + return; + } + + if (unlikely(!sc)) { + SAS_DPRINTK("task_done called with non existing SCSI cmnd!\n"); + list_del_init(&task->list); + sas_free_task(task); + return; + } + + ASSIGN_SAS_TASK(sc, NULL); + sas_end_task(sc, task); sc->scsi_done(sc); } @@ -236,18 +243,16 @@ static void sas_eh_finish_cmd(struct scsi_cmnd *cmd) struct sas_task *task = TO_SAS_TASK(cmd); struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(cmd->device->host); - /* remove the aborted task flag to allow the task to be - * completed now. At this point, we only get called following - * an actual abort of the task, so we should be guaranteed not - * to be racing with any completions from the LLD (hence we - * don't need the task state lock to clear the flag) */ - task->task_state_flags &= ~SAS_TASK_STATE_ABORTED; - /* Now call task_done. However, task will be free'd after - * this */ - task->task_done(task); + /* At this point, we only get called following an actual abort + * of the task, so we should be guaranteed not to be racing with + * any completions from the LLD. Task is freed after this. + */ + sas_end_task(cmd, task); + /* now finish the command and move it on to the error * handler done list, this also takes it off the - * error handler pending list */ + * error handler pending list. + */ scsi_eh_finish_cmd(cmd, &sas_ha->eh_done_q); } diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h index 10eb2ea74431..071041b290d6 100644 --- a/include/scsi/libsas.h +++ b/include/scsi/libsas.h @@ -452,7 +452,10 @@ enum service_response { }; enum exec_status { - /* The SAM_STAT_.. codes fit in the lower 6 bits */ + /* The SAM_STAT_.. codes fit in the lower 6 bits, alias some of + * them here to silence 'case value not in enumerated type' warnings + */ + __SAM_STAT_CHECK_CONDITION = SAM_STAT_CHECK_CONDITION, SAS_DEV_NO_RESPONSE = 0x80, SAS_DATA_UNDERRUN, -- cgit v1.2.3 From 9095a64a9aead653df320e3a6fc70835c15d46e4 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Mon, 28 Nov 2011 11:29:20 -0800 Subject: [SCSI] libsas: fix timeout vs completion race Until we have told the lldd to forget a task a timed out operation can return from the hardware at any time. Since completion frees the task we need to make sure that no tasks run their normal completion handler once eh has decided to manage the task. Similar to ata_scsi_cmd_error_handler() freeze completions to let eh judge the outcome of the race. Task collector mode is problematic because it presents a situation where a task can be timed out and aborted before the lldd has even seen it. For this case we need to guarantee that a task that an lldd has been told to forget does not get queued after the lldd says "never seen it". With sas_scsi_timed_out we achieve this with the ->task_queue_flush mutex, rather than adding more time. Signed-off-by: Dan Williams Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_ata.c | 35 +++++------- drivers/scsi/libsas/sas_internal.h | 1 + drivers/scsi/libsas/sas_scsi_host.c | 104 ++++++++++++++++++------------------ include/scsi/libsas.h | 3 ++ include/scsi/sas_ata.h | 8 --- 5 files changed, 68 insertions(+), 83 deletions(-) (limited to 'include/scsi') diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index 903bb441b9f9..4c2a1402373c 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -93,21 +93,30 @@ static enum ata_completion_errors sas_to_ata_err(struct task_status_struct *ts) static void sas_ata_task_done(struct sas_task *task) { struct ata_queued_cmd *qc = task->uldd_task; - struct domain_device *dev; + struct domain_device *dev = task->dev; struct task_status_struct *stat = &task->task_status; struct ata_task_resp *resp = (struct ata_task_resp *)stat->buf; - struct sas_ha_struct *sas_ha; + struct sas_ha_struct *sas_ha = dev->port->ha; enum ata_completion_errors ac; unsigned long flags; struct ata_link *link; struct ata_port *ap; + spin_lock_irqsave(&dev->done_lock, flags); + if (test_bit(SAS_HA_FROZEN, &sas_ha->state)) + task = NULL; + else if (qc && qc->scsicmd) + ASSIGN_SAS_TASK(qc->scsicmd, NULL); + spin_unlock_irqrestore(&dev->done_lock, flags); + + /* check if libsas-eh got to the task before us */ + if (unlikely(!task)) + return; + if (!qc) goto qc_already_gone; ap = qc->ap; - dev = ap->private_data; - sas_ha = dev->port->ha; link = &ap->link; spin_lock_irqsave(ap->lock, flags); @@ -156,8 +165,6 @@ static void sas_ata_task_done(struct sas_task *task) } qc->lldd_task = NULL; - if (qc->scsicmd) - ASSIGN_SAS_TASK(qc->scsicmd, NULL); ata_qc_complete(qc); spin_unlock_irqrestore(ap->lock, flags); @@ -633,22 +640,6 @@ void sas_ata_strategy_handler(struct Scsi_Host *shost) sas_enable_revalidation(sas_ha); } -int sas_ata_timed_out(struct scsi_cmnd *cmd, struct sas_task *task, - enum blk_eh_timer_return *rtn) -{ - struct domain_device *ddev = cmd_to_domain_dev(cmd); - - if (!dev_is_sata(ddev) || task) - return 0; - - /* we're a sata device with no task, so this must be a libata - * eh timeout. Ideally should hook into libata timeout - * handling, but there's no point, it just wants to activate - * the eh thread */ - *rtn = BLK_EH_NOT_HANDLED; - return 1; -} - int sas_ata_eh(struct Scsi_Host *shost, struct list_head *work_q, struct list_head *done_q) { diff --git a/drivers/scsi/libsas/sas_internal.h b/drivers/scsi/libsas/sas_internal.h index ebe9b81ddef5..662ffcba99d2 100644 --- a/drivers/scsi/libsas/sas_internal.h +++ b/drivers/scsi/libsas/sas_internal.h @@ -142,6 +142,7 @@ static inline struct domain_device *sas_alloc_device(void) INIT_LIST_HEAD(&dev->dev_list_node); INIT_LIST_HEAD(&dev->disco_list_node); kref_init(&dev->kref); + spin_lock_init(&dev->done_lock); } return dev; } diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c index 50db8f971a06..0e3fdba7b510 100644 --- a/drivers/scsi/libsas/sas_scsi_host.c +++ b/drivers/scsi/libsas/sas_scsi_host.c @@ -119,9 +119,19 @@ static void sas_end_task(struct scsi_cmnd *sc, struct sas_task *task) static void sas_scsi_task_done(struct sas_task *task) { struct scsi_cmnd *sc = task->uldd_task; + struct domain_device *dev = task->dev; + struct sas_ha_struct *ha = dev->port->ha; + unsigned long flags; + + spin_lock_irqsave(&dev->done_lock, flags); + if (test_bit(SAS_HA_FROZEN, &ha->state)) + task = NULL; + else + ASSIGN_SAS_TASK(sc, NULL); + spin_unlock_irqrestore(&dev->done_lock, flags); - if (unlikely(task->task_state_flags & SAS_TASK_STATE_ABORTED)) { - /* Aborted tasks will be completed by the error handler */ + if (unlikely(!task)) { + /* task will be completed by the error handler */ SAS_DPRINTK("task done but aborted\n"); return; } @@ -133,7 +143,6 @@ static void sas_scsi_task_done(struct sas_task *task) return; } - ASSIGN_SAS_TASK(sc, NULL); sas_end_task(sc, task); sc->scsi_done(sc); } @@ -298,6 +307,7 @@ enum task_disposition { TASK_IS_DONE, TASK_IS_ABORTED, TASK_IS_AT_LU, + TASK_IS_NOT_AT_HA, TASK_IS_NOT_AT_LU, TASK_ABORT_FAILED, }; @@ -314,19 +324,18 @@ static enum task_disposition sas_scsi_find_task(struct sas_task *task) struct scsi_core *core = &ha->core; struct sas_task *t, *n; + mutex_lock(&core->task_queue_flush); spin_lock_irqsave(&core->task_queue_lock, flags); - list_for_each_entry_safe(t, n, &core->task_queue, list) { + list_for_each_entry_safe(t, n, &core->task_queue, list) if (task == t) { list_del_init(&t->list); - spin_unlock_irqrestore(&core->task_queue_lock, - flags); - SAS_DPRINTK("%s: task 0x%p aborted from " - "task_queue\n", - __func__, task); - return TASK_IS_ABORTED; + break; } - } spin_unlock_irqrestore(&core->task_queue_lock, flags); + mutex_unlock(&core->task_queue_flush); + + if (task == t) + return TASK_IS_NOT_AT_HA; } for (i = 0; i < 5; i++) { @@ -499,8 +508,7 @@ try_bus_reset: } static int sas_eh_handle_sas_errors(struct Scsi_Host *shost, - struct list_head *work_q, - struct list_head *done_q) + struct list_head *work_q) { struct scsi_cmnd *cmd, *n; enum task_disposition res = TASK_IS_DONE; @@ -511,7 +519,16 @@ static int sas_eh_handle_sas_errors(struct Scsi_Host *shost, Again: list_for_each_entry_safe(cmd, n, work_q, eh_entry) { - struct sas_task *task = TO_SAS_TASK(cmd); + struct domain_device *dev = cmd_to_domain_dev(cmd); + struct sas_task *task; + + spin_lock_irqsave(&dev->done_lock, flags); + /* by this point the lldd has either observed + * SAS_HA_FROZEN and is leaving the task alone, or has + * won the race with eh and decided to complete it + */ + task = TO_SAS_TASK(cmd); + spin_unlock_irqrestore(&dev->done_lock, flags); if (!task) continue; @@ -534,6 +551,14 @@ Again: cmd->eh_eflags = 0; switch (res) { + case TASK_IS_NOT_AT_HA: + SAS_DPRINTK("%s: task 0x%p is not at ha: %s\n", + __func__, task, + cmd->retries ? "retry" : "aborted"); + if (cmd->retries) + cmd->retries--; + sas_eh_finish_cmd(cmd); + continue; case TASK_IS_DONE: SAS_DPRINTK("%s: task 0x%p is done\n", __func__, task); @@ -635,7 +660,8 @@ void sas_scsi_recover_host(struct Scsi_Host *shost) * Deal with commands that still have SAS tasks (i.e. they didn't * complete via the normal sas_task completion mechanism) */ - if (sas_eh_handle_sas_errors(shost, &eh_work_q, &ha->eh_done_q)) + set_bit(SAS_HA_FROZEN, &ha->state); + if (sas_eh_handle_sas_errors(shost, &eh_work_q)) goto out; /* @@ -649,6 +675,10 @@ void sas_scsi_recover_host(struct Scsi_Host *shost) scsi_eh_ready_devs(shost, &eh_work_q, &ha->eh_done_q); out: + clear_bit(SAS_HA_FROZEN, &ha->state); + if (ha->lldd_max_execute_num > 1) + wake_up_process(ha->core.queue_thread); + /* now link into libata eh --- if we have any ata devices */ sas_ata_strategy_handler(shost); @@ -660,43 +690,7 @@ out: enum blk_eh_timer_return sas_scsi_timed_out(struct scsi_cmnd *cmd) { - struct sas_task *task = TO_SAS_TASK(cmd); - unsigned long flags; - enum blk_eh_timer_return rtn; - - if (sas_ata_timed_out(cmd, task, &rtn)) - return rtn; - - if (!task) { - cmd->request->timeout /= 2; - SAS_DPRINTK("command 0x%p, task 0x%p, gone: %s\n", - cmd, task, (cmd->request->timeout ? - "BLK_EH_RESET_TIMER" : "BLK_EH_NOT_HANDLED")); - if (!cmd->request->timeout) - return BLK_EH_NOT_HANDLED; - return BLK_EH_RESET_TIMER; - } - - spin_lock_irqsave(&task->task_state_lock, flags); - BUG_ON(task->task_state_flags & SAS_TASK_STATE_ABORTED); - if (task->task_state_flags & SAS_TASK_STATE_DONE) { - spin_unlock_irqrestore(&task->task_state_lock, flags); - SAS_DPRINTK("command 0x%p, task 0x%p, timed out: " - "BLK_EH_HANDLED\n", cmd, task); - return BLK_EH_HANDLED; - } - if (!(task->task_state_flags & SAS_TASK_AT_INITIATOR)) { - spin_unlock_irqrestore(&task->task_state_lock, flags); - SAS_DPRINTK("command 0x%p, task 0x%p, not at initiator: " - "BLK_EH_RESET_TIMER\n", - cmd, task); - return BLK_EH_RESET_TIMER; - } - task->task_state_flags |= SAS_TASK_STATE_ABORTED; - spin_unlock_irqrestore(&task->task_state_lock, flags); - - SAS_DPRINTK("command 0x%p, task 0x%p, timed out: BLK_EH_NOT_HANDLED\n", - cmd, task); + scmd_printk(KERN_DEBUG, cmd, "command %p timed out\n", cmd); return BLK_EH_NOT_HANDLED; } @@ -861,9 +855,11 @@ static void sas_queue(struct sas_ha_struct *sas_ha) int res; struct sas_internal *i = to_sas_internal(core->shost->transportt); + mutex_lock(&core->task_queue_flush); spin_lock_irqsave(&core->task_queue_lock, flags); while (!kthread_should_stop() && - !list_empty(&core->task_queue)) { + !list_empty(&core->task_queue) && + !test_bit(SAS_HA_FROZEN, &sas_ha->state)) { can_queue = sas_ha->lldd_queue_size - core->task_queue_size; if (can_queue >= 0) { @@ -899,6 +895,7 @@ static void sas_queue(struct sas_ha_struct *sas_ha) } } spin_unlock_irqrestore(&core->task_queue_lock, flags); + mutex_unlock(&core->task_queue_flush); } /** @@ -925,6 +922,7 @@ int sas_init_queue(struct sas_ha_struct *sas_ha) struct scsi_core *core = &sas_ha->core; spin_lock_init(&core->task_queue_lock); + mutex_init(&core->task_queue_flush); core->task_queue_size = 0; INIT_LIST_HEAD(&core->task_queue); diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h index 071041b290d6..aa7192ff4355 100644 --- a/include/scsi/libsas.h +++ b/include/scsi/libsas.h @@ -178,6 +178,7 @@ enum { }; struct domain_device { + spinlock_t done_lock; enum sas_dev_type dev_type; enum sas_linkrate linkrate; @@ -321,6 +322,7 @@ struct asd_sas_phy { struct scsi_core { struct Scsi_Host *shost; + struct mutex task_queue_flush; spinlock_t task_queue_lock; struct list_head task_queue; int task_queue_size; @@ -337,6 +339,7 @@ enum sas_ha_state { SAS_HA_REGISTERED, SAS_HA_DRAINING, SAS_HA_ATA_EH_ACTIVE, + SAS_HA_FROZEN, }; struct sas_ha_struct { diff --git a/include/scsi/sas_ata.h b/include/scsi/sas_ata.h index 557fc9a8559b..9f7a23d1146d 100644 --- a/include/scsi/sas_ata.h +++ b/include/scsi/sas_ata.h @@ -41,8 +41,6 @@ int sas_ata_init_host_and_port(struct domain_device *found_dev, void sas_ata_task_abort(struct sas_task *task); void sas_ata_strategy_handler(struct Scsi_Host *shost); -int sas_ata_timed_out(struct scsi_cmnd *cmd, struct sas_task *task, - enum blk_eh_timer_return *rtn); int sas_ata_eh(struct Scsi_Host *shost, struct list_head *work_q, struct list_head *done_q); void sas_probe_sata(struct work_struct *work); @@ -67,12 +65,6 @@ static inline void sas_ata_strategy_handler(struct Scsi_Host *shost) { } -static inline int sas_ata_timed_out(struct scsi_cmnd *cmd, - struct sas_task *task, - enum blk_eh_timer_return *rtn) -{ - return 0; -} static inline int sas_ata_eh(struct Scsi_Host *shost, struct list_head *work_q, struct list_head *done_q) { -- cgit v1.2.3 From 3944f50995f947558c35fb16ae0288354756762c Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 29 Nov 2011 12:08:50 -0800 Subject: [SCSI] libsas: let libata handle command timeouts libsas-eh if it successfully aborts an ata command will hide the timeout condition (AC_ERR_TIMEOUT) from libata. The command likely completes with the all-zero task->task_status it started with. Instead, interpret a TMF_RESP_FUNC_COMPLETE as the end of the sas_task but keep the scmd around for libata-eh to handle. Tested-by: Andrzej Jakowski Signed-off-by: Dan Williams Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_init.c | 1 + drivers/scsi/libsas/sas_scsi_host.c | 22 ++++++++++++++++++++-- include/scsi/libsas.h | 3 ++- 3 files changed, 23 insertions(+), 3 deletions(-) (limited to 'include/scsi') diff --git a/drivers/scsi/libsas/sas_init.c b/drivers/scsi/libsas/sas_init.c index 52cd11d76664..e17fe35af30c 100644 --- a/drivers/scsi/libsas/sas_init.c +++ b/drivers/scsi/libsas/sas_init.c @@ -146,6 +146,7 @@ int sas_register_ha(struct sas_ha_struct *sas_ha) } INIT_LIST_HEAD(&sas_ha->eh_done_q); + INIT_LIST_HEAD(&sas_ha->eh_ata_q); return 0; diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c index 0e3fdba7b510..e02ca3d570f5 100644 --- a/drivers/scsi/libsas/sas_scsi_host.c +++ b/drivers/scsi/libsas/sas_scsi_host.c @@ -265,6 +265,22 @@ static void sas_eh_finish_cmd(struct scsi_cmnd *cmd) scsi_eh_finish_cmd(cmd, &sas_ha->eh_done_q); } +static void sas_eh_defer_cmd(struct scsi_cmnd *cmd) +{ + struct sas_task *task = TO_SAS_TASK(cmd); + struct domain_device *dev = task->dev; + struct sas_ha_struct *ha = dev->port->ha; + + if (!dev_is_sata(dev)) { + sas_eh_finish_cmd(cmd); + return; + } + + /* report the timeout to libata */ + sas_end_task(cmd, task); + list_move_tail(&cmd->eh_entry, &ha->eh_ata_q); +} + static void sas_scsi_clear_queue_lu(struct list_head *error_q, struct scsi_cmnd *my_cmd) { struct scsi_cmnd *cmd, *n; @@ -562,12 +578,12 @@ Again: case TASK_IS_DONE: SAS_DPRINTK("%s: task 0x%p is done\n", __func__, task); - sas_eh_finish_cmd(cmd); + sas_eh_defer_cmd(cmd); continue; case TASK_IS_ABORTED: SAS_DPRINTK("%s: task 0x%p is aborted\n", __func__, task); - sas_eh_finish_cmd(cmd); + sas_eh_defer_cmd(cmd); continue; case TASK_IS_AT_LU: SAS_DPRINTK("task 0x%p is at LU: lu recover\n", task); @@ -635,12 +651,14 @@ Again: goto clear_q; } } + list_splice_tail_init(&ha->eh_ata_q, work_q); return list_empty(work_q); clear_q: SAS_DPRINTK("--- Exit %s -- clear_q\n", __func__); list_for_each_entry_safe(cmd, n, work_q, eh_entry) sas_eh_finish_cmd(cmd); + list_splice_tail_init(&ha->eh_ata_q, work_q); return list_empty(work_q); } diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h index aa7192ff4355..6b80310e08af 100644 --- a/include/scsi/libsas.h +++ b/include/scsi/libsas.h @@ -382,7 +382,8 @@ struct sas_ha_struct { void *lldd_ha; /* not touched by sas class code */ - struct list_head eh_done_q; + struct list_head eh_done_q; /* complete via scsi_eh_flush_done_q */ + struct list_head eh_ata_q; /* scmds to promote from sas to ata eh */ }; #define SHOST_TO_SAS_HA(_shost) (*(struct sas_ha_struct **)(_shost)->hostdata) -- cgit v1.2.3 From b52df4174dff7e587f6fbfb21e3c2cb57109e5cf Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 30 Nov 2011 23:23:33 -0800 Subject: [SCSI] libsas: use libata-eh-reset for sata rediscovery fis transmit failures Since sata devices can take several seconds to recover the link on reset the 0.5 seconds that libsas currently waits may not be enough. Instead if we are rediscovering a phy that was previously attached to a sata device let libata handle any resets to encourage the device to transmit the initial fis. Once sas_ata_hard_reset() and lldds learn how to honor 'deadline' libsas should stop encountering phys in an intermediate state, until then this will loop until the fis is transmitted or ->attached_sas_addr gets cleared, but in the more likely initial discovery case we keep existing behavior. Signed-off-by: Dan Williams Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_ata.c | 19 ++++++++++++++++ drivers/scsi/libsas/sas_expander.c | 44 +++++++++++++++++++++++++++++++++----- include/scsi/sas_ata.h | 6 +++++- 3 files changed, 63 insertions(+), 6 deletions(-) (limited to 'include/scsi') diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index a8ace8d24e66..48cadf88c399 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -679,3 +679,22 @@ int sas_ata_eh(struct Scsi_Host *shost, struct list_head *work_q, return rtn; } + +void sas_ata_schedule_reset(struct domain_device *dev) +{ + struct ata_eh_info *ehi; + struct ata_port *ap; + unsigned long flags; + + if (!dev_is_sata(dev)) + return; + + ap = dev->sata_dev.ap; + ehi = &ap->link.eh_info; + + spin_lock_irqsave(ap->lock, flags); + ehi->err_mask |= AC_ERR_TIMEOUT; + ehi->action |= ATA_EH_RESET; + ata_port_schedule_eh(ap); + spin_unlock_irqrestore(ap->lock, flags); +} diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index e45b259dac4c..f4894b0f537b 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c @@ -28,6 +28,7 @@ #include "sas_internal.h" +#include #include #include #include "../scsi_sas_internal.h" @@ -226,12 +227,35 @@ static void sas_set_ex_phy(struct domain_device *dev, int phy_id, return; } +/* check if we have an existing attached ata device on this expander phy */ +static struct domain_device *sas_ex_to_ata(struct domain_device *ex_dev, int phy_id) +{ + struct ex_phy *ex_phy = &ex_dev->ex_dev.ex_phy[phy_id]; + struct domain_device *dev; + struct sas_rphy *rphy; + + if (!ex_phy->port) + return NULL; + + rphy = ex_phy->port->rphy; + if (!rphy) + return NULL; + + dev = sas_find_dev_by_rphy(rphy); + + if (dev && dev_is_sata(dev)) + return dev; + + return NULL; +} + #define DISCOVER_REQ_SIZE 16 #define DISCOVER_RESP_SIZE 56 static int sas_ex_phy_discover_helper(struct domain_device *dev, u8 *disc_req, u8 *disc_resp, int single) { + struct domain_device *ata_dev = sas_ex_to_ata(dev, single); int i, res; disc_req[9] = single; @@ -242,20 +266,30 @@ static int sas_ex_phy_discover_helper(struct domain_device *dev, u8 *disc_req, disc_resp, DISCOVER_RESP_SIZE); if (res) return res; - /* This is detecting a failure to transmit initial - * dev to host FIS as described in section G.5 of - * sas-2 r 04b */ dr = &((struct smp_resp *)disc_resp)->disc; if (memcmp(dev->sas_addr, dr->attached_sas_addr, SAS_ADDR_SIZE) == 0) { sas_printk("Found loopback topology, just ignore it!\n"); return 0; } + + /* This is detecting a failure to transmit initial + * dev to host FIS as described in section J.5 of + * sas-2 r16 + */ if (!(dr->attached_dev_type == 0 && dr->attached_sata_dev)) break; - /* In order to generate the dev to host FIS, we - * send a link reset to the expander port */ + + /* In order to generate the dev to host FIS, we send a + * link reset to the expander port. If a device was + * previously detected on this port we ask libata to + * manage the reset and link recovery. + */ + if (ata_dev) { + sas_ata_schedule_reset(ata_dev); + break; + } sas_smp_phy_control(dev, single, PHY_FUNC_LINK_RESET, NULL); /* Wait for the reset to trigger the negotiation */ msleep(500); diff --git a/include/scsi/sas_ata.h b/include/scsi/sas_ata.h index 9f7a23d1146d..c0bcd30eec56 100644 --- a/include/scsi/sas_ata.h +++ b/include/scsi/sas_ata.h @@ -44,7 +44,7 @@ void sas_ata_strategy_handler(struct Scsi_Host *shost); int sas_ata_eh(struct Scsi_Host *shost, struct list_head *work_q, struct list_head *done_q); void sas_probe_sata(struct work_struct *work); - +void sas_ata_schedule_reset(struct domain_device *dev); #else @@ -75,6 +75,10 @@ static inline void sas_probe_sata(struct work_struct *work) { } +static inline void sas_ata_schedule_reset(struct domain_device *dev) +{ +} + #endif #endif /* _SAS_ATA_H_ */ -- cgit v1.2.3 From 0b3e09da1350397f3f8b6fd839ab455b0b587451 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 20 Dec 2011 01:03:48 -0800 Subject: [SCSI] libsas: perform sas-transport resets in shost->workq context Extend the sas transport class to allow transport users to attach extra data to a sas_phy (->hostdata). Use this area in libsas to move resets to workq context in preparation for scheduling ata device resets through libata-eh. Signed-off-by: Dan Williams Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_event.c | 2 +- drivers/scsi/libsas/sas_init.c | 59 +++++++++++++++++++++++++++++++++++++- drivers/scsi/libsas/sas_internal.h | 10 +++++++ drivers/scsi/scsi_transport_sas.c | 18 +++++++++++- include/scsi/scsi_transport_sas.h | 5 +++- 5 files changed, 90 insertions(+), 4 deletions(-) (limited to 'include/scsi') diff --git a/drivers/scsi/libsas/sas_event.c b/drivers/scsi/libsas/sas_event.c index 933d757499b5..dbfaceeea0f7 100644 --- a/drivers/scsi/libsas/sas_event.c +++ b/drivers/scsi/libsas/sas_event.c @@ -27,7 +27,7 @@ #include "sas_internal.h" #include "sas_dump.h" -static void sas_queue_work(struct sas_ha_struct *ha, struct work_struct *work) +void sas_queue_work(struct sas_ha_struct *ha, struct work_struct *work) { if (!test_bit(SAS_HA_REGISTERED, &ha->state)) return; diff --git a/drivers/scsi/libsas/sas_init.c b/drivers/scsi/libsas/sas_init.c index e17fe35af30c..cb65adf4ab16 100644 --- a/drivers/scsi/libsas/sas_init.c +++ b/drivers/scsi/libsas/sas_init.c @@ -290,9 +290,66 @@ int sas_set_phy_speed(struct sas_phy *phy, return ret; } +static void sas_phy_release(struct sas_phy *phy) +{ + kfree(phy->hostdata); + phy->hostdata = NULL; +} + +static void phy_reset_work(struct work_struct *work) +{ + struct sas_phy_data *d = container_of(work, typeof(*d), reset_work); + + d->reset_result = sas_phy_reset(d->phy, d->hard_reset); +} + +static int sas_phy_setup(struct sas_phy *phy) +{ + struct sas_phy_data *d = kzalloc(sizeof(*d), GFP_KERNEL); + + if (!d) + return -ENOMEM; + + mutex_init(&d->event_lock); + INIT_WORK(&d->reset_work, phy_reset_work); + d->phy = phy; + phy->hostdata = d; + + return 0; +} + +static int queue_phy_reset(struct sas_phy *phy, int hard_reset) +{ + struct Scsi_Host *shost = dev_to_shost(phy->dev.parent); + struct sas_ha_struct *ha = SHOST_TO_SAS_HA(shost); + struct sas_phy_data *d = phy->hostdata; + int rc; + + if (!d) + return -ENOMEM; + + /* libsas workqueue coordinates ata-eh reset with discovery */ + mutex_lock(&d->event_lock); + d->reset_result = 0; + d->hard_reset = hard_reset; + + spin_lock_irq(&ha->state_lock); + sas_queue_work(ha, &d->reset_work); + spin_unlock_irq(&ha->state_lock); + + rc = sas_drain_work(ha); + if (rc == 0) + rc = d->reset_result; + mutex_unlock(&d->event_lock); + + return rc; +} + static struct sas_function_template sft = { .phy_enable = sas_phy_enable, - .phy_reset = sas_phy_reset, + .phy_reset = queue_phy_reset, + .phy_setup = sas_phy_setup, + .phy_release = sas_phy_release, .set_phy_speed = sas_set_phy_speed, .get_linkerrors = sas_get_linkerrors, .smp_handler = sas_smp_handler, diff --git a/drivers/scsi/libsas/sas_internal.h b/drivers/scsi/libsas/sas_internal.h index 662ffcba99d2..9ba65e0c6f91 100644 --- a/drivers/scsi/libsas/sas_internal.h +++ b/drivers/scsi/libsas/sas_internal.h @@ -38,6 +38,15 @@ #define TO_SAS_TASK(_scsi_cmd) ((void *)(_scsi_cmd)->host_scribble) #define ASSIGN_SAS_TASK(_sc, _t) do { (_sc)->host_scribble = (void *) _t; } while (0) +struct sas_phy_data { + /* let reset be performed in sas_queue_work() context */ + struct sas_phy *phy; + struct mutex event_lock; + int hard_reset; + int reset_result; + struct work_struct reset_work; +}; + void sas_scsi_recover_host(struct Scsi_Host *shost); int sas_show_class(enum sas_class class, char *buf); @@ -66,6 +75,7 @@ void sas_porte_broadcast_rcvd(struct work_struct *work); void sas_porte_link_reset_err(struct work_struct *work); void sas_porte_timer_event(struct work_struct *work); void sas_porte_hard_reset(struct work_struct *work); +void sas_queue_work(struct sas_ha_struct *ha, struct work_struct *work); int sas_notify_lldd_dev_found(struct domain_device *); void sas_notify_lldd_dev_gone(struct domain_device *); diff --git a/drivers/scsi/scsi_transport_sas.c b/drivers/scsi/scsi_transport_sas.c index 9421bae8af1a..ab3bd0b5ffd9 100644 --- a/drivers/scsi/scsi_transport_sas.c +++ b/drivers/scsi/scsi_transport_sas.c @@ -652,9 +652,21 @@ sas_phy_linkerror_attr(running_disparity_error_count); sas_phy_linkerror_attr(loss_of_dword_sync_count); sas_phy_linkerror_attr(phy_reset_problem_count); +static int sas_phy_setup(struct transport_container *tc, struct device *dev, + struct device *cdev) +{ + struct sas_phy *phy = dev_to_phy(dev); + struct Scsi_Host *shost = dev_to_shost(phy->dev.parent); + struct sas_internal *i = to_sas_internal(shost->transportt); + + if (i->f->phy_setup) + i->f->phy_setup(phy); + + return 0; +} static DECLARE_TRANSPORT_CLASS(sas_phy_class, - "sas_phy", NULL, NULL, NULL); + "sas_phy", sas_phy_setup, NULL, NULL); static int sas_phy_match(struct attribute_container *cont, struct device *dev) { @@ -678,7 +690,11 @@ static int sas_phy_match(struct attribute_container *cont, struct device *dev) static void sas_phy_release(struct device *dev) { struct sas_phy *phy = dev_to_phy(dev); + struct Scsi_Host *shost = dev_to_shost(phy->dev.parent); + struct sas_internal *i = to_sas_internal(shost->transportt); + if (i->f->phy_release) + i->f->phy_release(phy); put_device(dev->parent); kfree(phy); } diff --git a/include/scsi/scsi_transport_sas.h b/include/scsi/scsi_transport_sas.h index 6d14daac7589..42817facaeda 100644 --- a/include/scsi/scsi_transport_sas.h +++ b/include/scsi/scsi_transport_sas.h @@ -75,7 +75,8 @@ struct sas_phy { /* for the list of phys belonging to a port */ struct list_head port_siblings; - struct work_struct reset_work; + /* available to the lldd */ + void *hostdata; }; #define dev_to_phy(d) \ @@ -169,6 +170,8 @@ struct sas_function_template { int (*get_bay_identifier)(struct sas_rphy *); int (*phy_reset)(struct sas_phy *, int); int (*phy_enable)(struct sas_phy *, int); + int (*phy_setup)(struct sas_phy *); + void (*phy_release)(struct sas_phy *); int (*set_phy_speed)(struct sas_phy *, struct sas_phy_linkrates *); int (*smp_handler)(struct Scsi_Host *, struct sas_rphy *, struct request *); }; -- cgit v1.2.3 From 81c757bc696284f39f07766f0c2ca67af64ce9bd Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Fri, 2 Dec 2011 16:07:01 -0800 Subject: [SCSI] libsas: execute transport link resets with libata-eh via host workqueue Link resets leave ata affiliations intact, so arrange for libsas to make an effort to avoid dropping the device due to a slow-to-recover link. Towards this end carry out reset in the host workqueue so that it can check for ata devices and kick the reset request to libata. Hard resets, in contrast, bypass libata since they are meant for associating an ata device with another initiator in the domain (tears down affiliations). Need to add a new transport_sas_phy_reset() since the current sas_phy_reset() is a utility function to libsas lldds. They are not prepared for it to loop back into eh. Signed-off-by: Dan Williams Signed-off-by: James Bottomley --- drivers/ata/libata-eh.c | 1 + drivers/ata/libata.h | 1 - drivers/scsi/libsas/sas_ata.c | 11 ++++++++ drivers/scsi/libsas/sas_expander.c | 2 +- drivers/scsi/libsas/sas_init.c | 56 +++++++++++++++++++++++++++++++++++++- drivers/scsi/libsas/sas_internal.h | 1 + include/linux/libata.h | 1 + include/scsi/sas_ata.h | 4 +++ 8 files changed, 74 insertions(+), 3 deletions(-) (limited to 'include/scsi') diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index a9b282038000..c61316e9d2f7 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -863,6 +863,7 @@ void ata_port_wait_eh(struct ata_port *ap) goto retry; } } +EXPORT_SYMBOL_GPL(ata_port_wait_eh); static int ata_eh_nr_in_flight(struct ata_port *ap) { diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h index 814486d35c44..1fab235ee516 100644 --- a/drivers/ata/libata.h +++ b/drivers/ata/libata.h @@ -151,7 +151,6 @@ extern void ata_eh_acquire(struct ata_port *ap); extern void ata_eh_release(struct ata_port *ap); extern enum blk_eh_timer_return ata_scsi_timed_out(struct scsi_cmnd *cmd); extern void ata_scsi_error(struct Scsi_Host *host); -extern void ata_port_wait_eh(struct ata_port *ap); extern void ata_eh_fastdrain_timerfn(unsigned long arg); extern void ata_qc_schedule_eh(struct ata_queued_cmd *qc); extern void ata_dev_disable(struct ata_device *dev); diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index 48cadf88c399..03930a04a679 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -698,3 +698,14 @@ void sas_ata_schedule_reset(struct domain_device *dev) ata_port_schedule_eh(ap); spin_unlock_irqrestore(ap->lock, flags); } + +void sas_ata_wait_eh(struct domain_device *dev) +{ + struct ata_port *ap; + + if (!dev_is_sata(dev)) + return; + + ap = dev->sata_dev.ap; + ata_port_wait_eh(ap); +} diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index f4894b0f537b..d3c1a29b8a2a 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c @@ -228,7 +228,7 @@ static void sas_set_ex_phy(struct domain_device *dev, int phy_id, } /* check if we have an existing attached ata device on this expander phy */ -static struct domain_device *sas_ex_to_ata(struct domain_device *ex_dev, int phy_id) +struct domain_device *sas_ex_to_ata(struct domain_device *ex_dev, int phy_id) { struct ex_phy *ex_phy = &ex_dev->ex_dev.ex_phy[phy_id]; struct domain_device *dev; diff --git a/drivers/scsi/libsas/sas_init.c b/drivers/scsi/libsas/sas_init.c index cb65adf4ab16..a15fb861daba 100644 --- a/drivers/scsi/libsas/sas_init.c +++ b/drivers/scsi/libsas/sas_init.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -195,6 +196,59 @@ static int sas_get_linkerrors(struct sas_phy *phy) return sas_smp_get_phy_events(phy); } +/** + * transport_sas_phy_reset - reset a phy and permit libata to manage the link + * + * phy reset request via sysfs in host workqueue context so we know we + * can block on eh and safely traverse the domain_device topology + */ +static int transport_sas_phy_reset(struct sas_phy *phy, int hard_reset) +{ + int ret; + enum phy_func reset_type; + + if (hard_reset) + reset_type = PHY_FUNC_HARD_RESET; + else + reset_type = PHY_FUNC_LINK_RESET; + + if (scsi_is_sas_phy_local(phy)) { + struct Scsi_Host *shost = dev_to_shost(phy->dev.parent); + struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost); + struct asd_sas_phy *asd_phy = sas_ha->sas_phy[phy->number]; + struct sas_internal *i = + to_sas_internal(sas_ha->core.shost->transportt); + struct domain_device *dev = NULL; + + if (asd_phy->port) + dev = asd_phy->port->port_dev; + + /* validate that dev has been probed */ + if (dev) + dev = sas_find_dev_by_rphy(dev->rphy); + + if (dev && dev_is_sata(dev) && !hard_reset) { + sas_ata_schedule_reset(dev); + sas_ata_wait_eh(dev); + ret = 0; + } else + ret = i->dft->lldd_control_phy(asd_phy, reset_type, NULL); + } else { + struct sas_rphy *rphy = dev_to_rphy(phy->dev.parent); + struct domain_device *ddev = sas_find_dev_by_rphy(rphy); + struct domain_device *ata_dev = sas_ex_to_ata(ddev, phy->number); + + if (ata_dev && !hard_reset) { + sas_ata_schedule_reset(ata_dev); + sas_ata_wait_eh(ata_dev); + ret = 0; + } else + ret = sas_smp_phy_control(ddev, phy->number, reset_type, NULL); + } + + return ret; +} + int sas_phy_enable(struct sas_phy *phy, int enable) { int ret; @@ -300,7 +354,7 @@ static void phy_reset_work(struct work_struct *work) { struct sas_phy_data *d = container_of(work, typeof(*d), reset_work); - d->reset_result = sas_phy_reset(d->phy, d->hard_reset); + d->reset_result = transport_sas_phy_reset(d->phy, d->hard_reset); } static int sas_phy_setup(struct sas_phy *phy) diff --git a/drivers/scsi/libsas/sas_internal.h b/drivers/scsi/libsas/sas_internal.h index 9ba65e0c6f91..ae9698d9d857 100644 --- a/drivers/scsi/libsas/sas_internal.h +++ b/drivers/scsi/libsas/sas_internal.h @@ -85,6 +85,7 @@ int sas_smp_phy_control(struct domain_device *dev, int phy_id, int sas_smp_get_phy_events(struct sas_phy *phy); struct domain_device *sas_find_dev_by_rphy(struct sas_rphy *rphy); +struct domain_device *sas_ex_to_ata(struct domain_device *ex_dev, int phy_id); void sas_hae_reset(struct work_struct *work); diff --git a/include/linux/libata.h b/include/linux/libata.h index cafc09a64fe4..aa4270477563 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -1147,6 +1147,7 @@ static inline int ata_acpi_cbl_80wire(struct ata_port *ap, * EH - drivers/ata/libata-eh.c */ extern void ata_port_schedule_eh(struct ata_port *ap); +extern void ata_port_wait_eh(struct ata_port *ap); extern int ata_link_abort(struct ata_link *link); extern int ata_port_abort(struct ata_port *ap); extern int ata_port_freeze(struct ata_port *ap); diff --git a/include/scsi/sas_ata.h b/include/scsi/sas_ata.h index c0bcd30eec56..da3f37727387 100644 --- a/include/scsi/sas_ata.h +++ b/include/scsi/sas_ata.h @@ -45,6 +45,7 @@ int sas_ata_eh(struct Scsi_Host *shost, struct list_head *work_q, struct list_head *done_q); void sas_probe_sata(struct work_struct *work); void sas_ata_schedule_reset(struct domain_device *dev); +void sas_ata_wait_eh(struct domain_device *dev); #else @@ -79,6 +80,9 @@ static inline void sas_ata_schedule_reset(struct domain_device *dev) { } +static inline void sas_ata_wait_eh(struct domain_device *dev) +{ +} #endif #endif /* _SAS_ATA_H_ */ -- cgit v1.2.3 From 2a559f4ba443265b4c58925b48296f1cf81b49f9 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Sun, 4 Dec 2011 00:06:57 -0800 Subject: [SCSI] libsas: sas_phy_enable via transport_sas_phy_reset Execute the link-reset triggered by sas_phy_enable via transport_sas_phy_reset so that it can be managed by libata. Signed-off-by: Dan Williams Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_init.c | 57 +++++++++++++++++++++++++++++++------ drivers/scsi/libsas/sas_internal.h | 3 ++ drivers/scsi/libsas/sas_scsi_host.c | 1 - include/scsi/libsas.h | 1 - 4 files changed, 52 insertions(+), 10 deletions(-) (limited to 'include/scsi') diff --git a/drivers/scsi/libsas/sas_init.c b/drivers/scsi/libsas/sas_init.c index a15fb861daba..53ae893e8b0b 100644 --- a/drivers/scsi/libsas/sas_init.c +++ b/drivers/scsi/libsas/sas_init.c @@ -249,15 +249,15 @@ static int transport_sas_phy_reset(struct sas_phy *phy, int hard_reset) return ret; } -int sas_phy_enable(struct sas_phy *phy, int enable) +static int sas_phy_enable(struct sas_phy *phy, int enable) { int ret; - enum phy_func command; + enum phy_func cmd; if (enable) - command = PHY_FUNC_LINK_RESET; + cmd = PHY_FUNC_LINK_RESET; else - command = PHY_FUNC_DISABLE; + cmd = PHY_FUNC_DISABLE; if (scsi_is_sas_phy_local(phy)) { struct Scsi_Host *shost = dev_to_shost(phy->dev.parent); @@ -266,15 +266,21 @@ int sas_phy_enable(struct sas_phy *phy, int enable) struct sas_internal *i = to_sas_internal(sas_ha->core.shost->transportt); - if (!enable) { + if (enable) + ret = transport_sas_phy_reset(phy, 0); + else { sas_phy_disconnected(asd_phy); sas_ha->notify_phy_event(asd_phy, PHYE_LOSS_OF_SIGNAL); + ret = i->dft->lldd_control_phy(asd_phy, cmd, NULL); } - ret = i->dft->lldd_control_phy(asd_phy, command, NULL); } else { struct sas_rphy *rphy = dev_to_rphy(phy->dev.parent); struct domain_device *ddev = sas_find_dev_by_rphy(rphy); - ret = sas_smp_phy_control(ddev, phy->number, command, NULL); + + if (enable) + ret = transport_sas_phy_reset(phy, 0); + else + ret = sas_smp_phy_control(ddev, phy->number, cmd, NULL); } return ret; } @@ -357,6 +363,13 @@ static void phy_reset_work(struct work_struct *work) d->reset_result = transport_sas_phy_reset(d->phy, d->hard_reset); } +static void phy_enable_work(struct work_struct *work) +{ + struct sas_phy_data *d = container_of(work, typeof(*d), enable_work); + + d->enable_result = sas_phy_enable(d->phy, d->enable); +} + static int sas_phy_setup(struct sas_phy *phy) { struct sas_phy_data *d = kzalloc(sizeof(*d), GFP_KERNEL); @@ -366,6 +379,7 @@ static int sas_phy_setup(struct sas_phy *phy) mutex_init(&d->event_lock); INIT_WORK(&d->reset_work, phy_reset_work); + INIT_WORK(&d->enable_work, phy_enable_work); d->phy = phy; phy->hostdata = d; @@ -399,8 +413,35 @@ static int queue_phy_reset(struct sas_phy *phy, int hard_reset) return rc; } +static int queue_phy_enable(struct sas_phy *phy, int enable) +{ + struct Scsi_Host *shost = dev_to_shost(phy->dev.parent); + struct sas_ha_struct *ha = SHOST_TO_SAS_HA(shost); + struct sas_phy_data *d = phy->hostdata; + int rc; + + if (!d) + return -ENOMEM; + + /* libsas workqueue coordinates ata-eh reset with discovery */ + mutex_lock(&d->event_lock); + d->enable_result = 0; + d->enable = enable; + + spin_lock_irq(&ha->state_lock); + sas_queue_work(ha, &d->enable_work); + spin_unlock_irq(&ha->state_lock); + + rc = sas_drain_work(ha); + if (rc == 0) + rc = d->enable_result; + mutex_unlock(&d->event_lock); + + return rc; +} + static struct sas_function_template sft = { - .phy_enable = sas_phy_enable, + .phy_enable = queue_phy_enable, .phy_reset = queue_phy_reset, .phy_setup = sas_phy_setup, .phy_release = sas_phy_release, diff --git a/drivers/scsi/libsas/sas_internal.h b/drivers/scsi/libsas/sas_internal.h index ae9698d9d857..9e960b2d535a 100644 --- a/drivers/scsi/libsas/sas_internal.h +++ b/drivers/scsi/libsas/sas_internal.h @@ -45,6 +45,9 @@ struct sas_phy_data { int hard_reset; int reset_result; struct work_struct reset_work; + int enable; + int enable_result; + struct work_struct enable_work; }; void sas_scsi_recover_host(struct Scsi_Host *shost); diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c index af71a6d0edae..5cc44fddfe95 100644 --- a/drivers/scsi/libsas/sas_scsi_host.c +++ b/drivers/scsi/libsas/sas_scsi_host.c @@ -1077,7 +1077,6 @@ EXPORT_SYMBOL_GPL(sas_change_queue_type); EXPORT_SYMBOL_GPL(sas_bios_param); EXPORT_SYMBOL_GPL(sas_task_abort); EXPORT_SYMBOL_GPL(sas_phy_reset); -EXPORT_SYMBOL_GPL(sas_phy_enable); EXPORT_SYMBOL_GPL(sas_eh_device_reset_handler); EXPORT_SYMBOL_GPL(sas_eh_bus_reset_handler); EXPORT_SYMBOL_GPL(sas_slave_alloc); diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h index 6b80310e08af..f388ba536128 100644 --- a/include/scsi/libsas.h +++ b/include/scsi/libsas.h @@ -634,7 +634,6 @@ extern int sas_unregister_ha(struct sas_ha_struct *); int sas_set_phy_speed(struct sas_phy *phy, struct sas_phy_linkrates *rates); -int sas_phy_enable(struct sas_phy *phy, int enabled); int sas_phy_reset(struct sas_phy *phy, int hard_reset); int sas_queue_up(struct sas_task *task); extern int sas_queuecommand(struct Scsi_Host * ,struct scsi_cmnd *); -- cgit v1.2.3 From 89d3cf6ac3cdc4f15a82709f8c78ed169a98be5b Mon Sep 17 00:00:00 2001 From: Jeff Skirvin Date: Wed, 16 Nov 2011 09:44:13 +0000 Subject: [SCSI] libsas: add mutex for SMP task execution SAS does not tag SMP requests, and at least one lldd (isci) does not permit more than one in-flight request at a time. [jejb: fix sas_init_dev tab issues while we're at it] Signed-off-by: Jeff Skirvin Signed-off-by: Dan Williams Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_discover.c | 31 ++++++++++++++++--------------- drivers/scsi/libsas/sas_expander.c | 29 ++++++++++++++++------------- include/scsi/libsas.h | 2 ++ 3 files changed, 34 insertions(+), 28 deletions(-) (limited to 'include/scsi') diff --git a/drivers/scsi/libsas/sas_discover.c b/drivers/scsi/libsas/sas_discover.c index bad5eba4a92b..c56cc6400819 100644 --- a/drivers/scsi/libsas/sas_discover.c +++ b/drivers/scsi/libsas/sas_discover.c @@ -37,21 +37,22 @@ void sas_init_dev(struct domain_device *dev) { - switch (dev->dev_type) { - case SAS_END_DEV: - break; - case EDGE_DEV: - case FANOUT_DEV: - INIT_LIST_HEAD(&dev->ex_dev.children); - break; - case SATA_DEV: - case SATA_PM: - case SATA_PM_PORT: - INIT_LIST_HEAD(&dev->sata_dev.children); - break; - default: - break; - } + switch (dev->dev_type) { + case SAS_END_DEV: + break; + case EDGE_DEV: + case FANOUT_DEV: + INIT_LIST_HEAD(&dev->ex_dev.children); + mutex_init(&dev->ex_dev.cmd_mutex); + break; + case SATA_DEV: + case SATA_PM: + case SATA_PM_PORT: + INIT_LIST_HEAD(&dev->sata_dev.children); + break; + default: + break; + } } /* ---------- Domain device discovery ---------- */ diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index d3c1a29b8a2a..7c59f97c0287 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c @@ -72,11 +72,13 @@ static int smp_execute_task(struct domain_device *dev, void *req, int req_size, struct sas_internal *i = to_sas_internal(dev->port->ha->core.shost->transportt); + mutex_lock(&dev->ex_dev.cmd_mutex); for (retry = 0; retry < 3; retry++) { task = sas_alloc_task(GFP_KERNEL); - if (!task) - return -ENOMEM; - + if (!task) { + res = -ENOMEM; + break; + } task->dev = dev; task->task_proto = dev->tproto; sg_init_one(&task->smp_task.smp_req, req, req_size); @@ -94,7 +96,7 @@ static int smp_execute_task(struct domain_device *dev, void *req, int req_size, if (res) { del_timer(&task->timer); SAS_DPRINTK("executing SMP task failed:%d\n", res); - goto ex_err; + break; } wait_for_completion(&task->completion); @@ -104,21 +106,23 @@ static int smp_execute_task(struct domain_device *dev, void *req, int req_size, i->dft->lldd_abort_task(task); if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) { SAS_DPRINTK("SMP task aborted and not done\n"); - goto ex_err; + break; } } if (task->task_status.resp == SAS_TASK_COMPLETE && task->task_status.stat == SAM_STAT_GOOD) { res = 0; break; - } if (task->task_status.resp == SAS_TASK_COMPLETE && - task->task_status.stat == SAS_DATA_UNDERRUN) { + } + if (task->task_status.resp == SAS_TASK_COMPLETE && + task->task_status.stat == SAS_DATA_UNDERRUN) { /* no error, but return the number of bytes of * underrun */ res = task->task_status.residual; break; - } if (task->task_status.resp == SAS_TASK_COMPLETE && - task->task_status.stat == SAS_DATA_OVERRUN) { + } + if (task->task_status.resp == SAS_TASK_COMPLETE && + task->task_status.stat == SAS_DATA_OVERRUN) { res = -EMSGSIZE; break; } else { @@ -131,11 +135,10 @@ static int smp_execute_task(struct domain_device *dev, void *req, int req_size, task = NULL; } } -ex_err: + mutex_unlock(&dev->ex_dev.cmd_mutex); + BUG_ON(retry == 3 && task != NULL); - if (task != NULL) { - sas_free_task(task); - } + sas_free_task(task); return res; } diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h index f388ba536128..18704a2e4f07 100644 --- a/include/scsi/libsas.h +++ b/include/scsi/libsas.h @@ -153,6 +153,8 @@ struct expander_device { struct ex_phy *ex_phy; struct sas_port *parent_port; + + struct mutex cmd_mutex; }; /* ---------- SATA device ---------- */ -- cgit v1.2.3 From 36a399473902a57218dc493c5a814708a56b73ab Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 17 Nov 2011 17:59:54 -0800 Subject: [SCSI] libsas: poll for ata device readiness after reset Use ata_wait_after_reset() to poll for link recovery after a reset. This combined with sas_ha->eh_mutex prevents expander rediscovery from probing phys in an intermediate state. Local discovery does not have a mechanism to filter link status changes during this timeout, so it remains the responsibility of lldds to prevent premature port teardown. Although once all lldd's support ->lldd_ata_check_ready() that could be used as a gate to local port teardown. The signature fis is re-transmitted when the link comes back so we should be revalidating the ata device class, but that is left to a future patch. Signed-off-by: Dan Williams Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_ata.c | 104 ++++++++++++++++++++++++++----------- drivers/scsi/libsas/sas_expander.c | 10 ++-- drivers/scsi/libsas/sas_internal.h | 3 +- include/scsi/libsas.h | 1 + 4 files changed, 83 insertions(+), 35 deletions(-) (limited to 'include/scsi') diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index 4beca66728b4..5fdb63ad94b7 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -272,39 +272,84 @@ static bool sas_ata_qc_fill_rtf(struct ata_queued_cmd *qc) return true; } -static int sas_ata_hard_reset(struct ata_link *link, unsigned int *class, - unsigned long deadline) +static struct sas_internal *dev_to_sas_internal(struct domain_device *dev) +{ + return to_sas_internal(dev->port->ha->core.shost->transportt); +} + +static int smp_ata_check_ready(struct ata_link *link) { + int res; + u8 addr[8]; struct ata_port *ap = link->ap; struct domain_device *dev = ap->private_data; - struct sas_internal *i = - to_sas_internal(dev->port->ha->core.shost->transportt); - int res = TMF_RESP_FUNC_FAILED; - int ret = 0; + struct domain_device *ex_dev = dev->parent; + struct sas_phy *phy = sas_find_local_phy(dev); - if (i->dft->lldd_I_T_nexus_reset) - res = i->dft->lldd_I_T_nexus_reset(dev); + res = sas_get_phy_attached_sas_addr(ex_dev, phy->number, addr); + /* break the wait early if the expander is unreachable, + * otherwise keep polling + */ + if (res == -ECOMM) + return res; + if (res != SMP_RESP_FUNC_ACC || SAS_ADDR(addr) == 0) + return 0; + else + return 1; +} - if (res != TMF_RESP_FUNC_COMPLETE) { - SAS_DPRINTK("%s: Unable to reset I T nexus?\n", __func__); - ret = -EAGAIN; +static int local_ata_check_ready(struct ata_link *link) +{ + struct ata_port *ap = link->ap; + struct domain_device *dev = ap->private_data; + struct sas_internal *i = dev_to_sas_internal(dev); + + if (i->dft->lldd_ata_check_ready) + return i->dft->lldd_ata_check_ready(dev); + else { + /* lldd's that don't implement 'ready' checking get the + * old default behavior of not coordinating reset + * recovery with libata + */ + return 1; } +} +static int sas_ata_hard_reset(struct ata_link *link, unsigned int *class, + unsigned long deadline) +{ + int ret = 0, res; + struct ata_port *ap = link->ap; + int (*check_ready)(struct ata_link *link); + struct domain_device *dev = ap->private_data; + struct sas_phy *phy = sas_find_local_phy(dev); + struct sas_internal *i = dev_to_sas_internal(dev); + + res = i->dft->lldd_I_T_nexus_reset(dev); + + if (res != TMF_RESP_FUNC_COMPLETE) + SAS_DPRINTK("%s: Unable to reset ata device?\n", __func__); + + if (scsi_is_sas_phy_local(phy)) + check_ready = local_ata_check_ready; + else + check_ready = smp_ata_check_ready; + + ret = ata_wait_after_reset(link, deadline, check_ready); + if (ret && ret != -EAGAIN) + ata_link_err(link, "COMRESET failed (errno=%d)\n", ret); + + /* XXX: if the class changes during the reset the upper layer + * should be informed, if the device has gone away we assume + * libsas will eventually delete it + */ switch (dev->sata_dev.command_set) { - case ATA_COMMAND_SET: - SAS_DPRINTK("%s: Found ATA device.\n", __func__); - *class = ATA_DEV_ATA; - break; - case ATAPI_COMMAND_SET: - SAS_DPRINTK("%s: Found ATAPI device.\n", __func__); - *class = ATA_DEV_ATAPI; - break; - default: - SAS_DPRINTK("%s: Unknown SATA command set: %d.\n", - __func__, - dev->sata_dev.command_set); - *class = ATA_DEV_UNKNOWN; - break; + case ATA_COMMAND_SET: + *class = ATA_DEV_ATA; + break; + case ATAPI_COMMAND_SET: + *class = ATA_DEV_ATAPI; + break; } ap->cbl = ATA_CBL_SATA; @@ -316,8 +361,7 @@ static int sas_ata_soft_reset(struct ata_link *link, unsigned int *class, { struct ata_port *ap = link->ap; struct domain_device *dev = ap->private_data; - struct sas_internal *i = - to_sas_internal(dev->port->ha->core.shost->transportt); + struct sas_internal *i = dev_to_sas_internal(dev); int res = TMF_RESP_FUNC_FAILED; int ret = 0; @@ -355,8 +399,7 @@ static int sas_ata_soft_reset(struct ata_link *link, unsigned int *class, */ static void sas_ata_internal_abort(struct sas_task *task) { - struct sas_internal *si = - to_sas_internal(task->dev->port->ha->core.shost->transportt); + struct sas_internal *si = dev_to_sas_internal(task->dev); unsigned long flags; int res; @@ -425,8 +468,7 @@ static void sas_ata_post_internal(struct ata_queued_cmd *qc) static void sas_ata_set_dmamode(struct ata_port *ap, struct ata_device *ata_dev) { struct domain_device *dev = ap->private_data; - struct sas_internal *i = - to_sas_internal(dev->port->ha->core.shost->transportt); + struct sas_internal *i = dev_to_sas_internal(dev); if (i->dft->lldd_ata_set_dmamode) i->dft->lldd_ata_set_dmamode(dev); diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index 7c59f97c0287..32e417e6c2f7 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c @@ -125,7 +125,11 @@ static int smp_execute_task(struct domain_device *dev, void *req, int req_size, task->task_status.stat == SAS_DATA_OVERRUN) { res = -EMSGSIZE; break; - } else { + } + if (task->task_status.resp == SAS_TASK_UNDELIVERED && + task->task_status.stat == SAS_DEVICE_UNKNOWN) + break; + else { SAS_DPRINTK("%s: task to dev %016llx response: 0x%x " "status 0x%x\n", __func__, SAS_ADDR(dev->sas_addr), @@ -1648,8 +1652,8 @@ static int sas_get_phy_change_count(struct domain_device *dev, return res; } -static int sas_get_phy_attached_sas_addr(struct domain_device *dev, - int phy_id, u8 *attached_sas_addr) +int sas_get_phy_attached_sas_addr(struct domain_device *dev, int phy_id, + u8 *attached_sas_addr) { int res; struct smp_resp *disc_resp; diff --git a/drivers/scsi/libsas/sas_internal.h b/drivers/scsi/libsas/sas_internal.h index 9e960b2d535a..a9a3bb94c1bc 100644 --- a/drivers/scsi/libsas/sas_internal.h +++ b/drivers/scsi/libsas/sas_internal.h @@ -89,7 +89,8 @@ int sas_smp_get_phy_events(struct sas_phy *phy); struct domain_device *sas_find_dev_by_rphy(struct sas_rphy *rphy); struct domain_device *sas_ex_to_ata(struct domain_device *ex_dev, int phy_id); - +int sas_get_phy_attached_sas_addr(struct domain_device *dev, int phy_id, + u8 *attached_sas_addr); void sas_hae_reset(struct work_struct *work); void sas_free_device(struct kref *kref); diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h index 18704a2e4f07..2079b18467a1 100644 --- a/include/scsi/libsas.h +++ b/include/scsi/libsas.h @@ -615,6 +615,7 @@ struct sas_domain_function_template { int (*lldd_clear_task_set)(struct domain_device *, u8 *lun); int (*lldd_I_T_nexus_reset)(struct domain_device *); int (*lldd_ata_soft_reset)(struct domain_device *); + int (*lldd_ata_check_ready)(struct domain_device *); void (*lldd_ata_set_dmamode)(struct domain_device *); int (*lldd_lu_reset)(struct domain_device *, u8 *lun); int (*lldd_query_task)(struct sas_task *); -- cgit v1.2.3 From f41a0c441c3fe43e79ebeb75584dbb5bfa83e5cd Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 21 Dec 2011 21:33:17 -0800 Subject: [SCSI] libsas: fix sas_find_local_phy(), take phy references In the direct-attached case this routine returns the phy on which this device was first discovered. Which is broken if we want to support wide-targets, as this phy reference can become stale even though the port is still active. In the expander-attached case this routine tries to lookup the phy by scanning the attached sas addresses of the parent expander, and BUG_ONs if it can't find it. However since eh and the libsas workqueue run independently we can still be attempting device recovery via eh after libsas has recorded the device as detached. This is even easier to hit now that eh is blocked while device domain rediscovery takes place, and that libata is fed more timed out commands increasing the chances that it will try to recover the ata device. Arrange for dev->phy to always point to a last known good phy, it may be stale after the port is torn down, but it will catch up for wide port reconfigurations, and never be NULL. Signed-off-by: Dan Williams Signed-off-by: James Bottomley --- drivers/scsi/aic94xx/aic94xx_tmf.c | 9 ++++++--- drivers/scsi/isci/task.c | 9 +++++---- drivers/scsi/libsas/sas_ata.c | 7 +++++-- drivers/scsi/libsas/sas_discover.c | 24 +++++++++++++++++++++++ drivers/scsi/libsas/sas_expander.c | 5 ++++- drivers/scsi/libsas/sas_internal.h | 1 + drivers/scsi/libsas/sas_port.c | 7 +++---- drivers/scsi/libsas/sas_scsi_host.c | 38 ++++++++++++++++++------------------- drivers/scsi/mvsas/mv_sas.c | 3 ++- drivers/scsi/pm8001/pm8001_sas.c | 19 ++++++++++++------- drivers/scsi/scsi_transport_sas.c | 23 ++++++++++++++++++++++ include/scsi/libsas.h | 9 +++++++-- include/scsi/scsi_transport_sas.h | 6 ++++++ 13 files changed, 116 insertions(+), 44 deletions(-) (limited to 'include/scsi') diff --git a/drivers/scsi/aic94xx/aic94xx_tmf.c b/drivers/scsi/aic94xx/aic94xx_tmf.c index 0add73bdf2a4..50b914ffab94 100644 --- a/drivers/scsi/aic94xx/aic94xx_tmf.c +++ b/drivers/scsi/aic94xx/aic94xx_tmf.c @@ -181,7 +181,7 @@ static int asd_clear_nexus_I_T(struct domain_device *dev, int asd_I_T_nexus_reset(struct domain_device *dev) { int res, tmp_res, i; - struct sas_phy *phy = sas_find_local_phy(dev); + struct sas_phy *phy = sas_get_local_phy(dev); /* Standard mandates link reset for ATA (type 0) and * hard reset for SSP (type 1) */ int reset_type = (dev->dev_type == SATA_DEV || @@ -201,7 +201,7 @@ int asd_I_T_nexus_reset(struct domain_device *dev) for (i = 0 ; i < 3; i++) { tmp_res = asd_clear_nexus_I_T(dev, NEXUS_PHASE_RESUME); if (tmp_res == TC_RESUME) - return res; + goto out; msleep(500); } @@ -211,7 +211,10 @@ int asd_I_T_nexus_reset(struct domain_device *dev) dev_printk(KERN_ERR, &phy->dev, "Failed to resume nexus after reset 0x%x\n", tmp_res); - return TMF_RESP_FUNC_FAILED; + res = TMF_RESP_FUNC_FAILED; + out: + sas_put_local_phy(phy); + return res; } static int asd_clear_nexus_I_T_L(struct domain_device *dev, u8 *lun) diff --git a/drivers/scsi/isci/task.c b/drivers/scsi/isci/task.c index 4bd88ef83cdf..b96e6044eda9 100644 --- a/drivers/scsi/isci/task.c +++ b/drivers/scsi/isci/task.c @@ -1332,7 +1332,7 @@ isci_task_request_complete(struct isci_host *ihost, static int isci_reset_device(struct isci_host *ihost, struct isci_remote_device *idev) { - struct sas_phy *phy = sas_find_local_phy(idev->domain_dev); + struct sas_phy *phy = sas_get_local_phy(idev->domain_dev); enum sci_status status; unsigned long flags; int rc; @@ -1347,8 +1347,8 @@ static int isci_reset_device(struct isci_host *ihost, dev_dbg(&ihost->pdev->dev, "%s: sci_remote_device_reset(%p) returned %d!\n", __func__, idev, status); - - return TMF_RESP_FUNC_FAILED; + rc = TMF_RESP_FUNC_FAILED; + goto out; } spin_unlock_irqrestore(&ihost->scic_lock, flags); @@ -1369,7 +1369,8 @@ static int isci_reset_device(struct isci_host *ihost, } dev_dbg(&ihost->pdev->dev, "%s: idev %p complete.\n", __func__, idev); - + out: + sas_put_local_phy(phy); return rc; } diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index 5fdb63ad94b7..92f7e78a096c 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -284,9 +284,10 @@ static int smp_ata_check_ready(struct ata_link *link) struct ata_port *ap = link->ap; struct domain_device *dev = ap->private_data; struct domain_device *ex_dev = dev->parent; - struct sas_phy *phy = sas_find_local_phy(dev); + struct sas_phy *phy = sas_get_local_phy(dev); res = sas_get_phy_attached_sas_addr(ex_dev, phy->number, addr); + sas_put_local_phy(phy); /* break the wait early if the expander is unreachable, * otherwise keep polling */ @@ -319,10 +320,10 @@ static int sas_ata_hard_reset(struct ata_link *link, unsigned int *class, unsigned long deadline) { int ret = 0, res; + struct sas_phy *phy; struct ata_port *ap = link->ap; int (*check_ready)(struct ata_link *link); struct domain_device *dev = ap->private_data; - struct sas_phy *phy = sas_find_local_phy(dev); struct sas_internal *i = dev_to_sas_internal(dev); res = i->dft->lldd_I_T_nexus_reset(dev); @@ -330,10 +331,12 @@ static int sas_ata_hard_reset(struct ata_link *link, unsigned int *class, if (res != TMF_RESP_FUNC_COMPLETE) SAS_DPRINTK("%s: Unable to reset ata device?\n", __func__); + phy = sas_get_local_phy(dev); if (scsi_is_sas_phy_local(phy)) check_ready = local_ata_check_ready; else check_ready = smp_ata_check_ready; + sas_put_local_phy(phy); ret = ata_wait_after_reset(link, deadline, check_ready); if (ret && ret != -EAGAIN) diff --git a/drivers/scsi/libsas/sas_discover.c b/drivers/scsi/libsas/sas_discover.c index c56cc6400819..789b50861bb9 100644 --- a/drivers/scsi/libsas/sas_discover.c +++ b/drivers/scsi/libsas/sas_discover.c @@ -147,6 +147,7 @@ static int sas_get_port_device(struct asd_sas_port *port) memset(port->disc.eeds_a, 0, SAS_ADDR_SIZE); memset(port->disc.eeds_b, 0, SAS_ADDR_SIZE); port->disc.max_level = 0; + sas_device_set_phy(dev, port->port); dev->rphy = rphy; @@ -234,6 +235,9 @@ void sas_free_device(struct kref *kref) if (dev->parent) sas_put_device(dev->parent); + sas_port_put_phy(dev->phy); + dev->phy = NULL; + /* remove the phys and ports, everything else should be gone */ if (dev->dev_type == EDGE_DEV || dev->dev_type == FANOUT_DEV) kfree(dev->ex_dev.ex_phy); @@ -308,6 +312,26 @@ void sas_unregister_domain_devices(struct asd_sas_port *port) } +void sas_device_set_phy(struct domain_device *dev, struct sas_port *port) +{ + struct sas_ha_struct *ha; + struct sas_phy *new_phy; + + if (!dev) + return; + + ha = dev->port->ha; + new_phy = sas_port_get_phy(port); + + /* pin and record last seen phy */ + spin_lock_irq(&ha->phy_port_lock); + if (new_phy) { + sas_port_put_phy(dev->phy); + dev->phy = new_phy; + } + spin_unlock_irq(&ha->phy_port_lock); +} + /* ---------- Discovery and Revalidation ---------- */ /** diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index 6fb1f3afd1e0..68a80a00f73f 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c @@ -723,6 +723,7 @@ static struct domain_device *sas_ex_discover_end_dev( } } sas_ex_get_linkrate(parent, child, phy); + sas_device_set_phy(child, phy->port); #ifdef CONFIG_SCSI_SAS_ATA if ((phy->attached_tproto & SAS_PROTOCOL_STP) || phy->attached_sata_dev) { @@ -1810,7 +1811,7 @@ static void sas_unregister_devs_sas_addr(struct domain_device *parent, { struct expander_device *ex_dev = &parent->ex_dev; struct ex_phy *phy = &ex_dev->ex_phy[phy_id]; - struct domain_device *child, *n; + struct domain_device *child, *n, *found = NULL; if (last) { list_for_each_entry_safe(child, n, &ex_dev->children, siblings) { @@ -1822,6 +1823,7 @@ static void sas_unregister_devs_sas_addr(struct domain_device *parent, sas_unregister_ex_tree(parent->port, child); else sas_unregister_dev(parent->port, child); + found = child; break; } } @@ -1830,6 +1832,7 @@ static void sas_unregister_devs_sas_addr(struct domain_device *parent, memset(phy->attached_sas_addr, 0, SAS_ADDR_SIZE); if (phy->port) { sas_port_delete_phy(phy->port, phy->phy); + sas_device_set_phy(found, phy->port); if (phy->port->num_phys == 0) sas_port_delete(phy->port); phy->port = NULL; diff --git a/drivers/scsi/libsas/sas_internal.h b/drivers/scsi/libsas/sas_internal.h index a9a3bb94c1bc..c8febc71c40d 100644 --- a/drivers/scsi/libsas/sas_internal.h +++ b/drivers/scsi/libsas/sas_internal.h @@ -87,6 +87,7 @@ int sas_smp_phy_control(struct domain_device *dev, int phy_id, enum phy_func phy_func, struct sas_phy_linkrates *); int sas_smp_get_phy_events(struct sas_phy *phy); +void sas_device_set_phy(struct domain_device *dev, struct sas_port *port); struct domain_device *sas_find_dev_by_rphy(struct sas_rphy *rphy); struct domain_device *sas_ex_to_ata(struct domain_device *ex_dev, int phy_id); int sas_get_phy_attached_sas_addr(struct domain_device *dev, int phy_id, diff --git a/drivers/scsi/libsas/sas_port.c b/drivers/scsi/libsas/sas_port.c index 2980bde4e34a..31adcd1b4191 100644 --- a/drivers/scsi/libsas/sas_port.c +++ b/drivers/scsi/libsas/sas_port.c @@ -108,9 +108,6 @@ static void sas_form_port(struct asd_sas_phy *phy) port->num_phys++; port->phy_mask |= (1U << phy->id); - if (!port->phy) - port->phy = phy->phy; - if (*(u64 *)port->attached_sas_addr == 0) { port->class = phy->class; memcpy(port->attached_sas_addr, phy->attached_sas_addr, @@ -175,8 +172,10 @@ void sas_deform_port(struct asd_sas_phy *phy, int gone) sas_unregister_domain_devices(port); sas_port_delete(port->port); port->port = NULL; - } else + } else { sas_port_delete_phy(port->port, phy->phy); + sas_device_set_phy(dev, port->port); + } if (si->dft->lldd_port_deformed) si->dft->lldd_port_deformed(phy); diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c index 5cc44fddfe95..94ef76316c31 100644 --- a/drivers/scsi/libsas/sas_scsi_host.c +++ b/drivers/scsi/libsas/sas_scsi_host.c @@ -439,30 +439,26 @@ static int sas_recover_I_T(struct domain_device *dev) return res; } -/* Find the sas_phy that's attached to this device */ -struct sas_phy *sas_find_local_phy(struct domain_device *dev) +/* take a reference on the last known good phy for this device */ +struct sas_phy *sas_get_local_phy(struct domain_device *dev) { - struct domain_device *pdev = dev->parent; - struct ex_phy *exphy = NULL; - int i; + struct sas_ha_struct *ha = dev->port->ha; + struct sas_phy *phy; + unsigned long flags; - /* Directly attached device */ - if (!pdev) - return dev->port->phy; + /* a published domain device always has a valid phy, it may be + * stale, but it is never NULL + */ + BUG_ON(!dev->phy); - /* Otherwise look in the expander */ - for (i = 0; i < pdev->ex_dev.num_phys; i++) - if (!memcmp(dev->sas_addr, - pdev->ex_dev.ex_phy[i].attached_sas_addr, - SAS_ADDR_SIZE)) { - exphy = &pdev->ex_dev.ex_phy[i]; - break; - } + spin_lock_irqsave(&ha->phy_port_lock, flags); + phy = dev->phy; + get_device(&phy->dev); + spin_unlock_irqrestore(&ha->phy_port_lock, flags); - BUG_ON(!exphy); - return exphy->phy; + return phy; } -EXPORT_SYMBOL_GPL(sas_find_local_phy); +EXPORT_SYMBOL_GPL(sas_get_local_phy); /* Attempt to send a LUN reset message to a device */ int sas_eh_device_reset_handler(struct scsi_cmnd *cmd) @@ -489,7 +485,7 @@ int sas_eh_device_reset_handler(struct scsi_cmnd *cmd) int sas_eh_bus_reset_handler(struct scsi_cmnd *cmd) { struct domain_device *dev = cmd_to_domain_dev(cmd); - struct sas_phy *phy = sas_find_local_phy(dev); + struct sas_phy *phy = sas_get_local_phy(dev); int res; res = sas_phy_reset(phy, 1); @@ -497,6 +493,8 @@ int sas_eh_bus_reset_handler(struct scsi_cmnd *cmd) SAS_DPRINTK("Bus reset of %s failed 0x%x\n", kobject_name(&phy->dev.kobj), res); + sas_put_local_phy(phy); + if (res == TMF_RESP_FUNC_SUCC || res == TMF_RESP_FUNC_COMPLETE) return SUCCESS; diff --git a/drivers/scsi/mvsas/mv_sas.c b/drivers/scsi/mvsas/mv_sas.c index cd882230591f..b68a65390f0d 100644 --- a/drivers/scsi/mvsas/mv_sas.c +++ b/drivers/scsi/mvsas/mv_sas.c @@ -1474,10 +1474,11 @@ static int mvs_debug_issue_ssp_tmf(struct domain_device *dev, static int mvs_debug_I_T_nexus_reset(struct domain_device *dev) { int rc; - struct sas_phy *phy = sas_find_local_phy(dev); + struct sas_phy *phy = sas_get_local_phy(dev); int reset_type = (dev->dev_type == SATA_DEV || (dev->tproto & SAS_PROTOCOL_STP)) ? 0 : 1; rc = sas_phy_reset(phy, reset_type); + sas_put_local_phy(phy); msleep(2000); return rc; } diff --git a/drivers/scsi/pm8001/pm8001_sas.c b/drivers/scsi/pm8001/pm8001_sas.c index 310860e37d98..3b11edd4a50c 100644 --- a/drivers/scsi/pm8001/pm8001_sas.c +++ b/drivers/scsi/pm8001/pm8001_sas.c @@ -967,12 +967,14 @@ int pm8001_I_T_nexus_reset(struct domain_device *dev) pm8001_dev = dev->lldd_dev; pm8001_ha = pm8001_find_ha_by_dev(dev); - phy = sas_find_local_phy(dev); + phy = sas_get_local_phy(dev); if (dev_is_sata(dev)) { DECLARE_COMPLETION_ONSTACK(completion_setstate); - if (scsi_is_sas_phy_local(phy)) - return 0; + if (scsi_is_sas_phy_local(phy)) { + rc = 0; + goto out; + } rc = sas_phy_reset(phy, 1); msleep(2000); rc = pm8001_exec_internal_task_abort(pm8001_ha, pm8001_dev , @@ -981,12 +983,14 @@ int pm8001_I_T_nexus_reset(struct domain_device *dev) rc = PM8001_CHIP_DISP->set_dev_state_req(pm8001_ha, pm8001_dev, 0x01); wait_for_completion(&completion_setstate); - } else{ - rc = sas_phy_reset(phy, 1); - msleep(2000); + } else { + rc = sas_phy_reset(phy, 1); + msleep(2000); } PM8001_EH_DBG(pm8001_ha, pm8001_printk(" for device[%x]:rc=%d\n", pm8001_dev->device_id, rc)); + out: + sas_put_local_phy(phy); return rc; } @@ -998,10 +1002,11 @@ int pm8001_lu_reset(struct domain_device *dev, u8 *lun) struct pm8001_device *pm8001_dev = dev->lldd_dev; struct pm8001_hba_info *pm8001_ha = pm8001_find_ha_by_dev(dev); if (dev_is_sata(dev)) { - struct sas_phy *phy = sas_find_local_phy(dev); + struct sas_phy *phy = sas_get_local_phy(dev); rc = pm8001_exec_internal_task_abort(pm8001_ha, pm8001_dev , dev, 1, 0); rc = sas_phy_reset(phy, 1); + sas_put_local_phy(phy); rc = PM8001_CHIP_DISP->set_dev_state_req(pm8001_ha, pm8001_dev, 0x01); msleep(2000); diff --git a/drivers/scsi/scsi_transport_sas.c b/drivers/scsi/scsi_transport_sas.c index ab3bd0b5ffd9..7d69a25d2004 100644 --- a/drivers/scsi/scsi_transport_sas.c +++ b/drivers/scsi/scsi_transport_sas.c @@ -1059,6 +1059,29 @@ int scsi_is_sas_port(const struct device *dev) } EXPORT_SYMBOL(scsi_is_sas_port); +/** + * sas_port_get_phy - try to take a reference on a port member + * @port: port to check + */ +struct sas_phy *sas_port_get_phy(struct sas_port *port) +{ + struct sas_phy *phy; + + mutex_lock(&port->phy_list_mutex); + if (list_empty(&port->phy_list)) + phy = NULL; + else { + struct list_head *ent = port->phy_list.next; + + phy = list_entry(ent, typeof(*phy), port_siblings); + get_device(&phy->dev); + } + mutex_unlock(&port->phy_list_mutex); + + return phy; +} +EXPORT_SYMBOL(sas_port_get_phy); + /** * sas_port_add_phy - add another phy to a port to form a wide port * @port: port to add the phy to diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h index 2079b18467a1..55bab8633807 100644 --- a/include/scsi/libsas.h +++ b/include/scsi/libsas.h @@ -192,6 +192,7 @@ struct domain_device { struct domain_device *parent; struct list_head siblings; /* devices on the same level */ struct asd_sas_port *port; /* shortcut to root of the tree */ + struct sas_phy *phy; struct list_head dev_list_node; struct list_head disco_list_node; /* awaiting probe or destruct */ @@ -243,7 +244,6 @@ struct asd_sas_port { struct list_head destroy_list; enum sas_linkrate linkrate; - struct sas_phy *phy; struct work_struct work; /* public: */ @@ -429,6 +429,11 @@ static inline unsigned int to_sas_gpio_od(int device, int bit) return 3 * device + bit; } +static inline void sas_put_local_phy(struct sas_phy *phy) +{ + put_device(&phy->dev); +} + #ifdef CONFIG_SCSI_SAS_HOST_SMP int try_test_sas_gpio_gp_bit(unsigned int od, u8 *data, u8 index, u8 count); #else @@ -684,7 +689,7 @@ extern int sas_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy, extern void sas_ssp_task_response(struct device *dev, struct sas_task *task, struct ssp_response_iu *iu); -struct sas_phy *sas_find_local_phy(struct domain_device *dev); +struct sas_phy *sas_get_local_phy(struct domain_device *dev); int sas_request_addr(struct Scsi_Host *shost, u8 *addr); diff --git a/include/scsi/scsi_transport_sas.h b/include/scsi/scsi_transport_sas.h index 42817facaeda..98b3a20a0102 100644 --- a/include/scsi/scsi_transport_sas.h +++ b/include/scsi/scsi_transport_sas.h @@ -209,6 +209,12 @@ void sas_port_add_phy(struct sas_port *, struct sas_phy *); void sas_port_delete_phy(struct sas_port *, struct sas_phy *); void sas_port_mark_backlink(struct sas_port *); int scsi_is_sas_port(const struct device *); +struct sas_phy *sas_port_get_phy(struct sas_port *port); +static inline void sas_port_put_phy(struct sas_phy *phy) +{ + if (phy) + put_device(&phy->dev); +} extern struct scsi_transport_template * sas_attach_transport(struct sas_function_template *); -- cgit v1.2.3 From 7d05919aad080074453de880822fe5805875645f Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 10 Jan 2012 14:39:13 -0800 Subject: [SCSI] libsas: mark all domain devices gone if root port disappears If the top level expander is hot removed, mark all child devices as gone before unregistration to short circuit futile recovery. Signed-off-by: Dan Williams Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_discover.c | 8 ++++++-- drivers/scsi/libsas/sas_port.c | 4 +--- include/scsi/libsas.h | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) (limited to 'include/scsi') diff --git a/drivers/scsi/libsas/sas_discover.c b/drivers/scsi/libsas/sas_discover.c index 789b50861bb9..b91866a8233b 100644 --- a/drivers/scsi/libsas/sas_discover.c +++ b/drivers/scsi/libsas/sas_discover.c @@ -299,12 +299,16 @@ void sas_unregister_dev(struct asd_sas_port *port, struct domain_device *dev) } } -void sas_unregister_domain_devices(struct asd_sas_port *port) +void sas_unregister_domain_devices(struct asd_sas_port *port, int gone) { struct domain_device *dev, *n; - list_for_each_entry_safe_reverse(dev, n, &port->dev_list, dev_list_node) + list_for_each_entry_safe_reverse(dev, n, &port->dev_list, dev_list_node) { + if (gone) + set_bit(SAS_DEV_GONE, &dev->state); sas_unregister_dev(port, dev); + } + list_for_each_entry_safe(dev, n, &port->disco_list, disco_list_node) sas_unregister_dev(port, dev); diff --git a/drivers/scsi/libsas/sas_port.c b/drivers/scsi/libsas/sas_port.c index 31adcd1b4191..59ee8a0a6ea9 100644 --- a/drivers/scsi/libsas/sas_port.c +++ b/drivers/scsi/libsas/sas_port.c @@ -167,9 +167,7 @@ void sas_deform_port(struct asd_sas_phy *phy, int gone) dev->pathways--; if (port->num_phys == 1) { - if (dev && gone) - set_bit(SAS_DEV_GONE, &dev->state); - sas_unregister_domain_devices(port); + sas_unregister_domain_devices(port, gone); sas_port_delete(port->port); port->port = NULL; } else { diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h index 55bab8633807..4a42be34fad0 100644 --- a/include/scsi/libsas.h +++ b/include/scsi/libsas.h @@ -664,7 +664,7 @@ void sas_init_ex_attr(void); int sas_ex_revalidate_domain(struct domain_device *); -void sas_unregister_domain_devices(struct asd_sas_port *port); +void sas_unregister_domain_devices(struct asd_sas_port *port, int gone); void sas_init_disc(struct sas_discovery *disc, struct asd_sas_port *); int sas_discover_event(struct asd_sas_port *, enum discover_event ev); -- cgit v1.2.3 From d230ce691c7712c4f56ba3378d6d2f44628a49f1 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 11 Jan 2012 12:08:36 -0800 Subject: [SCSI] libsas: fix mixed topology recovery If we have a domain with sas and sata devices there may still be sas recovery actions to take after peeling off the commands to send to libata. Reported-by: Andrzej Jakowski Signed-off-by: Dan Williams Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_ata.c | 8 ++------ drivers/scsi/libsas/sas_scsi_host.c | 13 +++++++------ include/scsi/sas_ata.h | 9 ++++----- 3 files changed, 13 insertions(+), 17 deletions(-) (limited to 'include/scsi') diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index 26a943eb153a..40edf520d69a 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -699,10 +699,9 @@ void sas_ata_strategy_handler(struct Scsi_Host *shost) sas_enable_revalidation(sas_ha); } -int sas_ata_eh(struct Scsi_Host *shost, struct list_head *work_q, - struct list_head *done_q) +void sas_ata_eh(struct Scsi_Host *shost, struct list_head *work_q, + struct list_head *done_q) { - int rtn = 0; struct scsi_cmnd *cmd, *n; struct ata_port *ap; @@ -719,7 +718,6 @@ int sas_ata_eh(struct Scsi_Host *shost, struct list_head *work_q, if (ap && ap != ddev->sata_dev.ap) continue; ap = ddev->sata_dev.ap; - rtn = 1; list_move(&cmd->eh_entry, &sata_q); } @@ -741,8 +739,6 @@ int sas_ata_eh(struct Scsi_Host *shost, struct list_head *work_q, list_del_init(sata_q.next); } } while (ap); - - return rtn; } void sas_ata_schedule_reset(struct domain_device *dev) diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c index b563ff27626b..e58ca50517d5 100644 --- a/drivers/scsi/libsas/sas_scsi_host.c +++ b/drivers/scsi/libsas/sas_scsi_host.c @@ -678,7 +678,8 @@ void sas_scsi_recover_host(struct Scsi_Host *shost) shost->host_eh_scheduled = 0; spin_unlock_irqrestore(shost->host_lock, flags); - SAS_DPRINTK("Enter %s\n", __func__); + SAS_DPRINTK("Enter %s busy: %d failed: %d\n", + __func__, shost->host_busy, shost->host_failed); /* * Deal with commands that still have SAS tasks (i.e. they didn't * complete via the normal sas_task completion mechanism) @@ -693,9 +694,9 @@ void sas_scsi_recover_host(struct Scsi_Host *shost) * scsi_unjam_host does, but we skip scsi_eh_abort_cmds because any * command we see here has no sas_task and is thus unknown to the HA. */ - if (!sas_ata_eh(shost, &eh_work_q, &ha->eh_done_q)) - if (!scsi_eh_get_sense(&eh_work_q, &ha->eh_done_q)) - scsi_eh_ready_devs(shost, &eh_work_q, &ha->eh_done_q); + sas_ata_eh(shost, &eh_work_q, &ha->eh_done_q); + if (!scsi_eh_get_sense(&eh_work_q, &ha->eh_done_q)) + scsi_eh_ready_devs(shost, &eh_work_q, &ha->eh_done_q); out: clear_bit(SAS_HA_FROZEN, &ha->state); @@ -707,8 +708,8 @@ out: scsi_eh_flush_done_q(&ha->eh_done_q); - SAS_DPRINTK("--- Exit %s\n", __func__); - return; + SAS_DPRINTK("--- Exit %s: busy: %d failed: %d\n", + __func__, shost->host_busy, shost->host_failed); } enum blk_eh_timer_return sas_scsi_timed_out(struct scsi_cmnd *cmd) diff --git a/include/scsi/sas_ata.h b/include/scsi/sas_ata.h index da3f37727387..cb724fd010f6 100644 --- a/include/scsi/sas_ata.h +++ b/include/scsi/sas_ata.h @@ -41,8 +41,8 @@ int sas_ata_init_host_and_port(struct domain_device *found_dev, void sas_ata_task_abort(struct sas_task *task); void sas_ata_strategy_handler(struct Scsi_Host *shost); -int sas_ata_eh(struct Scsi_Host *shost, struct list_head *work_q, - struct list_head *done_q); +void sas_ata_eh(struct Scsi_Host *shost, struct list_head *work_q, + struct list_head *done_q); void sas_probe_sata(struct work_struct *work); void sas_ata_schedule_reset(struct domain_device *dev); void sas_ata_wait_eh(struct domain_device *dev); @@ -66,10 +66,9 @@ static inline void sas_ata_strategy_handler(struct Scsi_Host *shost) { } -static inline int sas_ata_eh(struct Scsi_Host *shost, struct list_head *work_q, - struct list_head *done_q) +static inline void sas_ata_eh(struct Scsi_Host *shost, struct list_head *work_q, + struct list_head *done_q) { - return 0; } static inline void sas_probe_sata(struct work_struct *work) -- cgit v1.2.3 From 354cf82980e2449e71fdaa3c6f170357ebd65467 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 12 Jan 2012 17:57:35 -0800 Subject: [SCSI] libsas: let libata recover links that fail to transmit initial sig-fis libsas fails to discover all sata devices in the domain. If a device fails negotiation and does not transmit a signature fis the link needs recovery. libata already understands how to manage slow to come up links, so treat these conditions as ata device attach events for the purposes of creating an ata_port. This allows libata to manage retrying link bring up. Rediscovery is modified to be careful about checking changes in dev_type. It looks like libsas leaks old devices if the sas address changes, but that's a fix for another patch. Acked-by: Jack Wang Signed-off-by: Dan Williams Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_ata.c | 71 +++++++++++++-- drivers/scsi/libsas/sas_discover.c | 1 + drivers/scsi/libsas/sas_expander.c | 178 +++++++++++++++++++++---------------- drivers/scsi/libsas/sas_internal.h | 6 +- include/scsi/sas.h | 4 +- include/scsi/sas_ata.h | 8 +- 6 files changed, 179 insertions(+), 89 deletions(-) (limited to 'include/scsi') diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index ba1ebfe991d7..25008a42412f 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -278,26 +278,84 @@ static struct sas_internal *dev_to_sas_internal(struct domain_device *dev) return to_sas_internal(dev->port->ha->core.shost->transportt); } +static void sas_get_ata_command_set(struct domain_device *dev); + +int sas_get_ata_info(struct domain_device *dev, struct ex_phy *phy) +{ + if (phy->attached_tproto & SAS_PROTOCOL_STP) + dev->tproto = phy->attached_tproto; + if (phy->attached_sata_dev) + dev->tproto |= SATA_DEV; + + if (phy->attached_dev_type == SATA_PENDING) + dev->dev_type = SATA_PENDING; + else { + int res; + + dev->dev_type = SATA_DEV; + res = sas_get_report_phy_sata(dev->parent, phy->phy_id, + &dev->sata_dev.rps_resp); + if (res) { + SAS_DPRINTK("report phy sata to %016llx:0x%x returned " + "0x%x\n", SAS_ADDR(dev->parent->sas_addr), + phy->phy_id, res); + return res; + } + memcpy(dev->frame_rcvd, &dev->sata_dev.rps_resp.rps.fis, + sizeof(struct dev_to_host_fis)); + /* TODO switch to ata_dev_classify() */ + sas_get_ata_command_set(dev); + } + return 0; +} + +static int sas_ata_clear_pending(struct domain_device *dev, struct ex_phy *phy) +{ + int res; + + /* we weren't pending, so successfully end the reset sequence now */ + if (dev->dev_type != SATA_PENDING) + return 1; + + /* hmmm, if this succeeds do we need to repost the domain_device to the + * lldd so it can pick up new parameters? + */ + res = sas_get_ata_info(dev, phy); + if (res) + return 0; /* retry */ + else + return 1; +} + static int smp_ata_check_ready(struct ata_link *link) { int res; - u8 addr[8]; struct ata_port *ap = link->ap; struct domain_device *dev = ap->private_data; struct domain_device *ex_dev = dev->parent; struct sas_phy *phy = sas_get_local_phy(dev); + struct ex_phy *ex_phy = &ex_dev->ex_dev.ex_phy[phy->number]; - res = sas_get_phy_attached_sas_addr(ex_dev, phy->number, addr); + res = sas_ex_phy_discover(ex_dev, phy->number); sas_put_local_phy(phy); + /* break the wait early if the expander is unreachable, * otherwise keep polling */ if (res == -ECOMM) return res; - if (res != SMP_RESP_FUNC_ACC || SAS_ADDR(addr) == 0) + if (res != SMP_RESP_FUNC_ACC) return 0; - else - return 1; + + switch (ex_phy->attached_dev_type) { + case SATA_PENDING: + return 0; + case SAS_END_DEV: + if (ex_phy->attached_sata_dev) + return sas_ata_clear_pending(dev, ex_phy); + default: + return -ENODEV; + } } static int local_ata_check_ready(struct ata_link *link) @@ -584,6 +642,9 @@ static void sas_get_ata_command_set(struct domain_device *dev) struct dev_to_host_fis *fis = (struct dev_to_host_fis *) dev->frame_rcvd; + if (dev->dev_type == SATA_PENDING) + return; + if ((fis->sector_count == 1 && /* ATA */ fis->lbal == 1 && fis->lbam == 0 && diff --git a/drivers/scsi/libsas/sas_discover.c b/drivers/scsi/libsas/sas_discover.c index c1ac99d25f5e..8bcfcaa7b2e1 100644 --- a/drivers/scsi/libsas/sas_discover.c +++ b/drivers/scsi/libsas/sas_discover.c @@ -48,6 +48,7 @@ void sas_init_dev(struct domain_device *dev) case SATA_DEV: case SATA_PM: case SATA_PM_PORT: + case SATA_PENDING: INIT_LIST_HEAD(&dev->sata_dev.children); break; default: diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index 4b2ecd35dc5a..7e2d3c4c6171 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c @@ -183,13 +183,27 @@ static char sas_route_char(struct domain_device *dev, struct ex_phy *phy) } } -static void sas_set_ex_phy(struct domain_device *dev, int phy_id, - void *disc_resp) +static enum sas_dev_type to_dev_type(struct discover_resp *dr) { + /* This is detecting a failure to transmit initial dev to host + * FIS as described in section J.5 of sas-2 r16 + */ + if (dr->attached_dev_type == NO_DEVICE && dr->attached_sata_dev && + dr->linkrate >= SAS_LINK_RATE_1_5_GBPS) + return SATA_PENDING; + else + return dr->attached_dev_type; +} + +static void sas_set_ex_phy(struct domain_device *dev, int phy_id, void *rsp) +{ + enum sas_dev_type dev_type; + enum sas_linkrate linkrate; + u8 sas_addr[SAS_ADDR_SIZE]; + struct smp_resp *resp = rsp; + struct discover_resp *dr = &resp->disc; struct expander_device *ex = &dev->ex_dev; struct ex_phy *phy = &ex->ex_phy[phy_id]; - struct smp_resp *resp = disc_resp; - struct discover_resp *dr = &resp->disc; struct sas_rphy *rphy = dev->rphy; bool new_phy = !phy->phy; char *type; @@ -213,8 +227,13 @@ static void sas_set_ex_phy(struct domain_device *dev, int phy_id, break; } + /* check if anything important changed to squelch debug */ + dev_type = phy->attached_dev_type; + linkrate = phy->linkrate; + memcpy(sas_addr, phy->attached_sas_addr, SAS_ADDR_SIZE); + + phy->attached_dev_type = to_dev_type(dr); phy->phy_id = phy_id; - phy->attached_dev_type = dr->attached_dev_type; phy->linkrate = dr->linkrate; phy->attached_sata_host = dr->attached_sata_host; phy->attached_sata_dev = dr->attached_sata_dev; @@ -229,7 +248,7 @@ static void sas_set_ex_phy(struct domain_device *dev, int phy_id, 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.device_type = dr->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; @@ -246,6 +265,9 @@ static void sas_set_ex_phy(struct domain_device *dev, int phy_id, } switch (phy->attached_dev_type) { + case SATA_PENDING: + type = "stp pending"; + break; case NO_DEVICE: type = "no device"; break; @@ -270,6 +292,16 @@ static void sas_set_ex_phy(struct domain_device *dev, int phy_id, type = "unknown"; } + /* this routine is polled by libata error recovery so filter + * unimportant messages + */ + if (new_phy || phy->attached_dev_type != dev_type || + phy->linkrate != linkrate || + SAS_ADDR(phy->attached_sas_addr) != SAS_ADDR(sas_addr)) + /* pass */; + else + return; + SAS_DPRINTK("ex %016llx phy%02d:%c:%X attached: %016llx (%s)\n", SAS_ADDR(dev->sas_addr), phy->phy_id, sas_route_char(dev, phy), phy->linkrate, @@ -304,50 +336,25 @@ struct domain_device *sas_ex_to_ata(struct domain_device *ex_dev, int phy_id) static int sas_ex_phy_discover_helper(struct domain_device *dev, u8 *disc_req, u8 *disc_resp, int single) { - struct domain_device *ata_dev = sas_ex_to_ata(dev, single); - int i, res; + struct discover_resp *dr; + int res; disc_req[9] = single; - for (i = 1 ; i < 3; i++) { - struct discover_resp *dr; - res = smp_execute_task(dev, disc_req, DISCOVER_REQ_SIZE, - disc_resp, DISCOVER_RESP_SIZE); - if (res) - return res; - dr = &((struct smp_resp *)disc_resp)->disc; - if (memcmp(dev->sas_addr, dr->attached_sas_addr, - SAS_ADDR_SIZE) == 0) { - sas_printk("Found loopback topology, just ignore it!\n"); - return 0; - } - - /* This is detecting a failure to transmit initial - * dev to host FIS as described in section J.5 of - * sas-2 r16 - */ - if (!(dr->attached_dev_type == 0 && - dr->attached_sata_dev)) - break; - - /* In order to generate the dev to host FIS, we send a - * link reset to the expander port. If a device was - * previously detected on this port we ask libata to - * manage the reset and link recovery. - */ - if (ata_dev) { - sas_ata_schedule_reset(ata_dev); - break; - } - sas_smp_phy_control(dev, single, PHY_FUNC_LINK_RESET, NULL); - /* Wait for the reset to trigger the negotiation */ - msleep(500); + res = smp_execute_task(dev, disc_req, DISCOVER_REQ_SIZE, + disc_resp, DISCOVER_RESP_SIZE); + if (res) + return res; + dr = &((struct smp_resp *)disc_resp)->disc; + if (memcmp(dev->sas_addr, dr->attached_sas_addr, SAS_ADDR_SIZE) == 0) { + sas_printk("Found loopback topology, just ignore it!\n"); + return 0; } sas_set_ex_phy(dev, single, disc_resp); return 0; } -static int sas_ex_phy_discover(struct domain_device *dev, int single) +int sas_ex_phy_discover(struct domain_device *dev, int single) { struct expander_device *ex = &dev->ex_dev; int res = 0; @@ -652,9 +659,8 @@ int sas_smp_get_phy_events(struct sas_phy *phy) #define RPS_REQ_SIZE 16 #define RPS_RESP_SIZE 60 -static int sas_get_report_phy_sata(struct domain_device *dev, - int phy_id, - struct smp_resp *rps_resp) +int sas_get_report_phy_sata(struct domain_device *dev, int phy_id, + struct smp_resp *rps_resp) { int res; u8 *rps_req = alloc_smp_req(RPS_REQ_SIZE); @@ -764,21 +770,9 @@ static struct domain_device *sas_ex_discover_end_dev( #ifdef CONFIG_SCSI_SAS_ATA if ((phy->attached_tproto & SAS_PROTOCOL_STP) || phy->attached_sata_dev) { - child->dev_type = SATA_DEV; - if (phy->attached_tproto & SAS_PROTOCOL_STP) - child->tproto = phy->attached_tproto; - if (phy->attached_sata_dev) - child->tproto |= SATA_DEV; - res = sas_get_report_phy_sata(parent, phy_id, - &child->sata_dev.rps_resp); - if (res) { - SAS_DPRINTK("report phy sata to %016llx:0x%x returned " - "0x%x\n", SAS_ADDR(parent->sas_addr), - phy_id, res); + res = sas_get_ata_info(child, phy); + if (res) goto out_free; - } - memcpy(child->frame_rcvd, &child->sata_dev.rps_resp.rps.fis, - sizeof(struct dev_to_host_fis)); rphy = sas_end_device_alloc(phy->port); if (unlikely(!rphy)) @@ -993,7 +987,8 @@ static int sas_ex_discover_dev(struct domain_device *dev, int phy_id) if (ex_phy->attached_dev_type != SAS_END_DEV && ex_phy->attached_dev_type != FANOUT_DEV && - ex_phy->attached_dev_type != EDGE_DEV) { + ex_phy->attached_dev_type != EDGE_DEV && + ex_phy->attached_dev_type != SATA_PENDING) { SAS_DPRINTK("unknown device type(0x%x) attached to ex %016llx " "phy 0x%x\n", ex_phy->attached_dev_type, SAS_ADDR(dev->sas_addr), @@ -1019,6 +1014,7 @@ static int sas_ex_discover_dev(struct domain_device *dev, int phy_id) switch (ex_phy->attached_dev_type) { case SAS_END_DEV: + case SATA_PENDING: child = sas_ex_discover_end_dev(dev, phy_id); break; case FANOUT_DEV: @@ -1688,8 +1684,8 @@ static int sas_get_phy_change_count(struct domain_device *dev, return res; } -int sas_get_phy_attached_sas_addr(struct domain_device *dev, int phy_id, - u8 *attached_sas_addr) +static int sas_get_phy_attached_dev(struct domain_device *dev, int phy_id, + u8 *sas_addr, enum sas_dev_type *type) { int res; struct smp_resp *disc_resp; @@ -1701,10 +1697,11 @@ int sas_get_phy_attached_sas_addr(struct domain_device *dev, int phy_id, dr = &disc_resp->disc; res = sas_get_phy_discover(dev, phy_id, disc_resp); - if (!res) { - memcpy(attached_sas_addr,disc_resp->disc.attached_sas_addr,8); - if (dr->attached_dev_type == 0) - memset(attached_sas_addr, 0, 8); + if (res == 0) { + memcpy(sas_addr, disc_resp->disc.attached_sas_addr, 8); + *type = to_dev_type(dr); + if (*type == 0) + memset(sas_addr, 0, 8); } kfree(disc_resp); return res; @@ -1953,39 +1950,62 @@ out: return res; } +static bool dev_type_flutter(enum sas_dev_type new, enum sas_dev_type old) +{ + if (old == new) + return true; + + /* treat device directed resets as flutter, if we went + * SAS_END_DEV to SATA_PENDING the link needs recovery + */ + if ((old == SATA_PENDING && new == SAS_END_DEV) || + (old == SAS_END_DEV && new == SATA_PENDING)) + return true; + + return false; +} + static int sas_rediscover_dev(struct domain_device *dev, int phy_id, bool last) { struct expander_device *ex = &dev->ex_dev; struct ex_phy *phy = &ex->ex_phy[phy_id]; - u8 attached_sas_addr[8]; + enum sas_dev_type type = NO_DEVICE; + u8 sas_addr[8]; int res; - res = sas_get_phy_attached_sas_addr(dev, phy_id, attached_sas_addr); + res = sas_get_phy_attached_dev(dev, phy_id, sas_addr, &type); switch (res) { case SMP_RESP_NO_PHY: phy->phy_state = PHY_NOT_PRESENT; sas_unregister_devs_sas_addr(dev, phy_id, last); - goto out; break; + return res; case SMP_RESP_PHY_VACANT: phy->phy_state = PHY_VACANT; sas_unregister_devs_sas_addr(dev, phy_id, last); - goto out; break; + return res; case SMP_RESP_FUNC_ACC: break; } - if (SAS_ADDR(attached_sas_addr) == 0) { + if (SAS_ADDR(sas_addr) == 0) { phy->phy_state = PHY_EMPTY; sas_unregister_devs_sas_addr(dev, phy_id, last); - } else if (SAS_ADDR(attached_sas_addr) == - SAS_ADDR(phy->attached_sas_addr)) { - SAS_DPRINTK("ex %016llx phy 0x%x broadcast flutter\n", - SAS_ADDR(dev->sas_addr), phy_id); + return res; + } else if (SAS_ADDR(sas_addr) == SAS_ADDR(phy->attached_sas_addr) && + dev_type_flutter(type, phy->attached_dev_type)) { + struct domain_device *ata_dev = sas_ex_to_ata(dev, phy_id); + char *action = ""; + sas_ex_phy_discover(dev, phy_id); - } else - res = sas_discover_new(dev, phy_id); -out: - return res; + + if (ata_dev && phy->attached_dev_type == SATA_PENDING) + action = ", needs recovery"; + SAS_DPRINTK("ex %016llx phy 0x%x broadcast flutter%s\n", + SAS_ADDR(dev->sas_addr), phy_id, action); + return res; + } + + return sas_discover_new(dev, phy_id); } /** diff --git a/drivers/scsi/libsas/sas_internal.h b/drivers/scsi/libsas/sas_internal.h index 7818c4673c3a..e028d7a44202 100644 --- a/drivers/scsi/libsas/sas_internal.h +++ b/drivers/scsi/libsas/sas_internal.h @@ -91,8 +91,9 @@ int sas_smp_get_phy_events(struct sas_phy *phy); void sas_device_set_phy(struct domain_device *dev, struct sas_port *port); struct domain_device *sas_find_dev_by_rphy(struct sas_rphy *rphy); struct domain_device *sas_ex_to_ata(struct domain_device *ex_dev, int phy_id); -int sas_get_phy_attached_sas_addr(struct domain_device *dev, int phy_id, - u8 *attached_sas_addr); +int sas_ex_phy_discover(struct domain_device *dev, int single); +int sas_get_report_phy_sata(struct domain_device *dev, int phy_id, + struct smp_resp *rps_resp); int sas_try_ata_reset(struct asd_sas_phy *phy); void sas_hae_reset(struct work_struct *work); @@ -122,6 +123,7 @@ static inline void sas_fill_in_rphy(struct domain_device *dev, case SATA_DEV: /* FIXME: need sata device type */ case SAS_END_DEV: + case SATA_PENDING: rphy->identify.device_type = SAS_END_DEVICE; break; case EDGE_DEV: diff --git a/include/scsi/sas.h b/include/scsi/sas.h index 3673d685e6ad..a577a833603d 100644 --- a/include/scsi/sas.h +++ b/include/scsi/sas.h @@ -89,8 +89,7 @@ enum sas_oob_mode { SAS_OOB_MODE }; -/* See sas_discover.c if you plan on changing these. - */ +/* See sas_discover.c if you plan on changing these */ enum sas_dev_type { NO_DEVICE = 0, /* protocol */ SAS_END_DEV = 1, /* protocol */ @@ -100,6 +99,7 @@ enum sas_dev_type { SATA_DEV = 5, SATA_PM = 7, SATA_PM_PORT= 8, + SATA_PENDING = 9, }; enum sas_protocol { diff --git a/include/scsi/sas_ata.h b/include/scsi/sas_ata.h index cb724fd010f6..0ca2f8a6bc60 100644 --- a/include/scsi/sas_ata.h +++ b/include/scsi/sas_ata.h @@ -33,9 +33,10 @@ static inline int dev_is_sata(struct domain_device *dev) { return dev->dev_type == SATA_DEV || dev->dev_type == SATA_PM || - dev->dev_type == SATA_PM_PORT; + dev->dev_type == SATA_PM_PORT || dev->dev_type == SATA_PENDING; } +int sas_get_ata_info(struct domain_device *dev, struct ex_phy *phy); int sas_ata_init_host_and_port(struct domain_device *found_dev, struct scsi_target *starget); @@ -82,6 +83,11 @@ static inline void sas_ata_schedule_reset(struct domain_device *dev) static inline void sas_ata_wait_eh(struct domain_device *dev) { } + +static inline int sas_get_ata_info(struct domain_device *dev, struct ex_phy *phy) +{ + return 0; +} #endif #endif /* _SAS_ATA_H_ */ -- cgit v1.2.3 From 92625f9bff3853951cc75f5bc084ee67c1317d2f Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 18 Jan 2012 20:14:01 -0800 Subject: [SCSI] libsas: restore scan order ata devices are always scanned after ssp. Prior to the ata error handling reworks libsas would tend to scan devices in ascending expander phy order. Restore this ordering by deferring ssp discovery to a DISCE_PROBE event, and keep the probe order consistent with the discovery order, not the placement of sata devices. Signed-off-by: Dan Williams Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_ata.c | 29 ------------------------- drivers/scsi/libsas/sas_discover.c | 44 +++++++++++++++++++++++++++----------- drivers/scsi/libsas/sas_expander.c | 4 +--- include/scsi/sas_ata.h | 5 ----- 4 files changed, 32 insertions(+), 50 deletions(-) (limited to 'include/scsi') diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index 25008a42412f..a9ec1643ee93 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -683,35 +683,6 @@ static void sas_get_ata_command_set(struct domain_device *dev) dev->sata_dev.command_set = ATAPI_COMMAND_SET; } -void sas_probe_sata(struct work_struct *work) -{ - struct domain_device *dev, *n; - struct sas_discovery_event *ev = - container_of(work, struct sas_discovery_event, work); - struct asd_sas_port *port = ev->port; - - clear_bit(DISCE_PROBE, &port->disc.pending); - - list_for_each_entry_safe(dev, n, &port->disco_list, disco_list_node) { - int err; - - spin_lock_irq(&port->dev_list_lock); - list_add_tail(&dev->dev_list_node, &port->dev_list); - spin_unlock_irq(&port->dev_list_lock); - - err = sas_rphy_add(dev->rphy); - - if (err) { - SAS_DPRINTK("%s: for %s device %16llx returned %d\n", - __func__, dev->parent ? "exp-attached" : - "direct-attached", - SAS_ADDR(dev->sas_addr), err); - sas_unregister_dev(port, dev); - } else - list_del_init(&dev->disco_list_node); - } -} - /** * sas_discover_sata -- discover an STP/SATA domain device * @dev: pointer to struct domain_device of interest diff --git a/drivers/scsi/libsas/sas_discover.c b/drivers/scsi/libsas/sas_discover.c index 8bcfcaa7b2e1..18fa364aa00f 100644 --- a/drivers/scsi/libsas/sas_discover.c +++ b/drivers/scsi/libsas/sas_discover.c @@ -152,7 +152,7 @@ static int sas_get_port_device(struct asd_sas_port *port) dev->rphy = rphy; - if (dev_is_sata(dev)) + if (dev_is_sata(dev) || dev->dev_type == SAS_END_DEV) list_add_tail(&dev->disco_list_node, &port->disco_list); else { spin_lock_irq(&port->dev_list_lock); @@ -198,8 +198,34 @@ void sas_notify_lldd_dev_gone(struct domain_device *dev) } } -/* ---------- Common/dispatchers ---------- */ +static void sas_probe_devices(struct work_struct *work) +{ + struct domain_device *dev, *n; + struct sas_discovery_event *ev = + container_of(work, struct sas_discovery_event, work); + struct asd_sas_port *port = ev->port; + clear_bit(DISCE_PROBE, &port->disc.pending); + + list_for_each_entry_safe(dev, n, &port->disco_list, disco_list_node) { + int err; + + spin_lock_irq(&port->dev_list_lock); + list_add_tail(&dev->dev_list_node, &port->dev_list); + spin_unlock_irq(&port->dev_list_lock); + + err = sas_rphy_add(dev->rphy); + + if (err) { + SAS_DPRINTK("%s: for %s device %16llx returned %d\n", + __func__, dev->parent ? "exp-attached" : + "direct-attached", + SAS_ADDR(dev->sas_addr), err); + sas_unregister_dev(port, dev); + } else + list_del_init(&dev->disco_list_node); + } +} /** * sas_discover_end_dev -- discover an end device (SSP, etc) @@ -213,18 +239,10 @@ int sas_discover_end_dev(struct domain_device *dev) res = sas_notify_lldd_dev_found(dev); if (res) - goto out_err2; - - res = sas_rphy_add(dev->rphy); - if (res) - goto out_err; + return res; + sas_discover_event(dev->port, DISCE_PROBE); return 0; - -out_err: - sas_notify_lldd_dev_gone(dev); -out_err2: - return res; } /* ---------- Device registration and unregistration ---------- */ @@ -491,7 +509,7 @@ void sas_init_disc(struct sas_discovery *disc, struct asd_sas_port *port) static const work_func_t sas_event_fns[DISC_NUM_EVENTS] = { [DISCE_DISCOVER_DOMAIN] = sas_discover_domain, [DISCE_REVALIDATE_DOMAIN] = sas_revalidate_domain, - [DISCE_PROBE] = sas_probe_sata, + [DISCE_PROBE] = sas_probe_devices, [DISCE_DESTRUCT] = sas_destruct_devices, }; diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index d63f0fbcd103..14e3244c1b20 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c @@ -806,9 +806,7 @@ static struct domain_device *sas_ex_discover_end_dev( child->rphy = rphy; sas_fill_in_rphy(child, rphy); - spin_lock_irq(&parent->port->dev_list_lock); - list_add_tail(&child->dev_list_node, &parent->port->dev_list); - spin_unlock_irq(&parent->port->dev_list_lock); + list_add_tail(&child->disco_list_node, &parent->port->disco_list); res = sas_discover_end_dev(child); if (res) { diff --git a/include/scsi/sas_ata.h b/include/scsi/sas_ata.h index 0ca2f8a6bc60..1556eff4cc44 100644 --- a/include/scsi/sas_ata.h +++ b/include/scsi/sas_ata.h @@ -44,7 +44,6 @@ void sas_ata_task_abort(struct sas_task *task); void sas_ata_strategy_handler(struct Scsi_Host *shost); void sas_ata_eh(struct Scsi_Host *shost, struct list_head *work_q, struct list_head *done_q); -void sas_probe_sata(struct work_struct *work); void sas_ata_schedule_reset(struct domain_device *dev); void sas_ata_wait_eh(struct domain_device *dev); #else @@ -72,10 +71,6 @@ static inline void sas_ata_eh(struct Scsi_Host *shost, struct list_head *work_q, { } -static inline void sas_probe_sata(struct work_struct *work) -{ -} - static inline void sas_ata_schedule_reset(struct domain_device *dev) { } -- cgit v1.2.3 From 9508a66f898d46e726a318469312b45e0b1d078b Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 18 Jan 2012 20:47:01 -0800 Subject: [SCSI] libsas: async ata scanning libsas ata error handling is already async but this does not help the scan case. Move initial link recovery out from under host->scan_mutex, and delay synchronization with eh until after all port probe/recovery work has been queued. Device ordering is maintained with scan order by still calling sas_rphy_add() in order of domain discovery. Since we now scan the domain list when invoking libata-eh we need to be careful to check for fully initialized ata ports. Acked-by: Jack Wang Acked-by: Jeff Garzik Signed-off-by: Dan Williams Signed-off-by: James Bottomley --- drivers/ata/libata-core.c | 34 +++++++++-------- drivers/ata/libata-scsi.c | 13 +++++++ drivers/ata/libata.h | 1 + drivers/scsi/aic94xx/aic94xx_init.c | 1 - drivers/scsi/isci/init.c | 1 - drivers/scsi/libsas/sas_ata.c | 74 +++++++++++++++++++++++++++++++------ drivers/scsi/libsas/sas_discover.c | 22 +++++------ drivers/scsi/libsas/sas_internal.h | 9 +++++ drivers/scsi/libsas/sas_scsi_host.c | 18 --------- drivers/scsi/mvsas/mv_init.c | 1 - drivers/scsi/pm8001/pm8001_init.c | 1 - include/linux/libata.h | 1 + include/scsi/libsas.h | 1 - include/scsi/sas_ata.h | 12 +++--- 14 files changed, 123 insertions(+), 66 deletions(-) (limited to 'include/scsi') diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index c06e0ec11556..e0bda9ff89cd 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -5936,29 +5936,31 @@ void ata_host_init(struct ata_host *host, struct device *dev, host->ops = ops; } -int ata_port_probe(struct ata_port *ap) +void __ata_port_probe(struct ata_port *ap) { - int rc = 0; + struct ata_eh_info *ehi = &ap->link.eh_info; + unsigned long flags; - /* probe */ - if (ap->ops->error_handler) { - struct ata_eh_info *ehi = &ap->link.eh_info; - unsigned long flags; + /* kick EH for boot probing */ + spin_lock_irqsave(ap->lock, flags); - /* kick EH for boot probing */ - spin_lock_irqsave(ap->lock, flags); + ehi->probe_mask |= ATA_ALL_DEVICES; + ehi->action |= ATA_EH_RESET; + ehi->flags |= ATA_EHI_NO_AUTOPSY | ATA_EHI_QUIET; - ehi->probe_mask |= ATA_ALL_DEVICES; - ehi->action |= ATA_EH_RESET; - ehi->flags |= ATA_EHI_NO_AUTOPSY | ATA_EHI_QUIET; + ap->pflags &= ~ATA_PFLAG_INITIALIZING; + ap->pflags |= ATA_PFLAG_LOADING; + ata_port_schedule_eh(ap); - ap->pflags &= ~ATA_PFLAG_INITIALIZING; - ap->pflags |= ATA_PFLAG_LOADING; - ata_port_schedule_eh(ap); + spin_unlock_irqrestore(ap->lock, flags); +} - spin_unlock_irqrestore(ap->lock, flags); +int ata_port_probe(struct ata_port *ap) +{ + int rc = 0; - /* wait for EH to finish */ + if (ap->ops->error_handler) { + __ata_port_probe(ap); ata_port_wait_eh(ap); } else { DPRINTK("ata%u: bus probe begin\n", ap->print_id); diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 508a60bfe5c1..1ee00c8b5b04 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -3838,6 +3838,19 @@ void ata_sas_port_stop(struct ata_port *ap) } EXPORT_SYMBOL_GPL(ata_sas_port_stop); +int ata_sas_async_port_init(struct ata_port *ap) +{ + int rc = ap->ops->port_start(ap); + + if (!rc) { + ap->print_id = ata_print_id++; + __ata_port_probe(ap); + } + + return rc; +} +EXPORT_SYMBOL_GPL(ata_sas_async_port_init); + /** * ata_sas_port_init - Initialize a SATA device * @ap: SATA port to initialize diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h index 1fab235ee516..2e26fcaf635b 100644 --- a/drivers/ata/libata.h +++ b/drivers/ata/libata.h @@ -105,6 +105,7 @@ extern int ata_cmd_ioctl(struct scsi_device *scsidev, void __user *arg); extern struct ata_port *ata_port_alloc(struct ata_host *host); extern const char *sata_spd_string(unsigned int spd); extern int ata_port_probe(struct ata_port *ap); +extern void __ata_port_probe(struct ata_port *ap); /* libata-acpi.c */ #ifdef CONFIG_ATA_ACPI diff --git a/drivers/scsi/aic94xx/aic94xx_init.c b/drivers/scsi/aic94xx/aic94xx_init.c index eea988a04f92..ff80552ead84 100644 --- a/drivers/scsi/aic94xx/aic94xx_init.c +++ b/drivers/scsi/aic94xx/aic94xx_init.c @@ -81,7 +81,6 @@ static struct scsi_host_template aic94xx_sht = { .use_clustering = ENABLE_CLUSTERING, .eh_device_reset_handler = sas_eh_device_reset_handler, .eh_bus_reset_handler = sas_eh_bus_reset_handler, - .slave_alloc = sas_slave_alloc, .target_destroy = sas_target_destroy, .ioctl = sas_ioctl, }; diff --git a/drivers/scsi/isci/init.c b/drivers/scsi/isci/init.c index c3fe39bcacd5..c9af456e7dfe 100644 --- a/drivers/scsi/isci/init.c +++ b/drivers/scsi/isci/init.c @@ -165,7 +165,6 @@ static struct scsi_host_template isci_sht = { .sg_tablesize = SG_ALL, .max_sectors = SCSI_DEFAULT_MAX_SECTORS, .use_clustering = ENABLE_CLUSTERING, - .slave_alloc = sas_slave_alloc, .target_destroy = sas_target_destroy, .ioctl = sas_ioctl, .shost_attrs = isci_host_attrs, diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index a9ec1643ee93..eb8b77c86169 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -585,11 +585,10 @@ static struct ata_port_info sata_port_info = { .port_ops = &sas_sata_ops }; -int sas_ata_init_host_and_port(struct domain_device *found_dev, - struct scsi_target *starget) +int sas_ata_init_host_and_port(struct domain_device *found_dev) { - struct Scsi_Host *shost = dev_to_shost(&starget->dev); - struct sas_ha_struct *ha = SHOST_TO_SAS_HA(shost); + struct sas_ha_struct *ha = found_dev->port->ha; + struct Scsi_Host *shost = ha->core.shost; struct ata_port *ap; ata_host_init(&found_dev->sata_dev.ata_host, @@ -607,6 +606,8 @@ int sas_ata_init_host_and_port(struct domain_device *found_dev, ap->private_data = found_dev; ap->cbl = ATA_CBL_SATA; ap->scsi_host = shost; + /* publish initialized ata port */ + smp_wmb(); found_dev->sata_dev.ap = ap; return 0; @@ -683,6 +684,38 @@ static void sas_get_ata_command_set(struct domain_device *dev) dev->sata_dev.command_set = ATAPI_COMMAND_SET; } +void sas_probe_sata(struct asd_sas_port *port) +{ + struct domain_device *dev, *n; + int err; + + mutex_lock(&port->ha->disco_mutex); + list_for_each_entry_safe(dev, n, &port->disco_list, disco_list_node) { + if (!dev_is_sata(dev)) + continue; + + err = sas_ata_init_host_and_port(dev); + if (err) + sas_fail_probe(dev, __func__, err); + else + ata_sas_async_port_init(dev->sata_dev.ap); + } + mutex_unlock(&port->ha->disco_mutex); + + list_for_each_entry_safe(dev, n, &port->disco_list, disco_list_node) { + if (!dev_is_sata(dev)) + continue; + + sas_ata_wait_eh(dev); + + /* if libata could not bring the link up, don't surface + * the device + */ + if (ata_dev_disabled(sas_to_ata_dev(dev))) + sas_fail_probe(dev, __func__, -ENODEV); + } +} + /** * sas_discover_sata -- discover an STP/SATA domain device * @dev: pointer to struct domain_device of interest @@ -724,11 +757,23 @@ static void async_sas_ata_eh(void *data, async_cookie_t cookie) sas_put_device(dev); } +static bool sas_ata_dev_eh_valid(struct domain_device *dev) +{ + struct ata_port *ap; + + if (!dev_is_sata(dev)) + return false; + ap = dev->sata_dev.ap; + /* consume fully initialized ata ports */ + smp_rmb(); + return !!ap; +} + void sas_ata_strategy_handler(struct Scsi_Host *shost) { - struct scsi_device *sdev; struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost); LIST_HEAD(async); + int i; /* it's ok to defer revalidation events during ata eh, these * disks are in one of three states: @@ -740,14 +785,21 @@ void sas_ata_strategy_handler(struct Scsi_Host *shost) */ sas_disable_revalidation(sas_ha); - shost_for_each_device(sdev, shost) { - struct domain_device *ddev = sdev_to_domain_dev(sdev); - - if (!dev_is_sata(ddev)) - continue; + spin_lock_irq(&sas_ha->phy_port_lock); + for (i = 0; i < sas_ha->num_phys; i++) { + struct asd_sas_port *port = sas_ha->sas_port[i]; + struct domain_device *dev; - async_schedule_domain(async_sas_ata_eh, ddev, &async); + spin_lock(&port->dev_list_lock); + list_for_each_entry(dev, &port->dev_list, dev_list_node) { + if (!sas_ata_dev_eh_valid(dev)) + continue; + async_schedule_domain(async_sas_ata_eh, dev, &async); + } + spin_unlock(&port->dev_list_lock); } + spin_unlock_irq(&sas_ha->phy_port_lock); + async_synchronize_full_domain(&async); sas_enable_revalidation(sas_ha); diff --git a/drivers/scsi/libsas/sas_discover.c b/drivers/scsi/libsas/sas_discover.c index 18fa364aa00f..0d58a8beaa3d 100644 --- a/drivers/scsi/libsas/sas_discover.c +++ b/drivers/scsi/libsas/sas_discover.c @@ -207,22 +207,22 @@ static void sas_probe_devices(struct work_struct *work) clear_bit(DISCE_PROBE, &port->disc.pending); - list_for_each_entry_safe(dev, n, &port->disco_list, disco_list_node) { - int err; - + /* devices must be domain members before link recovery and probe */ + list_for_each_entry(dev, &port->disco_list, disco_list_node) { spin_lock_irq(&port->dev_list_lock); list_add_tail(&dev->dev_list_node, &port->dev_list); spin_unlock_irq(&port->dev_list_lock); + } - err = sas_rphy_add(dev->rphy); + sas_probe_sata(port); - if (err) { - SAS_DPRINTK("%s: for %s device %16llx returned %d\n", - __func__, dev->parent ? "exp-attached" : - "direct-attached", - SAS_ADDR(dev->sas_addr), err); - sas_unregister_dev(port, dev); - } else + list_for_each_entry_safe(dev, n, &port->disco_list, disco_list_node) { + int err; + + err = sas_rphy_add(dev->rphy); + if (err) + sas_fail_probe(dev, __func__, err); + else list_del_init(&dev->disco_list_node); } } diff --git a/drivers/scsi/libsas/sas_internal.h b/drivers/scsi/libsas/sas_internal.h index e028d7a44202..d0d9bf10f79c 100644 --- a/drivers/scsi/libsas/sas_internal.h +++ b/drivers/scsi/libsas/sas_internal.h @@ -113,6 +113,15 @@ static inline int sas_smp_host_handler(struct Scsi_Host *shost, } #endif +static inline void sas_fail_probe(struct domain_device *dev, const char *func, int err) +{ + SAS_DPRINTK("%s: for %s device %16llx returned %d\n", + func, dev->parent ? "exp-attached" : + "direct-attached", + SAS_ADDR(dev->sas_addr), err); + sas_unregister_dev(dev->port, dev); +} + static inline void sas_fill_in_rphy(struct domain_device *dev, struct sas_rphy *rphy) { diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c index e58ca50517d5..3701ff7e7267 100644 --- a/drivers/scsi/libsas/sas_scsi_host.c +++ b/drivers/scsi/libsas/sas_scsi_host.c @@ -762,17 +762,10 @@ int sas_target_alloc(struct scsi_target *starget) { struct sas_rphy *rphy = dev_to_rphy(starget->dev.parent); struct domain_device *found_dev = sas_find_dev_by_rphy(rphy); - int res; if (!found_dev) return -ENODEV; - if (dev_is_sata(found_dev)) { - res = sas_ata_init_host_and_port(found_dev, starget); - if (res) - return res; - } - kref_get(&found_dev->kref); starget->hostdata = found_dev; return 0; @@ -1012,16 +1005,6 @@ void sas_task_abort(struct sas_task *task) } } -int sas_slave_alloc(struct scsi_device *scsi_dev) -{ - struct domain_device *dev = sdev_to_domain_dev(scsi_dev); - - if (dev_is_sata(dev)) - return ata_sas_port_init(dev->sata_dev.ap); - - return 0; -} - void sas_target_destroy(struct scsi_target *starget) { struct domain_device *found_dev = starget->hostdata; @@ -1082,6 +1065,5 @@ EXPORT_SYMBOL_GPL(sas_task_abort); EXPORT_SYMBOL_GPL(sas_phy_reset); EXPORT_SYMBOL_GPL(sas_eh_device_reset_handler); EXPORT_SYMBOL_GPL(sas_eh_bus_reset_handler); -EXPORT_SYMBOL_GPL(sas_slave_alloc); EXPORT_SYMBOL_GPL(sas_target_destroy); EXPORT_SYMBOL_GPL(sas_ioctl); diff --git a/drivers/scsi/mvsas/mv_init.c b/drivers/scsi/mvsas/mv_init.c index d45878b31254..cc59dff3810b 100644 --- a/drivers/scsi/mvsas/mv_init.c +++ b/drivers/scsi/mvsas/mv_init.c @@ -73,7 +73,6 @@ static struct scsi_host_template mvs_sht = { .use_clustering = ENABLE_CLUSTERING, .eh_device_reset_handler = sas_eh_device_reset_handler, .eh_bus_reset_handler = sas_eh_bus_reset_handler, - .slave_alloc = sas_slave_alloc, .target_destroy = sas_target_destroy, .ioctl = sas_ioctl, .shost_attrs = mvst_host_attrs, diff --git a/drivers/scsi/pm8001/pm8001_init.c b/drivers/scsi/pm8001/pm8001_init.c index bd165ea61919..36efaa7c3a54 100644 --- a/drivers/scsi/pm8001/pm8001_init.c +++ b/drivers/scsi/pm8001/pm8001_init.c @@ -75,7 +75,6 @@ static struct scsi_host_template pm8001_sht = { .use_clustering = ENABLE_CLUSTERING, .eh_device_reset_handler = sas_eh_device_reset_handler, .eh_bus_reset_handler = sas_eh_bus_reset_handler, - .slave_alloc = sas_slave_alloc, .target_destroy = sas_target_destroy, .ioctl = sas_ioctl, .shost_attrs = pm8001_host_attrs, diff --git a/include/linux/libata.h b/include/linux/libata.h index aa4270477563..42378d637ffb 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -996,6 +996,7 @@ extern int ata_sas_scsi_ioctl(struct ata_port *ap, struct scsi_device *dev, extern void ata_sas_port_destroy(struct ata_port *); extern struct ata_port *ata_sas_port_alloc(struct ata_host *, struct ata_port_info *, struct Scsi_Host *); +extern int ata_sas_async_port_init(struct ata_port *); extern int ata_sas_port_init(struct ata_port *); extern int ata_sas_port_start(struct ata_port *ap); extern void ata_sas_port_stop(struct ata_port *ap); diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h index 4a42be34fad0..20153d58e4e6 100644 --- a/include/scsi/libsas.h +++ b/include/scsi/libsas.h @@ -646,7 +646,6 @@ int sas_phy_reset(struct sas_phy *phy, int hard_reset); int sas_queue_up(struct sas_task *task); extern int sas_queuecommand(struct Scsi_Host * ,struct scsi_cmnd *); extern int sas_target_alloc(struct scsi_target *); -extern int sas_slave_alloc(struct scsi_device *); extern int sas_slave_configure(struct scsi_device *); extern int sas_change_queue_depth(struct scsi_device *, int new_depth, int reason); diff --git a/include/scsi/sas_ata.h b/include/scsi/sas_ata.h index 1556eff4cc44..cdccd2eb7b6c 100644 --- a/include/scsi/sas_ata.h +++ b/include/scsi/sas_ata.h @@ -37,15 +37,14 @@ static inline int dev_is_sata(struct domain_device *dev) } int sas_get_ata_info(struct domain_device *dev, struct ex_phy *phy); -int sas_ata_init_host_and_port(struct domain_device *found_dev, - struct scsi_target *starget); - +int sas_ata_init_host_and_port(struct domain_device *found_dev); void sas_ata_task_abort(struct sas_task *task); void sas_ata_strategy_handler(struct Scsi_Host *shost); void sas_ata_eh(struct Scsi_Host *shost, struct list_head *work_q, struct list_head *done_q); void sas_ata_schedule_reset(struct domain_device *dev); void sas_ata_wait_eh(struct domain_device *dev); +void sas_probe_sata(struct asd_sas_port *port); #else @@ -53,8 +52,7 @@ static inline int dev_is_sata(struct domain_device *dev) { return 0; } -static inline int sas_ata_init_host_and_port(struct domain_device *found_dev, - struct scsi_target *starget) +static inline int sas_ata_init_host_and_port(struct domain_device *found_dev) { return 0; } @@ -79,6 +77,10 @@ static inline void sas_ata_wait_eh(struct domain_device *dev) { } +static inline void sas_probe_sata(struct asd_sas_port *port) +{ +} + static inline int sas_get_ata_info(struct domain_device *dev, struct ex_phy *phy) { return 0; -- cgit v1.2.3 From 9a10b33caf78f897356ac006c455e6060a40af15 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Fri, 20 Jan 2012 15:26:03 -0800 Subject: [SCSI] libsas: revert ata srst libata issues follow up srsts when the controller has a hard time recording the signature-fis after a reset, or if the link supports port multipliers. libsas does not support port multipliers and no current libsas lldds appear to need help retrieving the signature fis. Revert it for now to remove confusion. Signed-off-by: Dan Williams Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_ata.c | 38 -------------------------------------- include/scsi/libsas.h | 1 - 2 files changed, 39 deletions(-) (limited to 'include/scsi') diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index eb8b77c86169..08d2103a45b7 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -443,43 +443,6 @@ static int sas_ata_hard_reset(struct ata_link *link, unsigned int *class, return ret; } -static int sas_ata_soft_reset(struct ata_link *link, unsigned int *class, - unsigned long deadline) -{ - struct ata_port *ap = link->ap; - struct domain_device *dev = ap->private_data; - struct sas_internal *i = dev_to_sas_internal(dev); - int res = TMF_RESP_FUNC_FAILED; - int ret = 0; - - if (i->dft->lldd_ata_soft_reset) - res = i->dft->lldd_ata_soft_reset(dev); - - if (res != TMF_RESP_FUNC_COMPLETE) { - SAS_DPRINTK("%s: Unable to soft reset\n", __func__); - ret = -EAGAIN; - } - - switch (dev->sata_dev.command_set) { - case ATA_COMMAND_SET: - SAS_DPRINTK("%s: Found ATA device.\n", __func__); - *class = ATA_DEV_ATA; - break; - case ATAPI_COMMAND_SET: - SAS_DPRINTK("%s: Found ATAPI device.\n", __func__); - *class = ATA_DEV_ATAPI; - break; - default: - SAS_DPRINTK("%s: Unknown SATA command set: %d.\n", - __func__, dev->sata_dev.command_set); - *class = ATA_DEV_UNKNOWN; - break; - } - - ap->cbl = ATA_CBL_SATA; - return ret; -} - /* * notify the lldd to forget the sas_task for this internal ata command * that bypasses scsi-eh @@ -563,7 +526,6 @@ static void sas_ata_set_dmamode(struct ata_port *ap, struct ata_device *ata_dev) static struct ata_port_operations sas_sata_ops = { .prereset = ata_std_prereset, - .softreset = sas_ata_soft_reset, .hardreset = sas_ata_hard_reset, .postreset = ata_std_postreset, .error_handler = ata_std_error_handler, diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h index 20153d58e4e6..5f5ed1b8b41b 100644 --- a/include/scsi/libsas.h +++ b/include/scsi/libsas.h @@ -619,7 +619,6 @@ struct sas_domain_function_template { int (*lldd_clear_aca)(struct domain_device *, u8 *lun); int (*lldd_clear_task_set)(struct domain_device *, u8 *lun); int (*lldd_I_T_nexus_reset)(struct domain_device *); - int (*lldd_ata_soft_reset)(struct domain_device *); int (*lldd_ata_check_ready)(struct domain_device *); void (*lldd_ata_set_dmamode)(struct domain_device *); int (*lldd_lu_reset)(struct domain_device *, u8 *lun); -- cgit v1.2.3 From 6260a5d221225f4e6befd98c6001325a3007a8c4 Mon Sep 17 00:00:00 2001 From: Nilesh Javali Date: Mon, 27 Feb 2012 03:08:51 -0800 Subject: [SCSI] iscsi_transport: Add support to display CHAP list and delete CHAP entry For offload iSCSI like qla4xxx CHAP entries are stored in FLASH. This patch adds support to list CHAP entries stored in FLASH and delete specified CHAP entry from FLASH using iscsi tools. Signed-off-by: Nilesh Javali Signed-off-by: Vikas Chaudhary Reviewed-by: Mike Christie Signed-off-by: James Bottomley --- drivers/scsi/scsi_transport_iscsi.c | 101 +++++++++++++++++++++++++++++++++++- include/scsi/iscsi_if.h | 29 +++++++++++ include/scsi/scsi_transport_iscsi.h | 4 ++ 3 files changed, 133 insertions(+), 1 deletion(-) (limited to 'include/scsi') diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c index a20f1813cb51..7bf0dec46271 100644 --- a/drivers/scsi/scsi_transport_iscsi.c +++ b/drivers/scsi/scsi_transport_iscsi.c @@ -727,10 +727,11 @@ static void iscsi_session_release(struct device *dev) kfree(session); } -static int iscsi_is_session_dev(const struct device *dev) +int iscsi_is_session_dev(const struct device *dev) { return dev->release == iscsi_session_release; } +EXPORT_SYMBOL_GPL(iscsi_is_session_dev); static int iscsi_iter_session_fn(struct device *dev, void *data) { @@ -2001,6 +2002,96 @@ iscsi_send_ping(struct iscsi_transport *transport, struct iscsi_uevent *ev) return err; } +static int +iscsi_get_chap(struct iscsi_transport *transport, struct nlmsghdr *nlh) +{ + struct iscsi_uevent *ev = NLMSG_DATA(nlh); + struct Scsi_Host *shost = NULL; + struct iscsi_chap_rec *chap_rec; + struct iscsi_internal *priv; + struct sk_buff *skbchap; + struct nlmsghdr *nlhchap; + struct iscsi_uevent *evchap; + uint32_t chap_buf_size; + int len, err = 0; + char *buf; + + if (!transport->get_chap) + return -EINVAL; + + priv = iscsi_if_transport_lookup(transport); + if (!priv) + return -EINVAL; + + chap_buf_size = (ev->u.get_chap.num_entries * sizeof(*chap_rec)); + len = NLMSG_SPACE(sizeof(*ev) + chap_buf_size); + + shost = scsi_host_lookup(ev->u.get_chap.host_no); + if (!shost) { + printk(KERN_ERR "%s: failed. Cound not find host no %u\n", + __func__, ev->u.get_chap.host_no); + return -ENODEV; + } + + do { + int actual_size; + + skbchap = alloc_skb(len, GFP_KERNEL); + if (!skbchap) { + printk(KERN_ERR "can not deliver chap: OOM\n"); + err = -ENOMEM; + goto exit_get_chap; + } + + nlhchap = __nlmsg_put(skbchap, 0, 0, 0, + (len - sizeof(*nlhchap)), 0); + evchap = NLMSG_DATA(nlhchap); + memset(evchap, 0, sizeof(*evchap)); + evchap->transport_handle = iscsi_handle(transport); + evchap->type = nlh->nlmsg_type; + evchap->u.get_chap.host_no = ev->u.get_chap.host_no; + evchap->u.get_chap.chap_tbl_idx = ev->u.get_chap.chap_tbl_idx; + evchap->u.get_chap.num_entries = ev->u.get_chap.num_entries; + buf = (char *) ((char *)evchap + sizeof(*evchap)); + memset(buf, 0, chap_buf_size); + + err = transport->get_chap(shost, ev->u.get_chap.chap_tbl_idx, + &evchap->u.get_chap.num_entries, buf); + + actual_size = NLMSG_SPACE(sizeof(*ev) + chap_buf_size); + skb_trim(skbchap, NLMSG_ALIGN(actual_size)); + nlhchap->nlmsg_len = actual_size; + + err = iscsi_multicast_skb(skbchap, ISCSI_NL_GRP_ISCSID, + GFP_KERNEL); + } while (err < 0 && err != -ECONNREFUSED); + +exit_get_chap: + scsi_host_put(shost); + return err; +} + +static int iscsi_delete_chap(struct iscsi_transport *transport, + struct iscsi_uevent *ev) +{ + struct Scsi_Host *shost; + int err = 0; + + if (!transport->delete_chap) + return -ENOSYS; + + shost = scsi_host_lookup(ev->u.delete_chap.host_no); + if (!shost) { + printk(KERN_ERR "%s could not find host no %u\n", + __func__, ev->u.delete_chap.host_no); + return -ENODEV; + } + + err = transport->delete_chap(shost, ev->u.delete_chap.chap_tbl_idx); + scsi_host_put(shost); + return err; +} + static int iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, uint32_t *group) { @@ -2149,6 +2240,12 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, uint32_t *group) case ISCSI_UEVENT_PING: err = iscsi_send_ping(transport, ev); break; + case ISCSI_UEVENT_GET_CHAP: + err = iscsi_get_chap(transport, nlh); + break; + case ISCSI_UEVENT_DELETE_CHAP: + err = iscsi_delete_chap(transport, ev); + break; default: err = -ENOSYS; break; @@ -2198,6 +2295,8 @@ iscsi_if_rx(struct sk_buff *skb) */ if (ev->type == ISCSI_UEVENT_GET_STATS && !err) break; + if (ev->type == ISCSI_UEVENT_GET_CHAP && !err) + break; err = iscsi_if_send_reply(group, nlh->nlmsg_seq, nlh->nlmsg_type, 0, 0, ev, sizeof(*ev)); } while (err < 0 && err != -ECONNREFUSED && err != -ESRCH); diff --git a/include/scsi/iscsi_if.h b/include/scsi/iscsi_if.h index 7ff9678b7e79..228a8af05129 100644 --- a/include/scsi/iscsi_if.h +++ b/include/scsi/iscsi_if.h @@ -61,6 +61,8 @@ enum iscsi_uevent_e { ISCSI_UEVENT_PATH_UPDATE = UEVENT_BASE + 20, ISCSI_UEVENT_SET_IFACE_PARAMS = UEVENT_BASE + 21, ISCSI_UEVENT_PING = UEVENT_BASE + 22, + ISCSI_UEVENT_GET_CHAP = UEVENT_BASE + 23, + ISCSI_UEVENT_DELETE_CHAP = UEVENT_BASE + 24, /* up events */ ISCSI_KEVENT_RECV_PDU = KEVENT_BASE + 1, @@ -196,6 +198,18 @@ struct iscsi_uevent { uint32_t pid; /* unique ping id associated with each ping request */ } iscsi_ping; + struct msg_get_chap { + uint32_t host_no; + uint32_t num_entries; /* number of CHAP entries + * on request, number of + * valid CHAP entries on + * response */ + uint16_t chap_tbl_idx; + } get_chap; + struct msg_delete_chap { + uint32_t host_no; + uint16_t chap_tbl_idx; + } delete_chap; } u; union { /* messages k -> u */ @@ -548,4 +562,19 @@ struct iscsi_stats { __attribute__ ((aligned (sizeof(uint64_t)))); }; +enum chap_type_e { + CHAP_TYPE_OUT, + CHAP_TYPE_IN, +}; + +#define ISCSI_CHAP_AUTH_NAME_MAX_LEN 256 +#define ISCSI_CHAP_AUTH_SECRET_MAX_LEN 256 +struct iscsi_chap_rec { + uint16_t chap_tbl_idx; + enum chap_type_e chap_type; + char username[ISCSI_CHAP_AUTH_NAME_MAX_LEN]; + uint8_t password[ISCSI_CHAP_AUTH_SECRET_MAX_LEN]; + uint8_t password_length; +} __packed; + #endif diff --git a/include/scsi/scsi_transport_iscsi.h b/include/scsi/scsi_transport_iscsi.h index aede513f99bd..53f0b361d668 100644 --- a/include/scsi/scsi_transport_iscsi.h +++ b/include/scsi/scsi_transport_iscsi.h @@ -147,6 +147,9 @@ struct iscsi_transport { int (*send_ping) (struct Scsi_Host *shost, uint32_t iface_num, uint32_t iface_type, uint32_t payload_size, uint32_t pid, struct sockaddr *dst_addr); + int (*get_chap) (struct Scsi_Host *shost, uint16_t chap_tbl_idx, + uint32_t *num_entries, char *buf); + int (*delete_chap) (struct Scsi_Host *shost, uint16_t chap_tbl_idx); }; /* @@ -325,5 +328,6 @@ extern void iscsi_destroy_iface(struct iscsi_iface *iface); extern struct iscsi_iface *iscsi_lookup_iface(int handle); extern char *iscsi_get_port_speed_name(struct Scsi_Host *shost); extern char *iscsi_get_port_state_name(struct Scsi_Host *shost); +extern int iscsi_is_session_dev(const struct device *dev); #endif -- cgit v1.2.3 From 30534952743f73f1de3c6c056400d7249f5c7f75 Mon Sep 17 00:00:00 2001 From: Mike Christie Date: Mon, 27 Feb 2012 03:08:53 -0800 Subject: [SCSI] scsi_transport: Export CHAP index as sysfs attribute Signed-off-by: Mike Christie Signed-off-by: Vikas Chaudhary Reviewed-by: Mike Christie Signed-off-by: James Bottomley --- drivers/scsi/scsi_transport_iscsi.c | 8 ++++++++ include/scsi/iscsi_if.h | 3 +++ 2 files changed, 11 insertions(+) (limited to 'include/scsi') diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c index 7bf0dec46271..fac31730addf 100644 --- a/drivers/scsi/scsi_transport_iscsi.c +++ b/drivers/scsi/scsi_transport_iscsi.c @@ -2475,6 +2475,8 @@ iscsi_session_attr(username, ISCSI_PARAM_USERNAME, 1); iscsi_session_attr(username_in, ISCSI_PARAM_USERNAME_IN, 1); iscsi_session_attr(password, ISCSI_PARAM_PASSWORD, 1); iscsi_session_attr(password_in, ISCSI_PARAM_PASSWORD_IN, 1); +iscsi_session_attr(chap_out_idx, ISCSI_PARAM_CHAP_OUT_IDX, 1); +iscsi_session_attr(chap_in_idx, ISCSI_PARAM_CHAP_IN_IDX, 1); iscsi_session_attr(fast_abort, ISCSI_PARAM_FAST_ABORT, 0); iscsi_session_attr(abort_tmo, ISCSI_PARAM_ABORT_TMO, 0); iscsi_session_attr(lu_reset_tmo, ISCSI_PARAM_LU_RESET_TMO, 0); @@ -2571,6 +2573,8 @@ static struct attribute *iscsi_session_attrs[] = { &dev_attr_priv_sess_recovery_tmo.attr, &dev_attr_priv_sess_state.attr, &dev_attr_priv_sess_creator.attr, + &dev_attr_sess_chap_out_idx.attr, + &dev_attr_sess_chap_in_idx.attr, NULL, }; @@ -2602,6 +2606,10 @@ static umode_t iscsi_session_attr_is_visible(struct kobject *kobj, param = ISCSI_PARAM_TARGET_NAME; else if (attr == &dev_attr_sess_tpgt.attr) param = ISCSI_PARAM_TPGT; + else if (attr == &dev_attr_sess_chap_in_idx.attr) + param = ISCSI_PARAM_CHAP_IN_IDX; + else if (attr == &dev_attr_sess_chap_out_idx.attr) + param = ISCSI_PARAM_CHAP_OUT_IDX; else if (attr == &dev_attr_sess_password.attr) param = ISCSI_PARAM_USERNAME; else if (attr == &dev_attr_sess_password_in.attr) diff --git a/include/scsi/iscsi_if.h b/include/scsi/iscsi_if.h index 228a8af05129..9c23ee8fd2d3 100644 --- a/include/scsi/iscsi_if.h +++ b/include/scsi/iscsi_if.h @@ -450,6 +450,9 @@ enum iscsi_param { ISCSI_PARAM_TGT_RESET_TMO, ISCSI_PARAM_TARGET_ALIAS, + + ISCSI_PARAM_CHAP_IN_IDX, + ISCSI_PARAM_CHAP_OUT_IDX, /* must always be last */ ISCSI_PARAM_MAX, }; -- cgit v1.2.3