18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * SCSI RDMA (SRP) transport class 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2007 FUJITA Tomonori <tomof@acm.org> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci#include <linux/init.h> 88c2ecf20Sopenharmony_ci#include <linux/module.h> 98c2ecf20Sopenharmony_ci#include <linux/jiffies.h> 108c2ecf20Sopenharmony_ci#include <linux/err.h> 118c2ecf20Sopenharmony_ci#include <linux/slab.h> 128c2ecf20Sopenharmony_ci#include <linux/string.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <scsi/scsi.h> 158c2ecf20Sopenharmony_ci#include <scsi/scsi_cmnd.h> 168c2ecf20Sopenharmony_ci#include <scsi/scsi_device.h> 178c2ecf20Sopenharmony_ci#include <scsi/scsi_host.h> 188c2ecf20Sopenharmony_ci#include <scsi/scsi_transport.h> 198c2ecf20Sopenharmony_ci#include <scsi/scsi_transport_srp.h> 208c2ecf20Sopenharmony_ci#include "scsi_priv.h" 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_cistruct srp_host_attrs { 238c2ecf20Sopenharmony_ci atomic_t next_port_id; 248c2ecf20Sopenharmony_ci}; 258c2ecf20Sopenharmony_ci#define to_srp_host_attrs(host) ((struct srp_host_attrs *)(host)->shost_data) 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#define SRP_HOST_ATTRS 0 288c2ecf20Sopenharmony_ci#define SRP_RPORT_ATTRS 8 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cistruct srp_internal { 318c2ecf20Sopenharmony_ci struct scsi_transport_template t; 328c2ecf20Sopenharmony_ci struct srp_function_template *f; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci struct device_attribute *host_attrs[SRP_HOST_ATTRS + 1]; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci struct device_attribute *rport_attrs[SRP_RPORT_ATTRS + 1]; 378c2ecf20Sopenharmony_ci struct transport_container rport_attr_cont; 388c2ecf20Sopenharmony_ci}; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic int scsi_is_srp_rport(const struct device *dev); 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci#define to_srp_internal(tmpl) container_of(tmpl, struct srp_internal, t) 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci#define dev_to_rport(d) container_of(d, struct srp_rport, dev) 458c2ecf20Sopenharmony_ci#define transport_class_to_srp_rport(dev) dev_to_rport((dev)->parent) 468c2ecf20Sopenharmony_cistatic inline struct Scsi_Host *rport_to_shost(struct srp_rport *r) 478c2ecf20Sopenharmony_ci{ 488c2ecf20Sopenharmony_ci return dev_to_shost(r->dev.parent); 498c2ecf20Sopenharmony_ci} 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistatic int find_child_rport(struct device *dev, void *data) 528c2ecf20Sopenharmony_ci{ 538c2ecf20Sopenharmony_ci struct device **child = data; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci if (scsi_is_srp_rport(dev)) { 568c2ecf20Sopenharmony_ci WARN_ON_ONCE(*child); 578c2ecf20Sopenharmony_ci *child = dev; 588c2ecf20Sopenharmony_ci } 598c2ecf20Sopenharmony_ci return 0; 608c2ecf20Sopenharmony_ci} 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic inline struct srp_rport *shost_to_rport(struct Scsi_Host *shost) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci struct device *child = NULL; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci WARN_ON_ONCE(device_for_each_child(&shost->shost_gendev, &child, 678c2ecf20Sopenharmony_ci find_child_rport) < 0); 688c2ecf20Sopenharmony_ci return child ? dev_to_rport(child) : NULL; 698c2ecf20Sopenharmony_ci} 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci/** 728c2ecf20Sopenharmony_ci * srp_tmo_valid() - check timeout combination validity 738c2ecf20Sopenharmony_ci * @reconnect_delay: Reconnect delay in seconds. 748c2ecf20Sopenharmony_ci * @fast_io_fail_tmo: Fast I/O fail timeout in seconds. 758c2ecf20Sopenharmony_ci * @dev_loss_tmo: Device loss timeout in seconds. 768c2ecf20Sopenharmony_ci * 778c2ecf20Sopenharmony_ci * The combination of the timeout parameters must be such that SCSI commands 788c2ecf20Sopenharmony_ci * are finished in a reasonable time. Hence do not allow the fast I/O fail 798c2ecf20Sopenharmony_ci * timeout to exceed SCSI_DEVICE_BLOCK_MAX_TIMEOUT nor allow dev_loss_tmo to 808c2ecf20Sopenharmony_ci * exceed that limit if failing I/O fast has been disabled. Furthermore, these 818c2ecf20Sopenharmony_ci * parameters must be such that multipath can detect failed paths timely. 828c2ecf20Sopenharmony_ci * Hence do not allow all three parameters to be disabled simultaneously. 838c2ecf20Sopenharmony_ci */ 848c2ecf20Sopenharmony_ciint srp_tmo_valid(int reconnect_delay, int fast_io_fail_tmo, long dev_loss_tmo) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci if (reconnect_delay < 0 && fast_io_fail_tmo < 0 && dev_loss_tmo < 0) 878c2ecf20Sopenharmony_ci return -EINVAL; 888c2ecf20Sopenharmony_ci if (reconnect_delay == 0) 898c2ecf20Sopenharmony_ci return -EINVAL; 908c2ecf20Sopenharmony_ci if (fast_io_fail_tmo > SCSI_DEVICE_BLOCK_MAX_TIMEOUT) 918c2ecf20Sopenharmony_ci return -EINVAL; 928c2ecf20Sopenharmony_ci if (fast_io_fail_tmo < 0 && 938c2ecf20Sopenharmony_ci dev_loss_tmo > SCSI_DEVICE_BLOCK_MAX_TIMEOUT) 948c2ecf20Sopenharmony_ci return -EINVAL; 958c2ecf20Sopenharmony_ci if (dev_loss_tmo >= LONG_MAX / HZ) 968c2ecf20Sopenharmony_ci return -EINVAL; 978c2ecf20Sopenharmony_ci if (fast_io_fail_tmo >= 0 && dev_loss_tmo >= 0 && 988c2ecf20Sopenharmony_ci fast_io_fail_tmo >= dev_loss_tmo) 998c2ecf20Sopenharmony_ci return -EINVAL; 1008c2ecf20Sopenharmony_ci return 0; 1018c2ecf20Sopenharmony_ci} 1028c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(srp_tmo_valid); 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_cistatic int srp_host_setup(struct transport_container *tc, struct device *dev, 1058c2ecf20Sopenharmony_ci struct device *cdev) 1068c2ecf20Sopenharmony_ci{ 1078c2ecf20Sopenharmony_ci struct Scsi_Host *shost = dev_to_shost(dev); 1088c2ecf20Sopenharmony_ci struct srp_host_attrs *srp_host = to_srp_host_attrs(shost); 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci atomic_set(&srp_host->next_port_id, 0); 1118c2ecf20Sopenharmony_ci return 0; 1128c2ecf20Sopenharmony_ci} 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_cistatic DECLARE_TRANSPORT_CLASS(srp_host_class, "srp_host", srp_host_setup, 1158c2ecf20Sopenharmony_ci NULL, NULL); 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cistatic DECLARE_TRANSPORT_CLASS(srp_rport_class, "srp_remote_ports", 1188c2ecf20Sopenharmony_ci NULL, NULL, NULL); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistatic ssize_t 1218c2ecf20Sopenharmony_cishow_srp_rport_id(struct device *dev, struct device_attribute *attr, 1228c2ecf20Sopenharmony_ci char *buf) 1238c2ecf20Sopenharmony_ci{ 1248c2ecf20Sopenharmony_ci struct srp_rport *rport = transport_class_to_srp_rport(dev); 1258c2ecf20Sopenharmony_ci return sprintf(buf, "%16phC\n", rport->port_id); 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cistatic DEVICE_ATTR(port_id, S_IRUGO, show_srp_rport_id, NULL); 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cistatic const struct { 1318c2ecf20Sopenharmony_ci u32 value; 1328c2ecf20Sopenharmony_ci char *name; 1338c2ecf20Sopenharmony_ci} srp_rport_role_names[] = { 1348c2ecf20Sopenharmony_ci {SRP_RPORT_ROLE_INITIATOR, "SRP Initiator"}, 1358c2ecf20Sopenharmony_ci {SRP_RPORT_ROLE_TARGET, "SRP Target"}, 1368c2ecf20Sopenharmony_ci}; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_cistatic ssize_t 1398c2ecf20Sopenharmony_cishow_srp_rport_roles(struct device *dev, struct device_attribute *attr, 1408c2ecf20Sopenharmony_ci char *buf) 1418c2ecf20Sopenharmony_ci{ 1428c2ecf20Sopenharmony_ci struct srp_rport *rport = transport_class_to_srp_rport(dev); 1438c2ecf20Sopenharmony_ci int i; 1448c2ecf20Sopenharmony_ci char *name = NULL; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(srp_rport_role_names); i++) 1478c2ecf20Sopenharmony_ci if (srp_rport_role_names[i].value == rport->roles) { 1488c2ecf20Sopenharmony_ci name = srp_rport_role_names[i].name; 1498c2ecf20Sopenharmony_ci break; 1508c2ecf20Sopenharmony_ci } 1518c2ecf20Sopenharmony_ci return sprintf(buf, "%s\n", name ? : "unknown"); 1528c2ecf20Sopenharmony_ci} 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_cistatic DEVICE_ATTR(roles, S_IRUGO, show_srp_rport_roles, NULL); 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_cistatic ssize_t store_srp_rport_delete(struct device *dev, 1578c2ecf20Sopenharmony_ci struct device_attribute *attr, 1588c2ecf20Sopenharmony_ci const char *buf, size_t count) 1598c2ecf20Sopenharmony_ci{ 1608c2ecf20Sopenharmony_ci struct srp_rport *rport = transport_class_to_srp_rport(dev); 1618c2ecf20Sopenharmony_ci struct Scsi_Host *shost = dev_to_shost(dev); 1628c2ecf20Sopenharmony_ci struct srp_internal *i = to_srp_internal(shost->transportt); 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci if (i->f->rport_delete) { 1658c2ecf20Sopenharmony_ci i->f->rport_delete(rport); 1668c2ecf20Sopenharmony_ci return count; 1678c2ecf20Sopenharmony_ci } else { 1688c2ecf20Sopenharmony_ci return -ENOSYS; 1698c2ecf20Sopenharmony_ci } 1708c2ecf20Sopenharmony_ci} 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_cistatic DEVICE_ATTR(delete, S_IWUSR, NULL, store_srp_rport_delete); 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_cistatic ssize_t show_srp_rport_state(struct device *dev, 1758c2ecf20Sopenharmony_ci struct device_attribute *attr, 1768c2ecf20Sopenharmony_ci char *buf) 1778c2ecf20Sopenharmony_ci{ 1788c2ecf20Sopenharmony_ci static const char *const state_name[] = { 1798c2ecf20Sopenharmony_ci [SRP_RPORT_RUNNING] = "running", 1808c2ecf20Sopenharmony_ci [SRP_RPORT_BLOCKED] = "blocked", 1818c2ecf20Sopenharmony_ci [SRP_RPORT_FAIL_FAST] = "fail-fast", 1828c2ecf20Sopenharmony_ci [SRP_RPORT_LOST] = "lost", 1838c2ecf20Sopenharmony_ci }; 1848c2ecf20Sopenharmony_ci struct srp_rport *rport = transport_class_to_srp_rport(dev); 1858c2ecf20Sopenharmony_ci enum srp_rport_state state = rport->state; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci return sprintf(buf, "%s\n", 1888c2ecf20Sopenharmony_ci (unsigned)state < ARRAY_SIZE(state_name) ? 1898c2ecf20Sopenharmony_ci state_name[state] : "???"); 1908c2ecf20Sopenharmony_ci} 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_cistatic DEVICE_ATTR(state, S_IRUGO, show_srp_rport_state, NULL); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_cistatic ssize_t srp_show_tmo(char *buf, int tmo) 1958c2ecf20Sopenharmony_ci{ 1968c2ecf20Sopenharmony_ci return tmo >= 0 ? sprintf(buf, "%d\n", tmo) : sprintf(buf, "off\n"); 1978c2ecf20Sopenharmony_ci} 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ciint srp_parse_tmo(int *tmo, const char *buf) 2008c2ecf20Sopenharmony_ci{ 2018c2ecf20Sopenharmony_ci int res = 0; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci if (strncmp(buf, "off", 3) != 0) 2048c2ecf20Sopenharmony_ci res = kstrtoint(buf, 0, tmo); 2058c2ecf20Sopenharmony_ci else 2068c2ecf20Sopenharmony_ci *tmo = -1; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci return res; 2098c2ecf20Sopenharmony_ci} 2108c2ecf20Sopenharmony_ciEXPORT_SYMBOL(srp_parse_tmo); 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_cistatic ssize_t show_reconnect_delay(struct device *dev, 2138c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 2148c2ecf20Sopenharmony_ci{ 2158c2ecf20Sopenharmony_ci struct srp_rport *rport = transport_class_to_srp_rport(dev); 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci return srp_show_tmo(buf, rport->reconnect_delay); 2188c2ecf20Sopenharmony_ci} 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_cistatic ssize_t store_reconnect_delay(struct device *dev, 2218c2ecf20Sopenharmony_ci struct device_attribute *attr, 2228c2ecf20Sopenharmony_ci const char *buf, const size_t count) 2238c2ecf20Sopenharmony_ci{ 2248c2ecf20Sopenharmony_ci struct srp_rport *rport = transport_class_to_srp_rport(dev); 2258c2ecf20Sopenharmony_ci int res, delay; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci res = srp_parse_tmo(&delay, buf); 2288c2ecf20Sopenharmony_ci if (res) 2298c2ecf20Sopenharmony_ci goto out; 2308c2ecf20Sopenharmony_ci res = srp_tmo_valid(delay, rport->fast_io_fail_tmo, 2318c2ecf20Sopenharmony_ci rport->dev_loss_tmo); 2328c2ecf20Sopenharmony_ci if (res) 2338c2ecf20Sopenharmony_ci goto out; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci if (rport->reconnect_delay <= 0 && delay > 0 && 2368c2ecf20Sopenharmony_ci rport->state != SRP_RPORT_RUNNING) { 2378c2ecf20Sopenharmony_ci queue_delayed_work(system_long_wq, &rport->reconnect_work, 2388c2ecf20Sopenharmony_ci delay * HZ); 2398c2ecf20Sopenharmony_ci } else if (delay <= 0) { 2408c2ecf20Sopenharmony_ci cancel_delayed_work(&rport->reconnect_work); 2418c2ecf20Sopenharmony_ci } 2428c2ecf20Sopenharmony_ci rport->reconnect_delay = delay; 2438c2ecf20Sopenharmony_ci res = count; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ciout: 2468c2ecf20Sopenharmony_ci return res; 2478c2ecf20Sopenharmony_ci} 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_cistatic DEVICE_ATTR(reconnect_delay, S_IRUGO | S_IWUSR, show_reconnect_delay, 2508c2ecf20Sopenharmony_ci store_reconnect_delay); 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_cistatic ssize_t show_failed_reconnects(struct device *dev, 2538c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 2548c2ecf20Sopenharmony_ci{ 2558c2ecf20Sopenharmony_ci struct srp_rport *rport = transport_class_to_srp_rport(dev); 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", rport->failed_reconnects); 2588c2ecf20Sopenharmony_ci} 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_cistatic DEVICE_ATTR(failed_reconnects, S_IRUGO, show_failed_reconnects, NULL); 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_cistatic ssize_t show_srp_rport_fast_io_fail_tmo(struct device *dev, 2638c2ecf20Sopenharmony_ci struct device_attribute *attr, 2648c2ecf20Sopenharmony_ci char *buf) 2658c2ecf20Sopenharmony_ci{ 2668c2ecf20Sopenharmony_ci struct srp_rport *rport = transport_class_to_srp_rport(dev); 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci return srp_show_tmo(buf, rport->fast_io_fail_tmo); 2698c2ecf20Sopenharmony_ci} 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_cistatic ssize_t store_srp_rport_fast_io_fail_tmo(struct device *dev, 2728c2ecf20Sopenharmony_ci struct device_attribute *attr, 2738c2ecf20Sopenharmony_ci const char *buf, size_t count) 2748c2ecf20Sopenharmony_ci{ 2758c2ecf20Sopenharmony_ci struct srp_rport *rport = transport_class_to_srp_rport(dev); 2768c2ecf20Sopenharmony_ci int res; 2778c2ecf20Sopenharmony_ci int fast_io_fail_tmo; 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci res = srp_parse_tmo(&fast_io_fail_tmo, buf); 2808c2ecf20Sopenharmony_ci if (res) 2818c2ecf20Sopenharmony_ci goto out; 2828c2ecf20Sopenharmony_ci res = srp_tmo_valid(rport->reconnect_delay, fast_io_fail_tmo, 2838c2ecf20Sopenharmony_ci rport->dev_loss_tmo); 2848c2ecf20Sopenharmony_ci if (res) 2858c2ecf20Sopenharmony_ci goto out; 2868c2ecf20Sopenharmony_ci rport->fast_io_fail_tmo = fast_io_fail_tmo; 2878c2ecf20Sopenharmony_ci res = count; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ciout: 2908c2ecf20Sopenharmony_ci return res; 2918c2ecf20Sopenharmony_ci} 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_cistatic DEVICE_ATTR(fast_io_fail_tmo, S_IRUGO | S_IWUSR, 2948c2ecf20Sopenharmony_ci show_srp_rport_fast_io_fail_tmo, 2958c2ecf20Sopenharmony_ci store_srp_rport_fast_io_fail_tmo); 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_cistatic ssize_t show_srp_rport_dev_loss_tmo(struct device *dev, 2988c2ecf20Sopenharmony_ci struct device_attribute *attr, 2998c2ecf20Sopenharmony_ci char *buf) 3008c2ecf20Sopenharmony_ci{ 3018c2ecf20Sopenharmony_ci struct srp_rport *rport = transport_class_to_srp_rport(dev); 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci return srp_show_tmo(buf, rport->dev_loss_tmo); 3048c2ecf20Sopenharmony_ci} 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_cistatic ssize_t store_srp_rport_dev_loss_tmo(struct device *dev, 3078c2ecf20Sopenharmony_ci struct device_attribute *attr, 3088c2ecf20Sopenharmony_ci const char *buf, size_t count) 3098c2ecf20Sopenharmony_ci{ 3108c2ecf20Sopenharmony_ci struct srp_rport *rport = transport_class_to_srp_rport(dev); 3118c2ecf20Sopenharmony_ci int res; 3128c2ecf20Sopenharmony_ci int dev_loss_tmo; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci res = srp_parse_tmo(&dev_loss_tmo, buf); 3158c2ecf20Sopenharmony_ci if (res) 3168c2ecf20Sopenharmony_ci goto out; 3178c2ecf20Sopenharmony_ci res = srp_tmo_valid(rport->reconnect_delay, rport->fast_io_fail_tmo, 3188c2ecf20Sopenharmony_ci dev_loss_tmo); 3198c2ecf20Sopenharmony_ci if (res) 3208c2ecf20Sopenharmony_ci goto out; 3218c2ecf20Sopenharmony_ci rport->dev_loss_tmo = dev_loss_tmo; 3228c2ecf20Sopenharmony_ci res = count; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ciout: 3258c2ecf20Sopenharmony_ci return res; 3268c2ecf20Sopenharmony_ci} 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_cistatic DEVICE_ATTR(dev_loss_tmo, S_IRUGO | S_IWUSR, 3298c2ecf20Sopenharmony_ci show_srp_rport_dev_loss_tmo, 3308c2ecf20Sopenharmony_ci store_srp_rport_dev_loss_tmo); 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_cistatic int srp_rport_set_state(struct srp_rport *rport, 3338c2ecf20Sopenharmony_ci enum srp_rport_state new_state) 3348c2ecf20Sopenharmony_ci{ 3358c2ecf20Sopenharmony_ci enum srp_rport_state old_state = rport->state; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci lockdep_assert_held(&rport->mutex); 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci switch (new_state) { 3408c2ecf20Sopenharmony_ci case SRP_RPORT_RUNNING: 3418c2ecf20Sopenharmony_ci switch (old_state) { 3428c2ecf20Sopenharmony_ci case SRP_RPORT_LOST: 3438c2ecf20Sopenharmony_ci goto invalid; 3448c2ecf20Sopenharmony_ci default: 3458c2ecf20Sopenharmony_ci break; 3468c2ecf20Sopenharmony_ci } 3478c2ecf20Sopenharmony_ci break; 3488c2ecf20Sopenharmony_ci case SRP_RPORT_BLOCKED: 3498c2ecf20Sopenharmony_ci switch (old_state) { 3508c2ecf20Sopenharmony_ci case SRP_RPORT_RUNNING: 3518c2ecf20Sopenharmony_ci break; 3528c2ecf20Sopenharmony_ci default: 3538c2ecf20Sopenharmony_ci goto invalid; 3548c2ecf20Sopenharmony_ci } 3558c2ecf20Sopenharmony_ci break; 3568c2ecf20Sopenharmony_ci case SRP_RPORT_FAIL_FAST: 3578c2ecf20Sopenharmony_ci switch (old_state) { 3588c2ecf20Sopenharmony_ci case SRP_RPORT_LOST: 3598c2ecf20Sopenharmony_ci goto invalid; 3608c2ecf20Sopenharmony_ci default: 3618c2ecf20Sopenharmony_ci break; 3628c2ecf20Sopenharmony_ci } 3638c2ecf20Sopenharmony_ci break; 3648c2ecf20Sopenharmony_ci case SRP_RPORT_LOST: 3658c2ecf20Sopenharmony_ci break; 3668c2ecf20Sopenharmony_ci } 3678c2ecf20Sopenharmony_ci rport->state = new_state; 3688c2ecf20Sopenharmony_ci return 0; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ciinvalid: 3718c2ecf20Sopenharmony_ci return -EINVAL; 3728c2ecf20Sopenharmony_ci} 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci/** 3758c2ecf20Sopenharmony_ci * srp_reconnect_work() - reconnect and schedule a new attempt if necessary 3768c2ecf20Sopenharmony_ci * @work: Work structure used for scheduling this operation. 3778c2ecf20Sopenharmony_ci */ 3788c2ecf20Sopenharmony_cistatic void srp_reconnect_work(struct work_struct *work) 3798c2ecf20Sopenharmony_ci{ 3808c2ecf20Sopenharmony_ci struct srp_rport *rport = container_of(to_delayed_work(work), 3818c2ecf20Sopenharmony_ci struct srp_rport, reconnect_work); 3828c2ecf20Sopenharmony_ci struct Scsi_Host *shost = rport_to_shost(rport); 3838c2ecf20Sopenharmony_ci int delay, res; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci res = srp_reconnect_rport(rport); 3868c2ecf20Sopenharmony_ci if (res != 0) { 3878c2ecf20Sopenharmony_ci shost_printk(KERN_ERR, shost, 3888c2ecf20Sopenharmony_ci "reconnect attempt %d failed (%d)\n", 3898c2ecf20Sopenharmony_ci ++rport->failed_reconnects, res); 3908c2ecf20Sopenharmony_ci delay = rport->reconnect_delay * 3918c2ecf20Sopenharmony_ci min(100, max(1, rport->failed_reconnects - 10)); 3928c2ecf20Sopenharmony_ci if (delay > 0) 3938c2ecf20Sopenharmony_ci queue_delayed_work(system_long_wq, 3948c2ecf20Sopenharmony_ci &rport->reconnect_work, delay * HZ); 3958c2ecf20Sopenharmony_ci } 3968c2ecf20Sopenharmony_ci} 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci/* 3998c2ecf20Sopenharmony_ci * scsi_target_block() must have been called before this function is 4008c2ecf20Sopenharmony_ci * called to guarantee that no .queuecommand() calls are in progress. 4018c2ecf20Sopenharmony_ci */ 4028c2ecf20Sopenharmony_cistatic void __rport_fail_io_fast(struct srp_rport *rport) 4038c2ecf20Sopenharmony_ci{ 4048c2ecf20Sopenharmony_ci struct Scsi_Host *shost = rport_to_shost(rport); 4058c2ecf20Sopenharmony_ci struct srp_internal *i; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci lockdep_assert_held(&rport->mutex); 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci if (srp_rport_set_state(rport, SRP_RPORT_FAIL_FAST)) 4108c2ecf20Sopenharmony_ci return; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci scsi_target_unblock(rport->dev.parent, SDEV_TRANSPORT_OFFLINE); 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci /* Involve the LLD if possible to terminate all I/O on the rport. */ 4158c2ecf20Sopenharmony_ci i = to_srp_internal(shost->transportt); 4168c2ecf20Sopenharmony_ci if (i->f->terminate_rport_io) 4178c2ecf20Sopenharmony_ci i->f->terminate_rport_io(rport); 4188c2ecf20Sopenharmony_ci} 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci/** 4218c2ecf20Sopenharmony_ci * rport_fast_io_fail_timedout() - fast I/O failure timeout handler 4228c2ecf20Sopenharmony_ci * @work: Work structure used for scheduling this operation. 4238c2ecf20Sopenharmony_ci */ 4248c2ecf20Sopenharmony_cistatic void rport_fast_io_fail_timedout(struct work_struct *work) 4258c2ecf20Sopenharmony_ci{ 4268c2ecf20Sopenharmony_ci struct srp_rport *rport = container_of(to_delayed_work(work), 4278c2ecf20Sopenharmony_ci struct srp_rport, fast_io_fail_work); 4288c2ecf20Sopenharmony_ci struct Scsi_Host *shost = rport_to_shost(rport); 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci pr_info("fast_io_fail_tmo expired for SRP %s / %s.\n", 4318c2ecf20Sopenharmony_ci dev_name(&rport->dev), dev_name(&shost->shost_gendev)); 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci mutex_lock(&rport->mutex); 4348c2ecf20Sopenharmony_ci if (rport->state == SRP_RPORT_BLOCKED) 4358c2ecf20Sopenharmony_ci __rport_fail_io_fast(rport); 4368c2ecf20Sopenharmony_ci mutex_unlock(&rport->mutex); 4378c2ecf20Sopenharmony_ci} 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci/** 4408c2ecf20Sopenharmony_ci * rport_dev_loss_timedout() - device loss timeout handler 4418c2ecf20Sopenharmony_ci * @work: Work structure used for scheduling this operation. 4428c2ecf20Sopenharmony_ci */ 4438c2ecf20Sopenharmony_cistatic void rport_dev_loss_timedout(struct work_struct *work) 4448c2ecf20Sopenharmony_ci{ 4458c2ecf20Sopenharmony_ci struct srp_rport *rport = container_of(to_delayed_work(work), 4468c2ecf20Sopenharmony_ci struct srp_rport, dev_loss_work); 4478c2ecf20Sopenharmony_ci struct Scsi_Host *shost = rport_to_shost(rport); 4488c2ecf20Sopenharmony_ci struct srp_internal *i = to_srp_internal(shost->transportt); 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci pr_info("dev_loss_tmo expired for SRP %s / %s.\n", 4518c2ecf20Sopenharmony_ci dev_name(&rport->dev), dev_name(&shost->shost_gendev)); 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci mutex_lock(&rport->mutex); 4548c2ecf20Sopenharmony_ci WARN_ON(srp_rport_set_state(rport, SRP_RPORT_LOST) != 0); 4558c2ecf20Sopenharmony_ci scsi_target_unblock(rport->dev.parent, SDEV_TRANSPORT_OFFLINE); 4568c2ecf20Sopenharmony_ci mutex_unlock(&rport->mutex); 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci i->f->rport_delete(rport); 4598c2ecf20Sopenharmony_ci} 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_cistatic void __srp_start_tl_fail_timers(struct srp_rport *rport) 4628c2ecf20Sopenharmony_ci{ 4638c2ecf20Sopenharmony_ci struct Scsi_Host *shost = rport_to_shost(rport); 4648c2ecf20Sopenharmony_ci int delay, fast_io_fail_tmo, dev_loss_tmo; 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci lockdep_assert_held(&rport->mutex); 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci delay = rport->reconnect_delay; 4698c2ecf20Sopenharmony_ci fast_io_fail_tmo = rport->fast_io_fail_tmo; 4708c2ecf20Sopenharmony_ci dev_loss_tmo = rport->dev_loss_tmo; 4718c2ecf20Sopenharmony_ci pr_debug("%s current state: %d\n", dev_name(&shost->shost_gendev), 4728c2ecf20Sopenharmony_ci rport->state); 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci if (rport->state == SRP_RPORT_LOST) 4758c2ecf20Sopenharmony_ci return; 4768c2ecf20Sopenharmony_ci if (delay > 0) 4778c2ecf20Sopenharmony_ci queue_delayed_work(system_long_wq, &rport->reconnect_work, 4788c2ecf20Sopenharmony_ci 1UL * delay * HZ); 4798c2ecf20Sopenharmony_ci if ((fast_io_fail_tmo >= 0 || dev_loss_tmo >= 0) && 4808c2ecf20Sopenharmony_ci srp_rport_set_state(rport, SRP_RPORT_BLOCKED) == 0) { 4818c2ecf20Sopenharmony_ci pr_debug("%s new state: %d\n", dev_name(&shost->shost_gendev), 4828c2ecf20Sopenharmony_ci rport->state); 4838c2ecf20Sopenharmony_ci scsi_target_block(&shost->shost_gendev); 4848c2ecf20Sopenharmony_ci if (fast_io_fail_tmo >= 0) 4858c2ecf20Sopenharmony_ci queue_delayed_work(system_long_wq, 4868c2ecf20Sopenharmony_ci &rport->fast_io_fail_work, 4878c2ecf20Sopenharmony_ci 1UL * fast_io_fail_tmo * HZ); 4888c2ecf20Sopenharmony_ci if (dev_loss_tmo >= 0) 4898c2ecf20Sopenharmony_ci queue_delayed_work(system_long_wq, 4908c2ecf20Sopenharmony_ci &rport->dev_loss_work, 4918c2ecf20Sopenharmony_ci 1UL * dev_loss_tmo * HZ); 4928c2ecf20Sopenharmony_ci } 4938c2ecf20Sopenharmony_ci} 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci/** 4968c2ecf20Sopenharmony_ci * srp_start_tl_fail_timers() - start the transport layer failure timers 4978c2ecf20Sopenharmony_ci * @rport: SRP target port. 4988c2ecf20Sopenharmony_ci * 4998c2ecf20Sopenharmony_ci * Start the transport layer fast I/O failure and device loss timers. Do not 5008c2ecf20Sopenharmony_ci * modify a timer that was already started. 5018c2ecf20Sopenharmony_ci */ 5028c2ecf20Sopenharmony_civoid srp_start_tl_fail_timers(struct srp_rport *rport) 5038c2ecf20Sopenharmony_ci{ 5048c2ecf20Sopenharmony_ci mutex_lock(&rport->mutex); 5058c2ecf20Sopenharmony_ci __srp_start_tl_fail_timers(rport); 5068c2ecf20Sopenharmony_ci mutex_unlock(&rport->mutex); 5078c2ecf20Sopenharmony_ci} 5088c2ecf20Sopenharmony_ciEXPORT_SYMBOL(srp_start_tl_fail_timers); 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci/** 5118c2ecf20Sopenharmony_ci * srp_reconnect_rport() - reconnect to an SRP target port 5128c2ecf20Sopenharmony_ci * @rport: SRP target port. 5138c2ecf20Sopenharmony_ci * 5148c2ecf20Sopenharmony_ci * Blocks SCSI command queueing before invoking reconnect() such that 5158c2ecf20Sopenharmony_ci * queuecommand() won't be invoked concurrently with reconnect() from outside 5168c2ecf20Sopenharmony_ci * the SCSI EH. This is important since a reconnect() implementation may 5178c2ecf20Sopenharmony_ci * reallocate resources needed by queuecommand(). 5188c2ecf20Sopenharmony_ci * 5198c2ecf20Sopenharmony_ci * Notes: 5208c2ecf20Sopenharmony_ci * - This function neither waits until outstanding requests have finished nor 5218c2ecf20Sopenharmony_ci * tries to abort these. It is the responsibility of the reconnect() 5228c2ecf20Sopenharmony_ci * function to finish outstanding commands before reconnecting to the target 5238c2ecf20Sopenharmony_ci * port. 5248c2ecf20Sopenharmony_ci * - It is the responsibility of the caller to ensure that the resources 5258c2ecf20Sopenharmony_ci * reallocated by the reconnect() function won't be used while this function 5268c2ecf20Sopenharmony_ci * is in progress. One possible strategy is to invoke this function from 5278c2ecf20Sopenharmony_ci * the context of the SCSI EH thread only. Another possible strategy is to 5288c2ecf20Sopenharmony_ci * lock the rport mutex inside each SCSI LLD callback that can be invoked by 5298c2ecf20Sopenharmony_ci * the SCSI EH (the scsi_host_template.eh_*() functions and also the 5308c2ecf20Sopenharmony_ci * scsi_host_template.queuecommand() function). 5318c2ecf20Sopenharmony_ci */ 5328c2ecf20Sopenharmony_ciint srp_reconnect_rport(struct srp_rport *rport) 5338c2ecf20Sopenharmony_ci{ 5348c2ecf20Sopenharmony_ci struct Scsi_Host *shost = rport_to_shost(rport); 5358c2ecf20Sopenharmony_ci struct srp_internal *i = to_srp_internal(shost->transportt); 5368c2ecf20Sopenharmony_ci struct scsi_device *sdev; 5378c2ecf20Sopenharmony_ci int res; 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci pr_debug("SCSI host %s\n", dev_name(&shost->shost_gendev)); 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci res = mutex_lock_interruptible(&rport->mutex); 5428c2ecf20Sopenharmony_ci if (res) 5438c2ecf20Sopenharmony_ci goto out; 5448c2ecf20Sopenharmony_ci if (rport->state != SRP_RPORT_FAIL_FAST && rport->state != SRP_RPORT_LOST) 5458c2ecf20Sopenharmony_ci /* 5468c2ecf20Sopenharmony_ci * sdev state must be SDEV_TRANSPORT_OFFLINE, transition 5478c2ecf20Sopenharmony_ci * to SDEV_BLOCK is illegal. Calling scsi_target_unblock() 5488c2ecf20Sopenharmony_ci * later is ok though, scsi_internal_device_unblock_nowait() 5498c2ecf20Sopenharmony_ci * treats SDEV_TRANSPORT_OFFLINE like SDEV_BLOCK. 5508c2ecf20Sopenharmony_ci */ 5518c2ecf20Sopenharmony_ci scsi_target_block(&shost->shost_gendev); 5528c2ecf20Sopenharmony_ci res = rport->state != SRP_RPORT_LOST ? i->f->reconnect(rport) : -ENODEV; 5538c2ecf20Sopenharmony_ci pr_debug("%s (state %d): transport.reconnect() returned %d\n", 5548c2ecf20Sopenharmony_ci dev_name(&shost->shost_gendev), rport->state, res); 5558c2ecf20Sopenharmony_ci if (res == 0) { 5568c2ecf20Sopenharmony_ci cancel_delayed_work(&rport->fast_io_fail_work); 5578c2ecf20Sopenharmony_ci cancel_delayed_work(&rport->dev_loss_work); 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci rport->failed_reconnects = 0; 5608c2ecf20Sopenharmony_ci srp_rport_set_state(rport, SRP_RPORT_RUNNING); 5618c2ecf20Sopenharmony_ci scsi_target_unblock(&shost->shost_gendev, SDEV_RUNNING); 5628c2ecf20Sopenharmony_ci /* 5638c2ecf20Sopenharmony_ci * If the SCSI error handler has offlined one or more devices, 5648c2ecf20Sopenharmony_ci * invoking scsi_target_unblock() won't change the state of 5658c2ecf20Sopenharmony_ci * these devices into running so do that explicitly. 5668c2ecf20Sopenharmony_ci */ 5678c2ecf20Sopenharmony_ci shost_for_each_device(sdev, shost) { 5688c2ecf20Sopenharmony_ci mutex_lock(&sdev->state_mutex); 5698c2ecf20Sopenharmony_ci if (sdev->sdev_state == SDEV_OFFLINE) 5708c2ecf20Sopenharmony_ci sdev->sdev_state = SDEV_RUNNING; 5718c2ecf20Sopenharmony_ci mutex_unlock(&sdev->state_mutex); 5728c2ecf20Sopenharmony_ci } 5738c2ecf20Sopenharmony_ci } else if (rport->state == SRP_RPORT_RUNNING) { 5748c2ecf20Sopenharmony_ci /* 5758c2ecf20Sopenharmony_ci * srp_reconnect_rport() has been invoked with fast_io_fail 5768c2ecf20Sopenharmony_ci * and dev_loss off. Mark the port as failed and start the TL 5778c2ecf20Sopenharmony_ci * failure timers if these had not yet been started. 5788c2ecf20Sopenharmony_ci */ 5798c2ecf20Sopenharmony_ci __rport_fail_io_fast(rport); 5808c2ecf20Sopenharmony_ci __srp_start_tl_fail_timers(rport); 5818c2ecf20Sopenharmony_ci } else if (rport->state != SRP_RPORT_BLOCKED) { 5828c2ecf20Sopenharmony_ci scsi_target_unblock(&shost->shost_gendev, 5838c2ecf20Sopenharmony_ci SDEV_TRANSPORT_OFFLINE); 5848c2ecf20Sopenharmony_ci } 5858c2ecf20Sopenharmony_ci mutex_unlock(&rport->mutex); 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ciout: 5888c2ecf20Sopenharmony_ci return res; 5898c2ecf20Sopenharmony_ci} 5908c2ecf20Sopenharmony_ciEXPORT_SYMBOL(srp_reconnect_rport); 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci/** 5938c2ecf20Sopenharmony_ci * srp_timed_out() - SRP transport intercept of the SCSI timeout EH 5948c2ecf20Sopenharmony_ci * @scmd: SCSI command. 5958c2ecf20Sopenharmony_ci * 5968c2ecf20Sopenharmony_ci * If a timeout occurs while an rport is in the blocked state, ask the SCSI 5978c2ecf20Sopenharmony_ci * EH to continue waiting (BLK_EH_RESET_TIMER). Otherwise let the SCSI core 5988c2ecf20Sopenharmony_ci * handle the timeout (BLK_EH_DONE). 5998c2ecf20Sopenharmony_ci * 6008c2ecf20Sopenharmony_ci * Note: This function is called from soft-IRQ context and with the request 6018c2ecf20Sopenharmony_ci * queue lock held. 6028c2ecf20Sopenharmony_ci */ 6038c2ecf20Sopenharmony_cienum blk_eh_timer_return srp_timed_out(struct scsi_cmnd *scmd) 6048c2ecf20Sopenharmony_ci{ 6058c2ecf20Sopenharmony_ci struct scsi_device *sdev = scmd->device; 6068c2ecf20Sopenharmony_ci struct Scsi_Host *shost = sdev->host; 6078c2ecf20Sopenharmony_ci struct srp_internal *i = to_srp_internal(shost->transportt); 6088c2ecf20Sopenharmony_ci struct srp_rport *rport = shost_to_rport(shost); 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci pr_debug("timeout for sdev %s\n", dev_name(&sdev->sdev_gendev)); 6118c2ecf20Sopenharmony_ci return rport && rport->fast_io_fail_tmo < 0 && 6128c2ecf20Sopenharmony_ci rport->dev_loss_tmo < 0 && 6138c2ecf20Sopenharmony_ci i->f->reset_timer_if_blocked && scsi_device_blocked(sdev) ? 6148c2ecf20Sopenharmony_ci BLK_EH_RESET_TIMER : BLK_EH_DONE; 6158c2ecf20Sopenharmony_ci} 6168c2ecf20Sopenharmony_ciEXPORT_SYMBOL(srp_timed_out); 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_cistatic void srp_rport_release(struct device *dev) 6198c2ecf20Sopenharmony_ci{ 6208c2ecf20Sopenharmony_ci struct srp_rport *rport = dev_to_rport(dev); 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci put_device(dev->parent); 6238c2ecf20Sopenharmony_ci kfree(rport); 6248c2ecf20Sopenharmony_ci} 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_cistatic int scsi_is_srp_rport(const struct device *dev) 6278c2ecf20Sopenharmony_ci{ 6288c2ecf20Sopenharmony_ci return dev->release == srp_rport_release; 6298c2ecf20Sopenharmony_ci} 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_cistatic int srp_rport_match(struct attribute_container *cont, 6328c2ecf20Sopenharmony_ci struct device *dev) 6338c2ecf20Sopenharmony_ci{ 6348c2ecf20Sopenharmony_ci struct Scsi_Host *shost; 6358c2ecf20Sopenharmony_ci struct srp_internal *i; 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci if (!scsi_is_srp_rport(dev)) 6388c2ecf20Sopenharmony_ci return 0; 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci shost = dev_to_shost(dev->parent); 6418c2ecf20Sopenharmony_ci if (!shost->transportt) 6428c2ecf20Sopenharmony_ci return 0; 6438c2ecf20Sopenharmony_ci if (shost->transportt->host_attrs.ac.class != &srp_host_class.class) 6448c2ecf20Sopenharmony_ci return 0; 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci i = to_srp_internal(shost->transportt); 6478c2ecf20Sopenharmony_ci return &i->rport_attr_cont.ac == cont; 6488c2ecf20Sopenharmony_ci} 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_cistatic int srp_host_match(struct attribute_container *cont, struct device *dev) 6518c2ecf20Sopenharmony_ci{ 6528c2ecf20Sopenharmony_ci struct Scsi_Host *shost; 6538c2ecf20Sopenharmony_ci struct srp_internal *i; 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci if (!scsi_is_host_device(dev)) 6568c2ecf20Sopenharmony_ci return 0; 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci shost = dev_to_shost(dev); 6598c2ecf20Sopenharmony_ci if (!shost->transportt) 6608c2ecf20Sopenharmony_ci return 0; 6618c2ecf20Sopenharmony_ci if (shost->transportt->host_attrs.ac.class != &srp_host_class.class) 6628c2ecf20Sopenharmony_ci return 0; 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci i = to_srp_internal(shost->transportt); 6658c2ecf20Sopenharmony_ci return &i->t.host_attrs.ac == cont; 6668c2ecf20Sopenharmony_ci} 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci/** 6698c2ecf20Sopenharmony_ci * srp_rport_get() - increment rport reference count 6708c2ecf20Sopenharmony_ci * @rport: SRP target port. 6718c2ecf20Sopenharmony_ci */ 6728c2ecf20Sopenharmony_civoid srp_rport_get(struct srp_rport *rport) 6738c2ecf20Sopenharmony_ci{ 6748c2ecf20Sopenharmony_ci get_device(&rport->dev); 6758c2ecf20Sopenharmony_ci} 6768c2ecf20Sopenharmony_ciEXPORT_SYMBOL(srp_rport_get); 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci/** 6798c2ecf20Sopenharmony_ci * srp_rport_put() - decrement rport reference count 6808c2ecf20Sopenharmony_ci * @rport: SRP target port. 6818c2ecf20Sopenharmony_ci */ 6828c2ecf20Sopenharmony_civoid srp_rport_put(struct srp_rport *rport) 6838c2ecf20Sopenharmony_ci{ 6848c2ecf20Sopenharmony_ci put_device(&rport->dev); 6858c2ecf20Sopenharmony_ci} 6868c2ecf20Sopenharmony_ciEXPORT_SYMBOL(srp_rport_put); 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci/** 6898c2ecf20Sopenharmony_ci * srp_rport_add - add a SRP remote port to the device hierarchy 6908c2ecf20Sopenharmony_ci * @shost: scsi host the remote port is connected to. 6918c2ecf20Sopenharmony_ci * @ids: The port id for the remote port. 6928c2ecf20Sopenharmony_ci * 6938c2ecf20Sopenharmony_ci * Publishes a port to the rest of the system. 6948c2ecf20Sopenharmony_ci */ 6958c2ecf20Sopenharmony_cistruct srp_rport *srp_rport_add(struct Scsi_Host *shost, 6968c2ecf20Sopenharmony_ci struct srp_rport_identifiers *ids) 6978c2ecf20Sopenharmony_ci{ 6988c2ecf20Sopenharmony_ci struct srp_rport *rport; 6998c2ecf20Sopenharmony_ci struct device *parent = &shost->shost_gendev; 7008c2ecf20Sopenharmony_ci struct srp_internal *i = to_srp_internal(shost->transportt); 7018c2ecf20Sopenharmony_ci int id, ret; 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci rport = kzalloc(sizeof(*rport), GFP_KERNEL); 7048c2ecf20Sopenharmony_ci if (!rport) 7058c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci mutex_init(&rport->mutex); 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci device_initialize(&rport->dev); 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci rport->dev.parent = get_device(parent); 7128c2ecf20Sopenharmony_ci rport->dev.release = srp_rport_release; 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci memcpy(rport->port_id, ids->port_id, sizeof(rport->port_id)); 7158c2ecf20Sopenharmony_ci rport->roles = ids->roles; 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci if (i->f->reconnect) 7188c2ecf20Sopenharmony_ci rport->reconnect_delay = i->f->reconnect_delay ? 7198c2ecf20Sopenharmony_ci *i->f->reconnect_delay : 10; 7208c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&rport->reconnect_work, srp_reconnect_work); 7218c2ecf20Sopenharmony_ci rport->fast_io_fail_tmo = i->f->fast_io_fail_tmo ? 7228c2ecf20Sopenharmony_ci *i->f->fast_io_fail_tmo : 15; 7238c2ecf20Sopenharmony_ci rport->dev_loss_tmo = i->f->dev_loss_tmo ? *i->f->dev_loss_tmo : 60; 7248c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&rport->fast_io_fail_work, 7258c2ecf20Sopenharmony_ci rport_fast_io_fail_timedout); 7268c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&rport->dev_loss_work, rport_dev_loss_timedout); 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci id = atomic_inc_return(&to_srp_host_attrs(shost)->next_port_id); 7298c2ecf20Sopenharmony_ci dev_set_name(&rport->dev, "port-%d:%d", shost->host_no, id); 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci transport_setup_device(&rport->dev); 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci ret = device_add(&rport->dev); 7348c2ecf20Sopenharmony_ci if (ret) { 7358c2ecf20Sopenharmony_ci transport_destroy_device(&rport->dev); 7368c2ecf20Sopenharmony_ci put_device(&rport->dev); 7378c2ecf20Sopenharmony_ci return ERR_PTR(ret); 7388c2ecf20Sopenharmony_ci } 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci transport_add_device(&rport->dev); 7418c2ecf20Sopenharmony_ci transport_configure_device(&rport->dev); 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci return rport; 7448c2ecf20Sopenharmony_ci} 7458c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(srp_rport_add); 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci/** 7488c2ecf20Sopenharmony_ci * srp_rport_del - remove a SRP remote port 7498c2ecf20Sopenharmony_ci * @rport: SRP remote port to remove 7508c2ecf20Sopenharmony_ci * 7518c2ecf20Sopenharmony_ci * Removes the specified SRP remote port. 7528c2ecf20Sopenharmony_ci */ 7538c2ecf20Sopenharmony_civoid srp_rport_del(struct srp_rport *rport) 7548c2ecf20Sopenharmony_ci{ 7558c2ecf20Sopenharmony_ci struct device *dev = &rport->dev; 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ci transport_remove_device(dev); 7588c2ecf20Sopenharmony_ci device_del(dev); 7598c2ecf20Sopenharmony_ci transport_destroy_device(dev); 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci put_device(dev); 7628c2ecf20Sopenharmony_ci} 7638c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(srp_rport_del); 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_cistatic int do_srp_rport_del(struct device *dev, void *data) 7668c2ecf20Sopenharmony_ci{ 7678c2ecf20Sopenharmony_ci if (scsi_is_srp_rport(dev)) 7688c2ecf20Sopenharmony_ci srp_rport_del(dev_to_rport(dev)); 7698c2ecf20Sopenharmony_ci return 0; 7708c2ecf20Sopenharmony_ci} 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci/** 7738c2ecf20Sopenharmony_ci * srp_remove_host - tear down a Scsi_Host's SRP data structures 7748c2ecf20Sopenharmony_ci * @shost: Scsi Host that is torn down 7758c2ecf20Sopenharmony_ci * 7768c2ecf20Sopenharmony_ci * Removes all SRP remote ports for a given Scsi_Host. 7778c2ecf20Sopenharmony_ci * Must be called just before scsi_remove_host for SRP HBAs. 7788c2ecf20Sopenharmony_ci */ 7798c2ecf20Sopenharmony_civoid srp_remove_host(struct Scsi_Host *shost) 7808c2ecf20Sopenharmony_ci{ 7818c2ecf20Sopenharmony_ci device_for_each_child(&shost->shost_gendev, NULL, do_srp_rport_del); 7828c2ecf20Sopenharmony_ci} 7838c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(srp_remove_host); 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci/** 7868c2ecf20Sopenharmony_ci * srp_stop_rport_timers - stop the transport layer recovery timers 7878c2ecf20Sopenharmony_ci * @rport: SRP remote port for which to stop the timers. 7888c2ecf20Sopenharmony_ci * 7898c2ecf20Sopenharmony_ci * Must be called after srp_remove_host() and scsi_remove_host(). The caller 7908c2ecf20Sopenharmony_ci * must hold a reference on the rport (rport->dev) and on the SCSI host 7918c2ecf20Sopenharmony_ci * (rport->dev.parent). 7928c2ecf20Sopenharmony_ci */ 7938c2ecf20Sopenharmony_civoid srp_stop_rport_timers(struct srp_rport *rport) 7948c2ecf20Sopenharmony_ci{ 7958c2ecf20Sopenharmony_ci mutex_lock(&rport->mutex); 7968c2ecf20Sopenharmony_ci if (rport->state == SRP_RPORT_BLOCKED) 7978c2ecf20Sopenharmony_ci __rport_fail_io_fast(rport); 7988c2ecf20Sopenharmony_ci srp_rport_set_state(rport, SRP_RPORT_LOST); 7998c2ecf20Sopenharmony_ci mutex_unlock(&rport->mutex); 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&rport->reconnect_work); 8028c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&rport->fast_io_fail_work); 8038c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&rport->dev_loss_work); 8048c2ecf20Sopenharmony_ci} 8058c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(srp_stop_rport_timers); 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_ci/** 8088c2ecf20Sopenharmony_ci * srp_attach_transport - instantiate SRP transport template 8098c2ecf20Sopenharmony_ci * @ft: SRP transport class function template 8108c2ecf20Sopenharmony_ci */ 8118c2ecf20Sopenharmony_cistruct scsi_transport_template * 8128c2ecf20Sopenharmony_cisrp_attach_transport(struct srp_function_template *ft) 8138c2ecf20Sopenharmony_ci{ 8148c2ecf20Sopenharmony_ci int count; 8158c2ecf20Sopenharmony_ci struct srp_internal *i; 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci i = kzalloc(sizeof(*i), GFP_KERNEL); 8188c2ecf20Sopenharmony_ci if (!i) 8198c2ecf20Sopenharmony_ci return NULL; 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci i->t.host_size = sizeof(struct srp_host_attrs); 8228c2ecf20Sopenharmony_ci i->t.host_attrs.ac.attrs = &i->host_attrs[0]; 8238c2ecf20Sopenharmony_ci i->t.host_attrs.ac.class = &srp_host_class.class; 8248c2ecf20Sopenharmony_ci i->t.host_attrs.ac.match = srp_host_match; 8258c2ecf20Sopenharmony_ci i->host_attrs[0] = NULL; 8268c2ecf20Sopenharmony_ci transport_container_register(&i->t.host_attrs); 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_ci i->rport_attr_cont.ac.attrs = &i->rport_attrs[0]; 8298c2ecf20Sopenharmony_ci i->rport_attr_cont.ac.class = &srp_rport_class.class; 8308c2ecf20Sopenharmony_ci i->rport_attr_cont.ac.match = srp_rport_match; 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_ci count = 0; 8338c2ecf20Sopenharmony_ci i->rport_attrs[count++] = &dev_attr_port_id; 8348c2ecf20Sopenharmony_ci i->rport_attrs[count++] = &dev_attr_roles; 8358c2ecf20Sopenharmony_ci if (ft->has_rport_state) { 8368c2ecf20Sopenharmony_ci i->rport_attrs[count++] = &dev_attr_state; 8378c2ecf20Sopenharmony_ci i->rport_attrs[count++] = &dev_attr_fast_io_fail_tmo; 8388c2ecf20Sopenharmony_ci i->rport_attrs[count++] = &dev_attr_dev_loss_tmo; 8398c2ecf20Sopenharmony_ci } 8408c2ecf20Sopenharmony_ci if (ft->reconnect) { 8418c2ecf20Sopenharmony_ci i->rport_attrs[count++] = &dev_attr_reconnect_delay; 8428c2ecf20Sopenharmony_ci i->rport_attrs[count++] = &dev_attr_failed_reconnects; 8438c2ecf20Sopenharmony_ci } 8448c2ecf20Sopenharmony_ci if (ft->rport_delete) 8458c2ecf20Sopenharmony_ci i->rport_attrs[count++] = &dev_attr_delete; 8468c2ecf20Sopenharmony_ci i->rport_attrs[count++] = NULL; 8478c2ecf20Sopenharmony_ci BUG_ON(count > ARRAY_SIZE(i->rport_attrs)); 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_ci transport_container_register(&i->rport_attr_cont); 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci i->f = ft; 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci return &i->t; 8548c2ecf20Sopenharmony_ci} 8558c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(srp_attach_transport); 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_ci/** 8588c2ecf20Sopenharmony_ci * srp_release_transport - release SRP transport template instance 8598c2ecf20Sopenharmony_ci * @t: transport template instance 8608c2ecf20Sopenharmony_ci */ 8618c2ecf20Sopenharmony_civoid srp_release_transport(struct scsi_transport_template *t) 8628c2ecf20Sopenharmony_ci{ 8638c2ecf20Sopenharmony_ci struct srp_internal *i = to_srp_internal(t); 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_ci transport_container_unregister(&i->t.host_attrs); 8668c2ecf20Sopenharmony_ci transport_container_unregister(&i->rport_attr_cont); 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci kfree(i); 8698c2ecf20Sopenharmony_ci} 8708c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(srp_release_transport); 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_cistatic __init int srp_transport_init(void) 8738c2ecf20Sopenharmony_ci{ 8748c2ecf20Sopenharmony_ci int ret; 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_ci ret = transport_class_register(&srp_host_class); 8778c2ecf20Sopenharmony_ci if (ret) 8788c2ecf20Sopenharmony_ci return ret; 8798c2ecf20Sopenharmony_ci ret = transport_class_register(&srp_rport_class); 8808c2ecf20Sopenharmony_ci if (ret) 8818c2ecf20Sopenharmony_ci goto unregister_host_class; 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci return 0; 8848c2ecf20Sopenharmony_ciunregister_host_class: 8858c2ecf20Sopenharmony_ci transport_class_unregister(&srp_host_class); 8868c2ecf20Sopenharmony_ci return ret; 8878c2ecf20Sopenharmony_ci} 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_cistatic void __exit srp_transport_exit(void) 8908c2ecf20Sopenharmony_ci{ 8918c2ecf20Sopenharmony_ci transport_class_unregister(&srp_host_class); 8928c2ecf20Sopenharmony_ci transport_class_unregister(&srp_rport_class); 8938c2ecf20Sopenharmony_ci} 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_ciMODULE_AUTHOR("FUJITA Tomonori"); 8968c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("SRP Transport Attributes"); 8978c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_cimodule_init(srp_transport_init); 9008c2ecf20Sopenharmony_cimodule_exit(srp_transport_exit); 901