162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * SCSI RDMA (SRP) transport class 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2007 FUJITA Tomonori <tomof@acm.org> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci#include <linux/init.h> 862306a36Sopenharmony_ci#include <linux/module.h> 962306a36Sopenharmony_ci#include <linux/jiffies.h> 1062306a36Sopenharmony_ci#include <linux/err.h> 1162306a36Sopenharmony_ci#include <linux/slab.h> 1262306a36Sopenharmony_ci#include <linux/string.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <scsi/scsi.h> 1562306a36Sopenharmony_ci#include <scsi/scsi_cmnd.h> 1662306a36Sopenharmony_ci#include <scsi/scsi_device.h> 1762306a36Sopenharmony_ci#include <scsi/scsi_host.h> 1862306a36Sopenharmony_ci#include <scsi/scsi_transport.h> 1962306a36Sopenharmony_ci#include <scsi/scsi_transport_srp.h> 2062306a36Sopenharmony_ci#include "scsi_priv.h" 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_cistruct srp_host_attrs { 2362306a36Sopenharmony_ci atomic_t next_port_id; 2462306a36Sopenharmony_ci}; 2562306a36Sopenharmony_ci#define to_srp_host_attrs(host) ((struct srp_host_attrs *)(host)->shost_data) 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#define SRP_HOST_ATTRS 0 2862306a36Sopenharmony_ci#define SRP_RPORT_ATTRS 8 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cistruct srp_internal { 3162306a36Sopenharmony_ci struct scsi_transport_template t; 3262306a36Sopenharmony_ci struct srp_function_template *f; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci struct device_attribute *host_attrs[SRP_HOST_ATTRS + 1]; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci struct device_attribute *rport_attrs[SRP_RPORT_ATTRS + 1]; 3762306a36Sopenharmony_ci struct transport_container rport_attr_cont; 3862306a36Sopenharmony_ci}; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistatic int scsi_is_srp_rport(const struct device *dev); 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci#define to_srp_internal(tmpl) container_of(tmpl, struct srp_internal, t) 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci#define dev_to_rport(d) container_of(d, struct srp_rport, dev) 4562306a36Sopenharmony_ci#define transport_class_to_srp_rport(dev) dev_to_rport((dev)->parent) 4662306a36Sopenharmony_cistatic inline struct Scsi_Host *rport_to_shost(struct srp_rport *r) 4762306a36Sopenharmony_ci{ 4862306a36Sopenharmony_ci return dev_to_shost(r->dev.parent); 4962306a36Sopenharmony_ci} 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cistatic int find_child_rport(struct device *dev, void *data) 5262306a36Sopenharmony_ci{ 5362306a36Sopenharmony_ci struct device **child = data; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci if (scsi_is_srp_rport(dev)) { 5662306a36Sopenharmony_ci WARN_ON_ONCE(*child); 5762306a36Sopenharmony_ci *child = dev; 5862306a36Sopenharmony_ci } 5962306a36Sopenharmony_ci return 0; 6062306a36Sopenharmony_ci} 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_cistatic inline struct srp_rport *shost_to_rport(struct Scsi_Host *shost) 6362306a36Sopenharmony_ci{ 6462306a36Sopenharmony_ci struct device *child = NULL; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci WARN_ON_ONCE(device_for_each_child(&shost->shost_gendev, &child, 6762306a36Sopenharmony_ci find_child_rport) < 0); 6862306a36Sopenharmony_ci return child ? dev_to_rport(child) : NULL; 6962306a36Sopenharmony_ci} 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci/** 7262306a36Sopenharmony_ci * srp_tmo_valid() - check timeout combination validity 7362306a36Sopenharmony_ci * @reconnect_delay: Reconnect delay in seconds. 7462306a36Sopenharmony_ci * @fast_io_fail_tmo: Fast I/O fail timeout in seconds. 7562306a36Sopenharmony_ci * @dev_loss_tmo: Device loss timeout in seconds. 7662306a36Sopenharmony_ci * 7762306a36Sopenharmony_ci * The combination of the timeout parameters must be such that SCSI commands 7862306a36Sopenharmony_ci * are finished in a reasonable time. Hence do not allow the fast I/O fail 7962306a36Sopenharmony_ci * timeout to exceed SCSI_DEVICE_BLOCK_MAX_TIMEOUT nor allow dev_loss_tmo to 8062306a36Sopenharmony_ci * exceed that limit if failing I/O fast has been disabled. Furthermore, these 8162306a36Sopenharmony_ci * parameters must be such that multipath can detect failed paths timely. 8262306a36Sopenharmony_ci * Hence do not allow all three parameters to be disabled simultaneously. 8362306a36Sopenharmony_ci */ 8462306a36Sopenharmony_ciint srp_tmo_valid(int reconnect_delay, int fast_io_fail_tmo, long dev_loss_tmo) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci if (reconnect_delay < 0 && fast_io_fail_tmo < 0 && dev_loss_tmo < 0) 8762306a36Sopenharmony_ci return -EINVAL; 8862306a36Sopenharmony_ci if (reconnect_delay == 0) 8962306a36Sopenharmony_ci return -EINVAL; 9062306a36Sopenharmony_ci if (fast_io_fail_tmo > SCSI_DEVICE_BLOCK_MAX_TIMEOUT) 9162306a36Sopenharmony_ci return -EINVAL; 9262306a36Sopenharmony_ci if (fast_io_fail_tmo < 0 && 9362306a36Sopenharmony_ci dev_loss_tmo > SCSI_DEVICE_BLOCK_MAX_TIMEOUT) 9462306a36Sopenharmony_ci return -EINVAL; 9562306a36Sopenharmony_ci if (dev_loss_tmo >= LONG_MAX / HZ) 9662306a36Sopenharmony_ci return -EINVAL; 9762306a36Sopenharmony_ci if (fast_io_fail_tmo >= 0 && dev_loss_tmo >= 0 && 9862306a36Sopenharmony_ci fast_io_fail_tmo >= dev_loss_tmo) 9962306a36Sopenharmony_ci return -EINVAL; 10062306a36Sopenharmony_ci return 0; 10162306a36Sopenharmony_ci} 10262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(srp_tmo_valid); 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_cistatic int srp_host_setup(struct transport_container *tc, struct device *dev, 10562306a36Sopenharmony_ci struct device *cdev) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci struct Scsi_Host *shost = dev_to_shost(dev); 10862306a36Sopenharmony_ci struct srp_host_attrs *srp_host = to_srp_host_attrs(shost); 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci atomic_set(&srp_host->next_port_id, 0); 11162306a36Sopenharmony_ci return 0; 11262306a36Sopenharmony_ci} 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_cistatic DECLARE_TRANSPORT_CLASS(srp_host_class, "srp_host", srp_host_setup, 11562306a36Sopenharmony_ci NULL, NULL); 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistatic DECLARE_TRANSPORT_CLASS(srp_rport_class, "srp_remote_ports", 11862306a36Sopenharmony_ci NULL, NULL, NULL); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_cistatic ssize_t 12162306a36Sopenharmony_cishow_srp_rport_id(struct device *dev, struct device_attribute *attr, 12262306a36Sopenharmony_ci char *buf) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci struct srp_rport *rport = transport_class_to_srp_rport(dev); 12562306a36Sopenharmony_ci return sprintf(buf, "%16phC\n", rport->port_id); 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_cistatic DEVICE_ATTR(port_id, S_IRUGO, show_srp_rport_id, NULL); 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cistatic const struct { 13162306a36Sopenharmony_ci u32 value; 13262306a36Sopenharmony_ci char *name; 13362306a36Sopenharmony_ci} srp_rport_role_names[] = { 13462306a36Sopenharmony_ci {SRP_RPORT_ROLE_INITIATOR, "SRP Initiator"}, 13562306a36Sopenharmony_ci {SRP_RPORT_ROLE_TARGET, "SRP Target"}, 13662306a36Sopenharmony_ci}; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_cistatic ssize_t 13962306a36Sopenharmony_cishow_srp_rport_roles(struct device *dev, struct device_attribute *attr, 14062306a36Sopenharmony_ci char *buf) 14162306a36Sopenharmony_ci{ 14262306a36Sopenharmony_ci struct srp_rport *rport = transport_class_to_srp_rport(dev); 14362306a36Sopenharmony_ci int i; 14462306a36Sopenharmony_ci char *name = NULL; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(srp_rport_role_names); i++) 14762306a36Sopenharmony_ci if (srp_rport_role_names[i].value == rport->roles) { 14862306a36Sopenharmony_ci name = srp_rport_role_names[i].name; 14962306a36Sopenharmony_ci break; 15062306a36Sopenharmony_ci } 15162306a36Sopenharmony_ci return sprintf(buf, "%s\n", name ? : "unknown"); 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cistatic DEVICE_ATTR(roles, S_IRUGO, show_srp_rport_roles, NULL); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_cistatic ssize_t store_srp_rport_delete(struct device *dev, 15762306a36Sopenharmony_ci struct device_attribute *attr, 15862306a36Sopenharmony_ci const char *buf, size_t count) 15962306a36Sopenharmony_ci{ 16062306a36Sopenharmony_ci struct srp_rport *rport = transport_class_to_srp_rport(dev); 16162306a36Sopenharmony_ci struct Scsi_Host *shost = dev_to_shost(dev); 16262306a36Sopenharmony_ci struct srp_internal *i = to_srp_internal(shost->transportt); 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci if (i->f->rport_delete) { 16562306a36Sopenharmony_ci i->f->rport_delete(rport); 16662306a36Sopenharmony_ci return count; 16762306a36Sopenharmony_ci } else { 16862306a36Sopenharmony_ci return -ENOSYS; 16962306a36Sopenharmony_ci } 17062306a36Sopenharmony_ci} 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_cistatic DEVICE_ATTR(delete, S_IWUSR, NULL, store_srp_rport_delete); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_cistatic ssize_t show_srp_rport_state(struct device *dev, 17562306a36Sopenharmony_ci struct device_attribute *attr, 17662306a36Sopenharmony_ci char *buf) 17762306a36Sopenharmony_ci{ 17862306a36Sopenharmony_ci static const char *const state_name[] = { 17962306a36Sopenharmony_ci [SRP_RPORT_RUNNING] = "running", 18062306a36Sopenharmony_ci [SRP_RPORT_BLOCKED] = "blocked", 18162306a36Sopenharmony_ci [SRP_RPORT_FAIL_FAST] = "fail-fast", 18262306a36Sopenharmony_ci [SRP_RPORT_LOST] = "lost", 18362306a36Sopenharmony_ci }; 18462306a36Sopenharmony_ci struct srp_rport *rport = transport_class_to_srp_rport(dev); 18562306a36Sopenharmony_ci enum srp_rport_state state = rport->state; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci return sprintf(buf, "%s\n", 18862306a36Sopenharmony_ci (unsigned)state < ARRAY_SIZE(state_name) ? 18962306a36Sopenharmony_ci state_name[state] : "???"); 19062306a36Sopenharmony_ci} 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_cistatic DEVICE_ATTR(state, S_IRUGO, show_srp_rport_state, NULL); 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_cistatic ssize_t srp_show_tmo(char *buf, int tmo) 19562306a36Sopenharmony_ci{ 19662306a36Sopenharmony_ci return tmo >= 0 ? sprintf(buf, "%d\n", tmo) : sprintf(buf, "off\n"); 19762306a36Sopenharmony_ci} 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ciint srp_parse_tmo(int *tmo, const char *buf) 20062306a36Sopenharmony_ci{ 20162306a36Sopenharmony_ci int res = 0; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci if (strncmp(buf, "off", 3) != 0) 20462306a36Sopenharmony_ci res = kstrtoint(buf, 0, tmo); 20562306a36Sopenharmony_ci else 20662306a36Sopenharmony_ci *tmo = -1; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci return res; 20962306a36Sopenharmony_ci} 21062306a36Sopenharmony_ciEXPORT_SYMBOL(srp_parse_tmo); 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_cistatic ssize_t show_reconnect_delay(struct device *dev, 21362306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 21462306a36Sopenharmony_ci{ 21562306a36Sopenharmony_ci struct srp_rport *rport = transport_class_to_srp_rport(dev); 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci return srp_show_tmo(buf, rport->reconnect_delay); 21862306a36Sopenharmony_ci} 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_cistatic ssize_t store_reconnect_delay(struct device *dev, 22162306a36Sopenharmony_ci struct device_attribute *attr, 22262306a36Sopenharmony_ci const char *buf, const size_t count) 22362306a36Sopenharmony_ci{ 22462306a36Sopenharmony_ci struct srp_rport *rport = transport_class_to_srp_rport(dev); 22562306a36Sopenharmony_ci int res, delay; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci res = srp_parse_tmo(&delay, buf); 22862306a36Sopenharmony_ci if (res) 22962306a36Sopenharmony_ci goto out; 23062306a36Sopenharmony_ci res = srp_tmo_valid(delay, rport->fast_io_fail_tmo, 23162306a36Sopenharmony_ci rport->dev_loss_tmo); 23262306a36Sopenharmony_ci if (res) 23362306a36Sopenharmony_ci goto out; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci if (rport->reconnect_delay <= 0 && delay > 0 && 23662306a36Sopenharmony_ci rport->state != SRP_RPORT_RUNNING) { 23762306a36Sopenharmony_ci queue_delayed_work(system_long_wq, &rport->reconnect_work, 23862306a36Sopenharmony_ci delay * HZ); 23962306a36Sopenharmony_ci } else if (delay <= 0) { 24062306a36Sopenharmony_ci cancel_delayed_work(&rport->reconnect_work); 24162306a36Sopenharmony_ci } 24262306a36Sopenharmony_ci rport->reconnect_delay = delay; 24362306a36Sopenharmony_ci res = count; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ciout: 24662306a36Sopenharmony_ci return res; 24762306a36Sopenharmony_ci} 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_cistatic DEVICE_ATTR(reconnect_delay, S_IRUGO | S_IWUSR, show_reconnect_delay, 25062306a36Sopenharmony_ci store_reconnect_delay); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_cistatic ssize_t show_failed_reconnects(struct device *dev, 25362306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 25462306a36Sopenharmony_ci{ 25562306a36Sopenharmony_ci struct srp_rport *rport = transport_class_to_srp_rport(dev); 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci return sprintf(buf, "%d\n", rport->failed_reconnects); 25862306a36Sopenharmony_ci} 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_cistatic DEVICE_ATTR(failed_reconnects, S_IRUGO, show_failed_reconnects, NULL); 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_cistatic ssize_t show_srp_rport_fast_io_fail_tmo(struct device *dev, 26362306a36Sopenharmony_ci struct device_attribute *attr, 26462306a36Sopenharmony_ci char *buf) 26562306a36Sopenharmony_ci{ 26662306a36Sopenharmony_ci struct srp_rport *rport = transport_class_to_srp_rport(dev); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci return srp_show_tmo(buf, rport->fast_io_fail_tmo); 26962306a36Sopenharmony_ci} 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_cistatic ssize_t store_srp_rport_fast_io_fail_tmo(struct device *dev, 27262306a36Sopenharmony_ci struct device_attribute *attr, 27362306a36Sopenharmony_ci const char *buf, size_t count) 27462306a36Sopenharmony_ci{ 27562306a36Sopenharmony_ci struct srp_rport *rport = transport_class_to_srp_rport(dev); 27662306a36Sopenharmony_ci int res; 27762306a36Sopenharmony_ci int fast_io_fail_tmo; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci res = srp_parse_tmo(&fast_io_fail_tmo, buf); 28062306a36Sopenharmony_ci if (res) 28162306a36Sopenharmony_ci goto out; 28262306a36Sopenharmony_ci res = srp_tmo_valid(rport->reconnect_delay, fast_io_fail_tmo, 28362306a36Sopenharmony_ci rport->dev_loss_tmo); 28462306a36Sopenharmony_ci if (res) 28562306a36Sopenharmony_ci goto out; 28662306a36Sopenharmony_ci rport->fast_io_fail_tmo = fast_io_fail_tmo; 28762306a36Sopenharmony_ci res = count; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ciout: 29062306a36Sopenharmony_ci return res; 29162306a36Sopenharmony_ci} 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_cistatic DEVICE_ATTR(fast_io_fail_tmo, S_IRUGO | S_IWUSR, 29462306a36Sopenharmony_ci show_srp_rport_fast_io_fail_tmo, 29562306a36Sopenharmony_ci store_srp_rport_fast_io_fail_tmo); 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_cistatic ssize_t show_srp_rport_dev_loss_tmo(struct device *dev, 29862306a36Sopenharmony_ci struct device_attribute *attr, 29962306a36Sopenharmony_ci char *buf) 30062306a36Sopenharmony_ci{ 30162306a36Sopenharmony_ci struct srp_rport *rport = transport_class_to_srp_rport(dev); 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci return srp_show_tmo(buf, rport->dev_loss_tmo); 30462306a36Sopenharmony_ci} 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_cistatic ssize_t store_srp_rport_dev_loss_tmo(struct device *dev, 30762306a36Sopenharmony_ci struct device_attribute *attr, 30862306a36Sopenharmony_ci const char *buf, size_t count) 30962306a36Sopenharmony_ci{ 31062306a36Sopenharmony_ci struct srp_rport *rport = transport_class_to_srp_rport(dev); 31162306a36Sopenharmony_ci int res; 31262306a36Sopenharmony_ci int dev_loss_tmo; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci res = srp_parse_tmo(&dev_loss_tmo, buf); 31562306a36Sopenharmony_ci if (res) 31662306a36Sopenharmony_ci goto out; 31762306a36Sopenharmony_ci res = srp_tmo_valid(rport->reconnect_delay, rport->fast_io_fail_tmo, 31862306a36Sopenharmony_ci dev_loss_tmo); 31962306a36Sopenharmony_ci if (res) 32062306a36Sopenharmony_ci goto out; 32162306a36Sopenharmony_ci rport->dev_loss_tmo = dev_loss_tmo; 32262306a36Sopenharmony_ci res = count; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ciout: 32562306a36Sopenharmony_ci return res; 32662306a36Sopenharmony_ci} 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_cistatic DEVICE_ATTR(dev_loss_tmo, S_IRUGO | S_IWUSR, 32962306a36Sopenharmony_ci show_srp_rport_dev_loss_tmo, 33062306a36Sopenharmony_ci store_srp_rport_dev_loss_tmo); 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_cistatic int srp_rport_set_state(struct srp_rport *rport, 33362306a36Sopenharmony_ci enum srp_rport_state new_state) 33462306a36Sopenharmony_ci{ 33562306a36Sopenharmony_ci enum srp_rport_state old_state = rport->state; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci lockdep_assert_held(&rport->mutex); 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci switch (new_state) { 34062306a36Sopenharmony_ci case SRP_RPORT_RUNNING: 34162306a36Sopenharmony_ci switch (old_state) { 34262306a36Sopenharmony_ci case SRP_RPORT_LOST: 34362306a36Sopenharmony_ci goto invalid; 34462306a36Sopenharmony_ci default: 34562306a36Sopenharmony_ci break; 34662306a36Sopenharmony_ci } 34762306a36Sopenharmony_ci break; 34862306a36Sopenharmony_ci case SRP_RPORT_BLOCKED: 34962306a36Sopenharmony_ci switch (old_state) { 35062306a36Sopenharmony_ci case SRP_RPORT_RUNNING: 35162306a36Sopenharmony_ci break; 35262306a36Sopenharmony_ci default: 35362306a36Sopenharmony_ci goto invalid; 35462306a36Sopenharmony_ci } 35562306a36Sopenharmony_ci break; 35662306a36Sopenharmony_ci case SRP_RPORT_FAIL_FAST: 35762306a36Sopenharmony_ci switch (old_state) { 35862306a36Sopenharmony_ci case SRP_RPORT_LOST: 35962306a36Sopenharmony_ci goto invalid; 36062306a36Sopenharmony_ci default: 36162306a36Sopenharmony_ci break; 36262306a36Sopenharmony_ci } 36362306a36Sopenharmony_ci break; 36462306a36Sopenharmony_ci case SRP_RPORT_LOST: 36562306a36Sopenharmony_ci break; 36662306a36Sopenharmony_ci } 36762306a36Sopenharmony_ci rport->state = new_state; 36862306a36Sopenharmony_ci return 0; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ciinvalid: 37162306a36Sopenharmony_ci return -EINVAL; 37262306a36Sopenharmony_ci} 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci/** 37562306a36Sopenharmony_ci * srp_reconnect_work() - reconnect and schedule a new attempt if necessary 37662306a36Sopenharmony_ci * @work: Work structure used for scheduling this operation. 37762306a36Sopenharmony_ci */ 37862306a36Sopenharmony_cistatic void srp_reconnect_work(struct work_struct *work) 37962306a36Sopenharmony_ci{ 38062306a36Sopenharmony_ci struct srp_rport *rport = container_of(to_delayed_work(work), 38162306a36Sopenharmony_ci struct srp_rport, reconnect_work); 38262306a36Sopenharmony_ci struct Scsi_Host *shost = rport_to_shost(rport); 38362306a36Sopenharmony_ci int delay, res; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci res = srp_reconnect_rport(rport); 38662306a36Sopenharmony_ci if (res != 0) { 38762306a36Sopenharmony_ci shost_printk(KERN_ERR, shost, 38862306a36Sopenharmony_ci "reconnect attempt %d failed (%d)\n", 38962306a36Sopenharmony_ci ++rport->failed_reconnects, res); 39062306a36Sopenharmony_ci delay = rport->reconnect_delay * 39162306a36Sopenharmony_ci min(100, max(1, rport->failed_reconnects - 10)); 39262306a36Sopenharmony_ci if (delay > 0) 39362306a36Sopenharmony_ci queue_delayed_work(system_long_wq, 39462306a36Sopenharmony_ci &rport->reconnect_work, delay * HZ); 39562306a36Sopenharmony_ci } 39662306a36Sopenharmony_ci} 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci/* 39962306a36Sopenharmony_ci * scsi_block_targets() must have been called before this function is 40062306a36Sopenharmony_ci * called to guarantee that no .queuecommand() calls are in progress. 40162306a36Sopenharmony_ci */ 40262306a36Sopenharmony_cistatic void __rport_fail_io_fast(struct srp_rport *rport) 40362306a36Sopenharmony_ci{ 40462306a36Sopenharmony_ci struct Scsi_Host *shost = rport_to_shost(rport); 40562306a36Sopenharmony_ci struct srp_internal *i; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci lockdep_assert_held(&rport->mutex); 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci if (srp_rport_set_state(rport, SRP_RPORT_FAIL_FAST)) 41062306a36Sopenharmony_ci return; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci scsi_target_unblock(rport->dev.parent, SDEV_TRANSPORT_OFFLINE); 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci /* Involve the LLD if possible to terminate all I/O on the rport. */ 41562306a36Sopenharmony_ci i = to_srp_internal(shost->transportt); 41662306a36Sopenharmony_ci if (i->f->terminate_rport_io) 41762306a36Sopenharmony_ci i->f->terminate_rport_io(rport); 41862306a36Sopenharmony_ci} 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci/** 42162306a36Sopenharmony_ci * rport_fast_io_fail_timedout() - fast I/O failure timeout handler 42262306a36Sopenharmony_ci * @work: Work structure used for scheduling this operation. 42362306a36Sopenharmony_ci */ 42462306a36Sopenharmony_cistatic void rport_fast_io_fail_timedout(struct work_struct *work) 42562306a36Sopenharmony_ci{ 42662306a36Sopenharmony_ci struct srp_rport *rport = container_of(to_delayed_work(work), 42762306a36Sopenharmony_ci struct srp_rport, fast_io_fail_work); 42862306a36Sopenharmony_ci struct Scsi_Host *shost = rport_to_shost(rport); 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci pr_info("fast_io_fail_tmo expired for SRP %s / %s.\n", 43162306a36Sopenharmony_ci dev_name(&rport->dev), dev_name(&shost->shost_gendev)); 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci mutex_lock(&rport->mutex); 43462306a36Sopenharmony_ci if (rport->state == SRP_RPORT_BLOCKED) 43562306a36Sopenharmony_ci __rport_fail_io_fast(rport); 43662306a36Sopenharmony_ci mutex_unlock(&rport->mutex); 43762306a36Sopenharmony_ci} 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci/** 44062306a36Sopenharmony_ci * rport_dev_loss_timedout() - device loss timeout handler 44162306a36Sopenharmony_ci * @work: Work structure used for scheduling this operation. 44262306a36Sopenharmony_ci */ 44362306a36Sopenharmony_cistatic void rport_dev_loss_timedout(struct work_struct *work) 44462306a36Sopenharmony_ci{ 44562306a36Sopenharmony_ci struct srp_rport *rport = container_of(to_delayed_work(work), 44662306a36Sopenharmony_ci struct srp_rport, dev_loss_work); 44762306a36Sopenharmony_ci struct Scsi_Host *shost = rport_to_shost(rport); 44862306a36Sopenharmony_ci struct srp_internal *i = to_srp_internal(shost->transportt); 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci pr_info("dev_loss_tmo expired for SRP %s / %s.\n", 45162306a36Sopenharmony_ci dev_name(&rport->dev), dev_name(&shost->shost_gendev)); 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci mutex_lock(&rport->mutex); 45462306a36Sopenharmony_ci WARN_ON(srp_rport_set_state(rport, SRP_RPORT_LOST) != 0); 45562306a36Sopenharmony_ci scsi_target_unblock(rport->dev.parent, SDEV_TRANSPORT_OFFLINE); 45662306a36Sopenharmony_ci mutex_unlock(&rport->mutex); 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci i->f->rport_delete(rport); 45962306a36Sopenharmony_ci} 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_cistatic void __srp_start_tl_fail_timers(struct srp_rport *rport) 46262306a36Sopenharmony_ci{ 46362306a36Sopenharmony_ci struct Scsi_Host *shost = rport_to_shost(rport); 46462306a36Sopenharmony_ci int delay, fast_io_fail_tmo, dev_loss_tmo; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci lockdep_assert_held(&rport->mutex); 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci delay = rport->reconnect_delay; 46962306a36Sopenharmony_ci fast_io_fail_tmo = rport->fast_io_fail_tmo; 47062306a36Sopenharmony_ci dev_loss_tmo = rport->dev_loss_tmo; 47162306a36Sopenharmony_ci pr_debug("%s current state: %d\n", dev_name(&shost->shost_gendev), 47262306a36Sopenharmony_ci rport->state); 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci if (rport->state == SRP_RPORT_LOST) 47562306a36Sopenharmony_ci return; 47662306a36Sopenharmony_ci if (delay > 0) 47762306a36Sopenharmony_ci queue_delayed_work(system_long_wq, &rport->reconnect_work, 47862306a36Sopenharmony_ci 1UL * delay * HZ); 47962306a36Sopenharmony_ci if ((fast_io_fail_tmo >= 0 || dev_loss_tmo >= 0) && 48062306a36Sopenharmony_ci srp_rport_set_state(rport, SRP_RPORT_BLOCKED) == 0) { 48162306a36Sopenharmony_ci pr_debug("%s new state: %d\n", dev_name(&shost->shost_gendev), 48262306a36Sopenharmony_ci rport->state); 48362306a36Sopenharmony_ci scsi_block_targets(shost, &shost->shost_gendev); 48462306a36Sopenharmony_ci if (fast_io_fail_tmo >= 0) 48562306a36Sopenharmony_ci queue_delayed_work(system_long_wq, 48662306a36Sopenharmony_ci &rport->fast_io_fail_work, 48762306a36Sopenharmony_ci 1UL * fast_io_fail_tmo * HZ); 48862306a36Sopenharmony_ci if (dev_loss_tmo >= 0) 48962306a36Sopenharmony_ci queue_delayed_work(system_long_wq, 49062306a36Sopenharmony_ci &rport->dev_loss_work, 49162306a36Sopenharmony_ci 1UL * dev_loss_tmo * HZ); 49262306a36Sopenharmony_ci } 49362306a36Sopenharmony_ci} 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci/** 49662306a36Sopenharmony_ci * srp_start_tl_fail_timers() - start the transport layer failure timers 49762306a36Sopenharmony_ci * @rport: SRP target port. 49862306a36Sopenharmony_ci * 49962306a36Sopenharmony_ci * Start the transport layer fast I/O failure and device loss timers. Do not 50062306a36Sopenharmony_ci * modify a timer that was already started. 50162306a36Sopenharmony_ci */ 50262306a36Sopenharmony_civoid srp_start_tl_fail_timers(struct srp_rport *rport) 50362306a36Sopenharmony_ci{ 50462306a36Sopenharmony_ci mutex_lock(&rport->mutex); 50562306a36Sopenharmony_ci __srp_start_tl_fail_timers(rport); 50662306a36Sopenharmony_ci mutex_unlock(&rport->mutex); 50762306a36Sopenharmony_ci} 50862306a36Sopenharmony_ciEXPORT_SYMBOL(srp_start_tl_fail_timers); 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci/** 51162306a36Sopenharmony_ci * srp_reconnect_rport() - reconnect to an SRP target port 51262306a36Sopenharmony_ci * @rport: SRP target port. 51362306a36Sopenharmony_ci * 51462306a36Sopenharmony_ci * Blocks SCSI command queueing before invoking reconnect() such that 51562306a36Sopenharmony_ci * queuecommand() won't be invoked concurrently with reconnect() from outside 51662306a36Sopenharmony_ci * the SCSI EH. This is important since a reconnect() implementation may 51762306a36Sopenharmony_ci * reallocate resources needed by queuecommand(). 51862306a36Sopenharmony_ci * 51962306a36Sopenharmony_ci * Notes: 52062306a36Sopenharmony_ci * - This function neither waits until outstanding requests have finished nor 52162306a36Sopenharmony_ci * tries to abort these. It is the responsibility of the reconnect() 52262306a36Sopenharmony_ci * function to finish outstanding commands before reconnecting to the target 52362306a36Sopenharmony_ci * port. 52462306a36Sopenharmony_ci * - It is the responsibility of the caller to ensure that the resources 52562306a36Sopenharmony_ci * reallocated by the reconnect() function won't be used while this function 52662306a36Sopenharmony_ci * is in progress. One possible strategy is to invoke this function from 52762306a36Sopenharmony_ci * the context of the SCSI EH thread only. Another possible strategy is to 52862306a36Sopenharmony_ci * lock the rport mutex inside each SCSI LLD callback that can be invoked by 52962306a36Sopenharmony_ci * the SCSI EH (the scsi_host_template.eh_*() functions and also the 53062306a36Sopenharmony_ci * scsi_host_template.queuecommand() function). 53162306a36Sopenharmony_ci */ 53262306a36Sopenharmony_ciint srp_reconnect_rport(struct srp_rport *rport) 53362306a36Sopenharmony_ci{ 53462306a36Sopenharmony_ci struct Scsi_Host *shost = rport_to_shost(rport); 53562306a36Sopenharmony_ci struct srp_internal *i = to_srp_internal(shost->transportt); 53662306a36Sopenharmony_ci struct scsi_device *sdev; 53762306a36Sopenharmony_ci int res; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci pr_debug("SCSI host %s\n", dev_name(&shost->shost_gendev)); 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci res = mutex_lock_interruptible(&rport->mutex); 54262306a36Sopenharmony_ci if (res) 54362306a36Sopenharmony_ci goto out; 54462306a36Sopenharmony_ci if (rport->state != SRP_RPORT_FAIL_FAST && rport->state != SRP_RPORT_LOST) 54562306a36Sopenharmony_ci /* 54662306a36Sopenharmony_ci * sdev state must be SDEV_TRANSPORT_OFFLINE, transition 54762306a36Sopenharmony_ci * to SDEV_BLOCK is illegal. Calling scsi_target_unblock() 54862306a36Sopenharmony_ci * later is ok though, scsi_internal_device_unblock_nowait() 54962306a36Sopenharmony_ci * treats SDEV_TRANSPORT_OFFLINE like SDEV_BLOCK. 55062306a36Sopenharmony_ci */ 55162306a36Sopenharmony_ci scsi_block_targets(shost, &shost->shost_gendev); 55262306a36Sopenharmony_ci res = rport->state != SRP_RPORT_LOST ? i->f->reconnect(rport) : -ENODEV; 55362306a36Sopenharmony_ci pr_debug("%s (state %d): transport.reconnect() returned %d\n", 55462306a36Sopenharmony_ci dev_name(&shost->shost_gendev), rport->state, res); 55562306a36Sopenharmony_ci if (res == 0) { 55662306a36Sopenharmony_ci cancel_delayed_work(&rport->fast_io_fail_work); 55762306a36Sopenharmony_ci cancel_delayed_work(&rport->dev_loss_work); 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci rport->failed_reconnects = 0; 56062306a36Sopenharmony_ci srp_rport_set_state(rport, SRP_RPORT_RUNNING); 56162306a36Sopenharmony_ci scsi_target_unblock(&shost->shost_gendev, SDEV_RUNNING); 56262306a36Sopenharmony_ci /* 56362306a36Sopenharmony_ci * If the SCSI error handler has offlined one or more devices, 56462306a36Sopenharmony_ci * invoking scsi_target_unblock() won't change the state of 56562306a36Sopenharmony_ci * these devices into running so do that explicitly. 56662306a36Sopenharmony_ci */ 56762306a36Sopenharmony_ci shost_for_each_device(sdev, shost) { 56862306a36Sopenharmony_ci mutex_lock(&sdev->state_mutex); 56962306a36Sopenharmony_ci if (sdev->sdev_state == SDEV_OFFLINE) 57062306a36Sopenharmony_ci sdev->sdev_state = SDEV_RUNNING; 57162306a36Sopenharmony_ci mutex_unlock(&sdev->state_mutex); 57262306a36Sopenharmony_ci } 57362306a36Sopenharmony_ci } else if (rport->state == SRP_RPORT_RUNNING) { 57462306a36Sopenharmony_ci /* 57562306a36Sopenharmony_ci * srp_reconnect_rport() has been invoked with fast_io_fail 57662306a36Sopenharmony_ci * and dev_loss off. Mark the port as failed and start the TL 57762306a36Sopenharmony_ci * failure timers if these had not yet been started. 57862306a36Sopenharmony_ci */ 57962306a36Sopenharmony_ci __rport_fail_io_fast(rport); 58062306a36Sopenharmony_ci __srp_start_tl_fail_timers(rport); 58162306a36Sopenharmony_ci } else if (rport->state != SRP_RPORT_BLOCKED) { 58262306a36Sopenharmony_ci scsi_target_unblock(&shost->shost_gendev, 58362306a36Sopenharmony_ci SDEV_TRANSPORT_OFFLINE); 58462306a36Sopenharmony_ci } 58562306a36Sopenharmony_ci mutex_unlock(&rport->mutex); 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ciout: 58862306a36Sopenharmony_ci return res; 58962306a36Sopenharmony_ci} 59062306a36Sopenharmony_ciEXPORT_SYMBOL(srp_reconnect_rport); 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci/** 59362306a36Sopenharmony_ci * srp_timed_out() - SRP transport intercept of the SCSI timeout EH 59462306a36Sopenharmony_ci * @scmd: SCSI command. 59562306a36Sopenharmony_ci * 59662306a36Sopenharmony_ci * If a timeout occurs while an rport is in the blocked state, ask the SCSI 59762306a36Sopenharmony_ci * EH to continue waiting (SCSI_EH_RESET_TIMER). Otherwise let the SCSI core 59862306a36Sopenharmony_ci * handle the timeout (SCSI_EH_NOT_HANDLED). 59962306a36Sopenharmony_ci * 60062306a36Sopenharmony_ci * Note: This function is called from soft-IRQ context and with the request 60162306a36Sopenharmony_ci * queue lock held. 60262306a36Sopenharmony_ci */ 60362306a36Sopenharmony_cienum scsi_timeout_action srp_timed_out(struct scsi_cmnd *scmd) 60462306a36Sopenharmony_ci{ 60562306a36Sopenharmony_ci struct scsi_device *sdev = scmd->device; 60662306a36Sopenharmony_ci struct Scsi_Host *shost = sdev->host; 60762306a36Sopenharmony_ci struct srp_internal *i = to_srp_internal(shost->transportt); 60862306a36Sopenharmony_ci struct srp_rport *rport = shost_to_rport(shost); 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci pr_debug("timeout for sdev %s\n", dev_name(&sdev->sdev_gendev)); 61162306a36Sopenharmony_ci return rport && rport->fast_io_fail_tmo < 0 && 61262306a36Sopenharmony_ci rport->dev_loss_tmo < 0 && 61362306a36Sopenharmony_ci i->f->reset_timer_if_blocked && scsi_device_blocked(sdev) ? 61462306a36Sopenharmony_ci SCSI_EH_RESET_TIMER : SCSI_EH_NOT_HANDLED; 61562306a36Sopenharmony_ci} 61662306a36Sopenharmony_ciEXPORT_SYMBOL(srp_timed_out); 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_cistatic void srp_rport_release(struct device *dev) 61962306a36Sopenharmony_ci{ 62062306a36Sopenharmony_ci struct srp_rport *rport = dev_to_rport(dev); 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci put_device(dev->parent); 62362306a36Sopenharmony_ci kfree(rport); 62462306a36Sopenharmony_ci} 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_cistatic int scsi_is_srp_rport(const struct device *dev) 62762306a36Sopenharmony_ci{ 62862306a36Sopenharmony_ci return dev->release == srp_rport_release; 62962306a36Sopenharmony_ci} 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_cistatic int srp_rport_match(struct attribute_container *cont, 63262306a36Sopenharmony_ci struct device *dev) 63362306a36Sopenharmony_ci{ 63462306a36Sopenharmony_ci struct Scsi_Host *shost; 63562306a36Sopenharmony_ci struct srp_internal *i; 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci if (!scsi_is_srp_rport(dev)) 63862306a36Sopenharmony_ci return 0; 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci shost = dev_to_shost(dev->parent); 64162306a36Sopenharmony_ci if (!shost->transportt) 64262306a36Sopenharmony_ci return 0; 64362306a36Sopenharmony_ci if (shost->transportt->host_attrs.ac.class != &srp_host_class.class) 64462306a36Sopenharmony_ci return 0; 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci i = to_srp_internal(shost->transportt); 64762306a36Sopenharmony_ci return &i->rport_attr_cont.ac == cont; 64862306a36Sopenharmony_ci} 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_cistatic int srp_host_match(struct attribute_container *cont, struct device *dev) 65162306a36Sopenharmony_ci{ 65262306a36Sopenharmony_ci struct Scsi_Host *shost; 65362306a36Sopenharmony_ci struct srp_internal *i; 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci if (!scsi_is_host_device(dev)) 65662306a36Sopenharmony_ci return 0; 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci shost = dev_to_shost(dev); 65962306a36Sopenharmony_ci if (!shost->transportt) 66062306a36Sopenharmony_ci return 0; 66162306a36Sopenharmony_ci if (shost->transportt->host_attrs.ac.class != &srp_host_class.class) 66262306a36Sopenharmony_ci return 0; 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci i = to_srp_internal(shost->transportt); 66562306a36Sopenharmony_ci return &i->t.host_attrs.ac == cont; 66662306a36Sopenharmony_ci} 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci/** 66962306a36Sopenharmony_ci * srp_rport_get() - increment rport reference count 67062306a36Sopenharmony_ci * @rport: SRP target port. 67162306a36Sopenharmony_ci */ 67262306a36Sopenharmony_civoid srp_rport_get(struct srp_rport *rport) 67362306a36Sopenharmony_ci{ 67462306a36Sopenharmony_ci get_device(&rport->dev); 67562306a36Sopenharmony_ci} 67662306a36Sopenharmony_ciEXPORT_SYMBOL(srp_rport_get); 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci/** 67962306a36Sopenharmony_ci * srp_rport_put() - decrement rport reference count 68062306a36Sopenharmony_ci * @rport: SRP target port. 68162306a36Sopenharmony_ci */ 68262306a36Sopenharmony_civoid srp_rport_put(struct srp_rport *rport) 68362306a36Sopenharmony_ci{ 68462306a36Sopenharmony_ci put_device(&rport->dev); 68562306a36Sopenharmony_ci} 68662306a36Sopenharmony_ciEXPORT_SYMBOL(srp_rport_put); 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci/** 68962306a36Sopenharmony_ci * srp_rport_add - add a SRP remote port to the device hierarchy 69062306a36Sopenharmony_ci * @shost: scsi host the remote port is connected to. 69162306a36Sopenharmony_ci * @ids: The port id for the remote port. 69262306a36Sopenharmony_ci * 69362306a36Sopenharmony_ci * Publishes a port to the rest of the system. 69462306a36Sopenharmony_ci */ 69562306a36Sopenharmony_cistruct srp_rport *srp_rport_add(struct Scsi_Host *shost, 69662306a36Sopenharmony_ci struct srp_rport_identifiers *ids) 69762306a36Sopenharmony_ci{ 69862306a36Sopenharmony_ci struct srp_rport *rport; 69962306a36Sopenharmony_ci struct device *parent = &shost->shost_gendev; 70062306a36Sopenharmony_ci struct srp_internal *i = to_srp_internal(shost->transportt); 70162306a36Sopenharmony_ci int id, ret; 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci rport = kzalloc(sizeof(*rport), GFP_KERNEL); 70462306a36Sopenharmony_ci if (!rport) 70562306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci mutex_init(&rport->mutex); 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci device_initialize(&rport->dev); 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci rport->dev.parent = get_device(parent); 71262306a36Sopenharmony_ci rport->dev.release = srp_rport_release; 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci memcpy(rport->port_id, ids->port_id, sizeof(rport->port_id)); 71562306a36Sopenharmony_ci rport->roles = ids->roles; 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci if (i->f->reconnect) 71862306a36Sopenharmony_ci rport->reconnect_delay = i->f->reconnect_delay ? 71962306a36Sopenharmony_ci *i->f->reconnect_delay : 10; 72062306a36Sopenharmony_ci INIT_DELAYED_WORK(&rport->reconnect_work, srp_reconnect_work); 72162306a36Sopenharmony_ci rport->fast_io_fail_tmo = i->f->fast_io_fail_tmo ? 72262306a36Sopenharmony_ci *i->f->fast_io_fail_tmo : 15; 72362306a36Sopenharmony_ci rport->dev_loss_tmo = i->f->dev_loss_tmo ? *i->f->dev_loss_tmo : 60; 72462306a36Sopenharmony_ci INIT_DELAYED_WORK(&rport->fast_io_fail_work, 72562306a36Sopenharmony_ci rport_fast_io_fail_timedout); 72662306a36Sopenharmony_ci INIT_DELAYED_WORK(&rport->dev_loss_work, rport_dev_loss_timedout); 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci id = atomic_inc_return(&to_srp_host_attrs(shost)->next_port_id); 72962306a36Sopenharmony_ci dev_set_name(&rport->dev, "port-%d:%d", shost->host_no, id); 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci transport_setup_device(&rport->dev); 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci ret = device_add(&rport->dev); 73462306a36Sopenharmony_ci if (ret) { 73562306a36Sopenharmony_ci transport_destroy_device(&rport->dev); 73662306a36Sopenharmony_ci put_device(&rport->dev); 73762306a36Sopenharmony_ci return ERR_PTR(ret); 73862306a36Sopenharmony_ci } 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci transport_add_device(&rport->dev); 74162306a36Sopenharmony_ci transport_configure_device(&rport->dev); 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci return rport; 74462306a36Sopenharmony_ci} 74562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(srp_rport_add); 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci/** 74862306a36Sopenharmony_ci * srp_rport_del - remove a SRP remote port 74962306a36Sopenharmony_ci * @rport: SRP remote port to remove 75062306a36Sopenharmony_ci * 75162306a36Sopenharmony_ci * Removes the specified SRP remote port. 75262306a36Sopenharmony_ci */ 75362306a36Sopenharmony_civoid srp_rport_del(struct srp_rport *rport) 75462306a36Sopenharmony_ci{ 75562306a36Sopenharmony_ci struct device *dev = &rport->dev; 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci transport_remove_device(dev); 75862306a36Sopenharmony_ci device_del(dev); 75962306a36Sopenharmony_ci transport_destroy_device(dev); 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci put_device(dev); 76262306a36Sopenharmony_ci} 76362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(srp_rport_del); 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_cistatic int do_srp_rport_del(struct device *dev, void *data) 76662306a36Sopenharmony_ci{ 76762306a36Sopenharmony_ci if (scsi_is_srp_rport(dev)) 76862306a36Sopenharmony_ci srp_rport_del(dev_to_rport(dev)); 76962306a36Sopenharmony_ci return 0; 77062306a36Sopenharmony_ci} 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci/** 77362306a36Sopenharmony_ci * srp_remove_host - tear down a Scsi_Host's SRP data structures 77462306a36Sopenharmony_ci * @shost: Scsi Host that is torn down 77562306a36Sopenharmony_ci * 77662306a36Sopenharmony_ci * Removes all SRP remote ports for a given Scsi_Host. 77762306a36Sopenharmony_ci * Must be called just before scsi_remove_host for SRP HBAs. 77862306a36Sopenharmony_ci */ 77962306a36Sopenharmony_civoid srp_remove_host(struct Scsi_Host *shost) 78062306a36Sopenharmony_ci{ 78162306a36Sopenharmony_ci device_for_each_child(&shost->shost_gendev, NULL, do_srp_rport_del); 78262306a36Sopenharmony_ci} 78362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(srp_remove_host); 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci/** 78662306a36Sopenharmony_ci * srp_stop_rport_timers - stop the transport layer recovery timers 78762306a36Sopenharmony_ci * @rport: SRP remote port for which to stop the timers. 78862306a36Sopenharmony_ci * 78962306a36Sopenharmony_ci * Must be called after srp_remove_host() and scsi_remove_host(). The caller 79062306a36Sopenharmony_ci * must hold a reference on the rport (rport->dev) and on the SCSI host 79162306a36Sopenharmony_ci * (rport->dev.parent). 79262306a36Sopenharmony_ci */ 79362306a36Sopenharmony_civoid srp_stop_rport_timers(struct srp_rport *rport) 79462306a36Sopenharmony_ci{ 79562306a36Sopenharmony_ci mutex_lock(&rport->mutex); 79662306a36Sopenharmony_ci if (rport->state == SRP_RPORT_BLOCKED) 79762306a36Sopenharmony_ci __rport_fail_io_fast(rport); 79862306a36Sopenharmony_ci srp_rport_set_state(rport, SRP_RPORT_LOST); 79962306a36Sopenharmony_ci mutex_unlock(&rport->mutex); 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci cancel_delayed_work_sync(&rport->reconnect_work); 80262306a36Sopenharmony_ci cancel_delayed_work_sync(&rport->fast_io_fail_work); 80362306a36Sopenharmony_ci cancel_delayed_work_sync(&rport->dev_loss_work); 80462306a36Sopenharmony_ci} 80562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(srp_stop_rport_timers); 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci/** 80862306a36Sopenharmony_ci * srp_attach_transport - instantiate SRP transport template 80962306a36Sopenharmony_ci * @ft: SRP transport class function template 81062306a36Sopenharmony_ci */ 81162306a36Sopenharmony_cistruct scsi_transport_template * 81262306a36Sopenharmony_cisrp_attach_transport(struct srp_function_template *ft) 81362306a36Sopenharmony_ci{ 81462306a36Sopenharmony_ci int count; 81562306a36Sopenharmony_ci struct srp_internal *i; 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci i = kzalloc(sizeof(*i), GFP_KERNEL); 81862306a36Sopenharmony_ci if (!i) 81962306a36Sopenharmony_ci return NULL; 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci i->t.host_size = sizeof(struct srp_host_attrs); 82262306a36Sopenharmony_ci i->t.host_attrs.ac.attrs = &i->host_attrs[0]; 82362306a36Sopenharmony_ci i->t.host_attrs.ac.class = &srp_host_class.class; 82462306a36Sopenharmony_ci i->t.host_attrs.ac.match = srp_host_match; 82562306a36Sopenharmony_ci i->host_attrs[0] = NULL; 82662306a36Sopenharmony_ci transport_container_register(&i->t.host_attrs); 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci i->rport_attr_cont.ac.attrs = &i->rport_attrs[0]; 82962306a36Sopenharmony_ci i->rport_attr_cont.ac.class = &srp_rport_class.class; 83062306a36Sopenharmony_ci i->rport_attr_cont.ac.match = srp_rport_match; 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci count = 0; 83362306a36Sopenharmony_ci i->rport_attrs[count++] = &dev_attr_port_id; 83462306a36Sopenharmony_ci i->rport_attrs[count++] = &dev_attr_roles; 83562306a36Sopenharmony_ci if (ft->has_rport_state) { 83662306a36Sopenharmony_ci i->rport_attrs[count++] = &dev_attr_state; 83762306a36Sopenharmony_ci i->rport_attrs[count++] = &dev_attr_fast_io_fail_tmo; 83862306a36Sopenharmony_ci i->rport_attrs[count++] = &dev_attr_dev_loss_tmo; 83962306a36Sopenharmony_ci } 84062306a36Sopenharmony_ci if (ft->reconnect) { 84162306a36Sopenharmony_ci i->rport_attrs[count++] = &dev_attr_reconnect_delay; 84262306a36Sopenharmony_ci i->rport_attrs[count++] = &dev_attr_failed_reconnects; 84362306a36Sopenharmony_ci } 84462306a36Sopenharmony_ci if (ft->rport_delete) 84562306a36Sopenharmony_ci i->rport_attrs[count++] = &dev_attr_delete; 84662306a36Sopenharmony_ci i->rport_attrs[count++] = NULL; 84762306a36Sopenharmony_ci BUG_ON(count > ARRAY_SIZE(i->rport_attrs)); 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci transport_container_register(&i->rport_attr_cont); 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci i->f = ft; 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci return &i->t; 85462306a36Sopenharmony_ci} 85562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(srp_attach_transport); 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci/** 85862306a36Sopenharmony_ci * srp_release_transport - release SRP transport template instance 85962306a36Sopenharmony_ci * @t: transport template instance 86062306a36Sopenharmony_ci */ 86162306a36Sopenharmony_civoid srp_release_transport(struct scsi_transport_template *t) 86262306a36Sopenharmony_ci{ 86362306a36Sopenharmony_ci struct srp_internal *i = to_srp_internal(t); 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci transport_container_unregister(&i->t.host_attrs); 86662306a36Sopenharmony_ci transport_container_unregister(&i->rport_attr_cont); 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci kfree(i); 86962306a36Sopenharmony_ci} 87062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(srp_release_transport); 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_cistatic __init int srp_transport_init(void) 87362306a36Sopenharmony_ci{ 87462306a36Sopenharmony_ci int ret; 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci ret = transport_class_register(&srp_host_class); 87762306a36Sopenharmony_ci if (ret) 87862306a36Sopenharmony_ci return ret; 87962306a36Sopenharmony_ci ret = transport_class_register(&srp_rport_class); 88062306a36Sopenharmony_ci if (ret) 88162306a36Sopenharmony_ci goto unregister_host_class; 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci return 0; 88462306a36Sopenharmony_ciunregister_host_class: 88562306a36Sopenharmony_ci transport_class_unregister(&srp_host_class); 88662306a36Sopenharmony_ci return ret; 88762306a36Sopenharmony_ci} 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_cistatic void __exit srp_transport_exit(void) 89062306a36Sopenharmony_ci{ 89162306a36Sopenharmony_ci transport_class_unregister(&srp_host_class); 89262306a36Sopenharmony_ci transport_class_unregister(&srp_rport_class); 89362306a36Sopenharmony_ci} 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ciMODULE_AUTHOR("FUJITA Tomonori"); 89662306a36Sopenharmony_ciMODULE_DESCRIPTION("SRP Transport Attributes"); 89762306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_cimodule_init(srp_transport_init); 90062306a36Sopenharmony_cimodule_exit(srp_transport_exit); 901