diff options
Diffstat (limited to 'drivers/s390')
-rw-r--r-- | drivers/s390/scsi/zfcp_aux.c | 6 | ||||
-rw-r--r-- | drivers/s390/scsi/zfcp_ccw.c | 32 | ||||
-rw-r--r-- | drivers/s390/scsi/zfcp_def.h | 3 | ||||
-rw-r--r-- | drivers/s390/scsi/zfcp_erp.c | 7 | ||||
-rw-r--r-- | drivers/s390/scsi/zfcp_ext.h | 1 | ||||
-rw-r--r-- | drivers/s390/scsi/zfcp_fc.c | 52 | ||||
-rw-r--r-- | drivers/s390/scsi/zfcp_fsf.c | 3 | ||||
-rw-r--r-- | drivers/s390/scsi/zfcp_scsi.c | 24 | ||||
-rw-r--r-- | drivers/s390/scsi/zfcp_sysfs.c | 66 |
9 files changed, 144 insertions, 50 deletions
diff --git a/drivers/s390/scsi/zfcp_aux.c b/drivers/s390/scsi/zfcp_aux.c index 8004b071a9f2..01a73395a017 100644 --- a/drivers/s390/scsi/zfcp_aux.c +++ b/drivers/s390/scsi/zfcp_aux.c @@ -353,9 +353,11 @@ struct zfcp_adapter *zfcp_adapter_enqueue(struct ccw_device *ccw_device) adapter->ccw_device = ccw_device; INIT_WORK(&adapter->stat_work, _zfcp_status_read_scheduler); - INIT_WORK(&adapter->scan_work, zfcp_fc_scan_ports); + INIT_DELAYED_WORK(&adapter->scan_work, zfcp_fc_scan_ports); INIT_WORK(&adapter->ns_up_work, zfcp_fc_sym_name_update); + adapter->next_port_scan = jiffies; + if (zfcp_qdio_setup(adapter)) goto failed; @@ -420,7 +422,7 @@ void zfcp_adapter_unregister(struct zfcp_adapter *adapter) { struct ccw_device *cdev = adapter->ccw_device; - cancel_work_sync(&adapter->scan_work); + cancel_delayed_work_sync(&adapter->scan_work); cancel_work_sync(&adapter->stat_work); cancel_work_sync(&adapter->ns_up_work); zfcp_destroy_adapter_work_queue(adapter); diff --git a/drivers/s390/scsi/zfcp_ccw.c b/drivers/s390/scsi/zfcp_ccw.c index f9879d400d0e..54c7b48fdb46 100644 --- a/drivers/s390/scsi/zfcp_ccw.c +++ b/drivers/s390/scsi/zfcp_ccw.c @@ -56,8 +56,22 @@ static int zfcp_ccw_activate(struct ccw_device *cdev, int clear, char *tag) zfcp_erp_set_adapter_status(adapter, ZFCP_STATUS_COMMON_RUNNING); zfcp_erp_adapter_reopen(adapter, ZFCP_STATUS_COMMON_ERP_FAILED, tag); + + /* + * We want to scan ports here, with some random backoff and without + * rate limit. Recovery has already scheduled a port scan for us, + * but with both random delay and rate limit. Nevertheless we get + * what we want here by flushing the scheduled work after sleeping + * an equivalent random time. + * Let the port scan random delay elapse first. If recovery finishes + * up to that point in time, that would be perfect for both recovery + * and port scan. If not, i.e. recovery takes ages, there was no + * point in waiting a random delay on top of the time consumed by + * recovery. + */ + msleep(zfcp_fc_port_scan_backoff()); zfcp_erp_wait(adapter); - flush_work(&adapter->scan_work); /* ok to call even if nothing queued */ + flush_delayed_work(&adapter->scan_work); zfcp_ccw_adapter_put(adapter); @@ -162,11 +176,19 @@ static int zfcp_ccw_set_online(struct ccw_device *cdev) adapter->req_no = 0; zfcp_ccw_activate(cdev, 0, "ccsonl1"); - /* scan for remote ports - either at the end of any successful adapter recovery - or only after the adapter recovery for setting a device online */ + + /* + * We want to scan ports here, always, with some random delay and + * without rate limit - basically what zfcp_ccw_activate() has + * achieved for us. Not quite! That port scan depended on + * !no_auto_port_rescan. So let's cover the no_auto_port_rescan + * case here to make sure a port scan is done unconditionally. + * Since zfcp_ccw_activate() has waited the desired random time, + * we can immediately schedule and flush a port scan for the + * remaining cases. + */ zfcp_fc_inverse_conditional_port_scan(adapter); - flush_work(&adapter->scan_work); /* ok to call even if nothing queued */ + flush_delayed_work(&adapter->scan_work); zfcp_ccw_adapter_put(adapter); return 0; } diff --git a/drivers/s390/scsi/zfcp_def.h b/drivers/s390/scsi/zfcp_def.h index d91173f326c5..b8e853e53546 100644 --- a/drivers/s390/scsi/zfcp_def.h +++ b/drivers/s390/scsi/zfcp_def.h @@ -186,12 +186,13 @@ struct zfcp_adapter { struct fc_host_statistics *fc_stats; struct fsf_qtcb_bottom_port *stats_reset_data; unsigned long stats_reset; - struct work_struct scan_work; + struct delayed_work scan_work; struct work_struct ns_up_work; struct service_level service_level; struct workqueue_struct *work_queue; struct device_dma_parameters dma_parms; struct zfcp_fc_events events; + unsigned long next_port_scan; }; struct zfcp_port { diff --git a/drivers/s390/scsi/zfcp_erp.c b/drivers/s390/scsi/zfcp_erp.c index c82fe65c4128..2c5d4567d1da 100644 --- a/drivers/s390/scsi/zfcp_erp.c +++ b/drivers/s390/scsi/zfcp_erp.c @@ -821,11 +821,6 @@ static int zfcp_erp_port_forced_strategy_close(struct zfcp_erp_action *act) return ZFCP_ERP_CONTINUES; } -static void zfcp_erp_port_strategy_clearstati(struct zfcp_port *port) -{ - atomic_clear_mask(ZFCP_STATUS_COMMON_ACCESS_DENIED, &port->status); -} - static int zfcp_erp_port_forced_strategy(struct zfcp_erp_action *erp_action) { struct zfcp_port *port = erp_action->port; @@ -833,7 +828,6 @@ static int zfcp_erp_port_forced_strategy(struct zfcp_erp_action *erp_action) switch (erp_action->step) { case ZFCP_ERP_STEP_UNINITIALIZED: - zfcp_erp_port_strategy_clearstati(port); if ((status & ZFCP_STATUS_PORT_PHYS_OPEN) && (status & ZFCP_STATUS_COMMON_OPEN)) return zfcp_erp_port_forced_strategy_close(erp_action); @@ -933,7 +927,6 @@ static int zfcp_erp_port_strategy(struct zfcp_erp_action *erp_action) switch (erp_action->step) { case ZFCP_ERP_STEP_UNINITIALIZED: - zfcp_erp_port_strategy_clearstati(port); if (p_status & ZFCP_STATUS_COMMON_OPEN) return zfcp_erp_port_strategy_close(erp_action); break; diff --git a/drivers/s390/scsi/zfcp_ext.h b/drivers/s390/scsi/zfcp_ext.h index a9c570a09b85..5b500652572b 100644 --- a/drivers/s390/scsi/zfcp_ext.h +++ b/drivers/s390/scsi/zfcp_ext.h @@ -85,6 +85,7 @@ extern void zfcp_fc_gs_destroy(struct zfcp_adapter *); extern int zfcp_fc_exec_bsg_job(struct fc_bsg_job *); extern int zfcp_fc_timeout_bsg_job(struct fc_bsg_job *); extern void zfcp_fc_sym_name_update(struct work_struct *); +extern unsigned int zfcp_fc_port_scan_backoff(void); extern void zfcp_fc_conditional_port_scan(struct zfcp_adapter *); extern void zfcp_fc_inverse_conditional_port_scan(struct zfcp_adapter *); diff --git a/drivers/s390/scsi/zfcp_fc.c b/drivers/s390/scsi/zfcp_fc.c index ca28e1c66115..25d49f32ca63 100644 --- a/drivers/s390/scsi/zfcp_fc.c +++ b/drivers/s390/scsi/zfcp_fc.c @@ -12,6 +12,7 @@ #include <linux/types.h> #include <linux/slab.h> #include <linux/utsname.h> +#include <linux/random.h> #include <scsi/fc/fc_els.h> #include <scsi/libfc.h> #include "zfcp_ext.h" @@ -31,12 +32,54 @@ module_param_named(no_auto_port_rescan, no_auto_port_rescan, bool, 0600); MODULE_PARM_DESC(no_auto_port_rescan, "no automatic port_rescan (default off)"); +static unsigned int port_scan_backoff = 500; +module_param(port_scan_backoff, uint, 0600); +MODULE_PARM_DESC(port_scan_backoff, + "upper limit of port scan random backoff in msecs (default 500)"); + +static unsigned int port_scan_ratelimit = 60000; +module_param(port_scan_ratelimit, uint, 0600); +MODULE_PARM_DESC(port_scan_ratelimit, + "minimum interval between port scans in msecs (default 60000)"); + +unsigned int zfcp_fc_port_scan_backoff(void) +{ + if (!port_scan_backoff) + return 0; + return get_random_int() % port_scan_backoff; +} + +static void zfcp_fc_port_scan_time(struct zfcp_adapter *adapter) +{ + unsigned long interval = msecs_to_jiffies(port_scan_ratelimit); + unsigned long backoff = msecs_to_jiffies(zfcp_fc_port_scan_backoff()); + + adapter->next_port_scan = jiffies + interval + backoff; +} + +static void zfcp_fc_port_scan(struct zfcp_adapter *adapter) +{ + unsigned long now = jiffies; + unsigned long next = adapter->next_port_scan; + unsigned long delay = 0, max; + + /* delay only needed within waiting period */ + if (time_before(now, next)) { + delay = next - now; + /* paranoia: never ever delay scans longer than specified */ + max = msecs_to_jiffies(port_scan_ratelimit + port_scan_backoff); + delay = min(delay, max); + } + + queue_delayed_work(adapter->work_queue, &adapter->scan_work, delay); +} + void zfcp_fc_conditional_port_scan(struct zfcp_adapter *adapter) { if (no_auto_port_rescan) return; - queue_work(adapter->work_queue, &adapter->scan_work); + zfcp_fc_port_scan(adapter); } void zfcp_fc_inverse_conditional_port_scan(struct zfcp_adapter *adapter) @@ -44,7 +87,7 @@ void zfcp_fc_inverse_conditional_port_scan(struct zfcp_adapter *adapter) if (!no_auto_port_rescan) return; - queue_work(adapter->work_queue, &adapter->scan_work); + zfcp_fc_port_scan(adapter); } /** @@ -680,12 +723,15 @@ static int zfcp_fc_eval_gpn_ft(struct zfcp_fc_req *fc_req, */ void zfcp_fc_scan_ports(struct work_struct *work) { - struct zfcp_adapter *adapter = container_of(work, struct zfcp_adapter, + struct delayed_work *dw = to_delayed_work(work); + struct zfcp_adapter *adapter = container_of(dw, struct zfcp_adapter, scan_work); int ret, i; struct zfcp_fc_req *fc_req; int chain, max_entries, buf_num, max_bytes; + zfcp_fc_port_scan_time(adapter); + chain = adapter->adapter_features & FSF_FEATURE_ELS_CT_CHAINED_SBALS; buf_num = chain ? ZFCP_FC_GPN_FT_NUM_BUFS : 1; max_entries = chain ? ZFCP_FC_GPN_FT_MAX_ENT : ZFCP_FC_GPN_FT_ENT_PAGE; diff --git a/drivers/s390/scsi/zfcp_fsf.c b/drivers/s390/scsi/zfcp_fsf.c index 0fe8d5d95119..21ec5e2f584c 100644 --- a/drivers/s390/scsi/zfcp_fsf.c +++ b/drivers/s390/scsi/zfcp_fsf.c @@ -1396,8 +1396,7 @@ static void zfcp_fsf_open_port_handler(struct zfcp_fsf_req *req) port->handle = header->port_handle; atomic_set_mask(ZFCP_STATUS_COMMON_OPEN | ZFCP_STATUS_PORT_PHYS_OPEN, &port->status); - atomic_clear_mask(ZFCP_STATUS_COMMON_ACCESS_DENIED | - ZFCP_STATUS_COMMON_ACCESS_BOXED, + atomic_clear_mask(ZFCP_STATUS_COMMON_ACCESS_BOXED, &port->status); /* check whether D_ID has changed during open */ /* diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c index b5dfa51f396f..75f4bfc2b98a 100644 --- a/drivers/s390/scsi/zfcp_scsi.c +++ b/drivers/s390/scsi/zfcp_scsi.c @@ -32,25 +32,6 @@ static bool allow_lun_scan = 1; module_param(allow_lun_scan, bool, 0600); MODULE_PARM_DESC(allow_lun_scan, "For NPIV, scan and attach all storage LUNs"); -static int zfcp_scsi_change_queue_depth(struct scsi_device *sdev, int depth, - int reason) -{ - switch (reason) { - case SCSI_QDEPTH_DEFAULT: - scsi_adjust_queue_depth(sdev, depth); - break; - case SCSI_QDEPTH_QFULL: - scsi_track_queue_full(sdev, depth); - break; - case SCSI_QDEPTH_RAMP_UP: - scsi_adjust_queue_depth(sdev, depth); - break; - default: - return -EOPNOTSUPP; - } - return sdev->queue_depth; -} - static void zfcp_scsi_slave_destroy(struct scsi_device *sdev) { struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(sdev); @@ -66,7 +47,7 @@ static void zfcp_scsi_slave_destroy(struct scsi_device *sdev) static int zfcp_scsi_slave_configure(struct scsi_device *sdp) { if (sdp->tagged_supported) - scsi_adjust_queue_depth(sdp, default_depth); + scsi_change_queue_depth(sdp, default_depth); return 0; } @@ -305,7 +286,7 @@ static struct scsi_host_template zfcp_scsi_host_template = { .slave_alloc = zfcp_scsi_slave_alloc, .slave_configure = zfcp_scsi_slave_configure, .slave_destroy = zfcp_scsi_slave_destroy, - .change_queue_depth = zfcp_scsi_change_queue_depth, + .change_queue_depth = scsi_change_queue_depth, .proc_name = "zfcp", .can_queue = 4096, .this_id = -1, @@ -320,6 +301,7 @@ static struct scsi_host_template zfcp_scsi_host_template = { .use_clustering = 1, .shost_attrs = zfcp_sysfs_shost_attrs, .sdev_attrs = zfcp_sysfs_sdev_attrs, + .track_queue_depth = 1, }; /** diff --git a/drivers/s390/scsi/zfcp_sysfs.c b/drivers/s390/scsi/zfcp_sysfs.c index 672b57219e11..96a0be13e841 100644 --- a/drivers/s390/scsi/zfcp_sysfs.c +++ b/drivers/s390/scsi/zfcp_sysfs.c @@ -73,9 +73,7 @@ ZFCP_DEFINE_ATTR(zfcp_port, port, status, "0x%08x\n", ZFCP_DEFINE_ATTR(zfcp_port, port, in_recovery, "%d\n", (atomic_read(&port->status) & ZFCP_STATUS_COMMON_ERP_INUSE) != 0); -ZFCP_DEFINE_ATTR(zfcp_port, port, access_denied, "%d\n", - (atomic_read(&port->status) & - ZFCP_STATUS_COMMON_ACCESS_DENIED) != 0); +ZFCP_DEFINE_ATTR_CONST(port, access_denied, "%d\n", 0); ZFCP_DEFINE_ATTR(zfcp_unit, unit, status, "0x%08x\n", zfcp_unit_sdev_status(unit)); @@ -223,9 +221,13 @@ static ssize_t zfcp_sysfs_port_rescan_store(struct device *dev, if (!adapter) return -ENODEV; - /* sync the user-space- with the kernel-invocation of scan_work */ - queue_work(adapter->work_queue, &adapter->scan_work); - flush_work(&adapter->scan_work); + /* + * Users wish is our command: immediately schedule and flush a + * worker to conduct a synchronous port scan, that is, neither + * a random delay nor a rate limit is applied here. + */ + queue_delayed_work(adapter->work_queue, &adapter->scan_work, 0); + flush_delayed_work(&adapter->scan_work); zfcp_ccw_adapter_put(adapter); return (ssize_t) count; @@ -439,16 +441,15 @@ static ssize_t zfcp_sysfs_scsi_##_name##_show(struct device *dev, \ { \ struct scsi_device *sdev = to_scsi_device(dev); \ struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(sdev); \ - struct zfcp_port *port = zfcp_sdev->port; \ \ return sprintf(buf, _format, _value); \ } \ static DEVICE_ATTR(_name, S_IRUGO, zfcp_sysfs_scsi_##_name##_show, NULL); ZFCP_DEFINE_SCSI_ATTR(hba_id, "%s\n", - dev_name(&port->adapter->ccw_device->dev)); + dev_name(&zfcp_sdev->port->adapter->ccw_device->dev)); ZFCP_DEFINE_SCSI_ATTR(wwpn, "0x%016llx\n", - (unsigned long long) port->wwpn); + (unsigned long long) zfcp_sdev->port->wwpn); static ssize_t zfcp_sysfs_scsi_fcp_lun_show(struct device *dev, struct device_attribute *attr, @@ -460,6 +461,49 @@ static ssize_t zfcp_sysfs_scsi_fcp_lun_show(struct device *dev, } static DEVICE_ATTR(fcp_lun, S_IRUGO, zfcp_sysfs_scsi_fcp_lun_show, NULL); +ZFCP_DEFINE_SCSI_ATTR(zfcp_access_denied, "%d\n", + (atomic_read(&zfcp_sdev->status) & + ZFCP_STATUS_COMMON_ACCESS_DENIED) != 0); + +static ssize_t zfcp_sysfs_scsi_zfcp_failed_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct scsi_device *sdev = to_scsi_device(dev); + unsigned int status = atomic_read(&sdev_to_zfcp(sdev)->status); + unsigned int failed = status & ZFCP_STATUS_COMMON_ERP_FAILED ? 1 : 0; + + return sprintf(buf, "%d\n", failed); +} + +static ssize_t zfcp_sysfs_scsi_zfcp_failed_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct scsi_device *sdev = to_scsi_device(dev); + unsigned long val; + + if (kstrtoul(buf, 0, &val) || val != 0) + return -EINVAL; + + zfcp_erp_set_lun_status(sdev, ZFCP_STATUS_COMMON_RUNNING); + zfcp_erp_lun_reopen(sdev, ZFCP_STATUS_COMMON_ERP_FAILED, + "syufai3"); + zfcp_erp_wait(sdev_to_zfcp(sdev)->port->adapter); + + return count; +} +static DEVICE_ATTR(zfcp_failed, S_IWUSR | S_IRUGO, + zfcp_sysfs_scsi_zfcp_failed_show, + zfcp_sysfs_scsi_zfcp_failed_store); + +ZFCP_DEFINE_SCSI_ATTR(zfcp_in_recovery, "%d\n", + (atomic_read(&zfcp_sdev->status) & + ZFCP_STATUS_COMMON_ERP_INUSE) != 0); + +ZFCP_DEFINE_SCSI_ATTR(zfcp_status, "0x%08x\n", + atomic_read(&zfcp_sdev->status)); + struct device_attribute *zfcp_sysfs_sdev_attrs[] = { &dev_attr_fcp_lun, &dev_attr_wwpn, @@ -467,6 +511,10 @@ struct device_attribute *zfcp_sysfs_sdev_attrs[] = { &dev_attr_read_latency, &dev_attr_write_latency, &dev_attr_cmd_latency, + &dev_attr_zfcp_access_denied, + &dev_attr_zfcp_failed, + &dev_attr_zfcp_in_recovery, + &dev_attr_zfcp_status, NULL }; |