aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMartin Peschke <mpeschke@linux.vnet.ibm.com>2014-11-13 08:59:48 -0500
committerChristoph Hellwig <hch@lst.de>2014-11-20 03:11:30 -0500
commit18f87a67e6d681d1c6f8b3c47985f21b25959a77 (patch)
tree405a16b6b14b8ceb031c4fcf12bf52df4511a27b
parentc8bba1443573055f4836c9f3ab5638d25b8d8d5c (diff)
zfcp: auto port scan resiliency
This patch improves the Fibre Channel port scan behaviour of the zfcp lldd. Without it the zfcp device driver may churn up the storage area network by excessive scanning and scan bursts, particularly in big virtual server environments, potentially resulting in interference of virtual servers and reduced availability of storage connectivity. The two main issues as to the zfcp device drivers automatic port scan in virtual server environments are frequency and simultaneity. On the one hand, there is no point in allowing lots of ports scans in a row. It makes sense, though, to make sure that a scan is conducted eventually if there has been any indication for potential SAN changes. On the other hand, lots of virtual servers receiving the same indication for a SAN change had better not attempt to conduct a scan instantly, that is, at the same time. Hence this patch has a two-fold approach for better port scanning: the introduction of a rate limit to amend frequency issues, and the introduction of a short random backoff to amend simultaneity issues. Both approaches boil down to deferred port scans, with delays comprising parts for both approaches. The new port scan behaviour is summarised best by: NEW: NEW: no_auto_port_rescan random rate flush backoff limit =wait adapter resume/thaw yes yes no yes* adapter online (user) no yes no yes* port rescan (user) no no no yes adapter recovery (user) yes yes yes no adapter recovery (other) yes yes yes no incoming ELS yes yes yes no incoming ELS lost yes yes yes no Implementation is straight-forward by converting an existing worker to a delayed worker. But care is needed whenever that worker is going to be flushed (in order to make sure work has been completed), since a flush operation cancels the timer set up for deferred execution (see * above). There is a small race window whenever a port scan work starts running up to the point in time of storing the time stamp for that port scan. The impact is negligible. Closing that gap isn't trivial, though, and would the destroy the beauty of a simple work-to-delayed-work conversion. Signed-off-by: Martin Peschke <mpeschke@linux.vnet.ibm.com> Signed-off-by: Steffen Maier <maier@linux.vnet.ibm.com> Reviewed-by: Hannes Reinecke <hare@suse.de> Signed-off-by: Christoph Hellwig <hch@lst.de>
-rw-r--r--drivers/s390/scsi/zfcp_aux.c6
-rw-r--r--drivers/s390/scsi/zfcp_ccw.c32
-rw-r--r--drivers/s390/scsi/zfcp_def.h3
-rw-r--r--drivers/s390/scsi/zfcp_ext.h1
-rw-r--r--drivers/s390/scsi/zfcp_fc.c52
-rw-r--r--drivers/s390/scsi/zfcp_sysfs.c10
6 files changed, 90 insertions, 14 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)
353 adapter->ccw_device = ccw_device; 353 adapter->ccw_device = ccw_device;
354 354
355 INIT_WORK(&adapter->stat_work, _zfcp_status_read_scheduler); 355 INIT_WORK(&adapter->stat_work, _zfcp_status_read_scheduler);
356 INIT_WORK(&adapter->scan_work, zfcp_fc_scan_ports); 356 INIT_DELAYED_WORK(&adapter->scan_work, zfcp_fc_scan_ports);
357 INIT_WORK(&adapter->ns_up_work, zfcp_fc_sym_name_update); 357 INIT_WORK(&adapter->ns_up_work, zfcp_fc_sym_name_update);
358 358
359 adapter->next_port_scan = jiffies;
360
359 if (zfcp_qdio_setup(adapter)) 361 if (zfcp_qdio_setup(adapter))
360 goto failed; 362 goto failed;
361 363
@@ -420,7 +422,7 @@ void zfcp_adapter_unregister(struct zfcp_adapter *adapter)
420{ 422{
421 struct ccw_device *cdev = adapter->ccw_device; 423 struct ccw_device *cdev = adapter->ccw_device;
422 424
423 cancel_work_sync(&adapter->scan_work); 425 cancel_delayed_work_sync(&adapter->scan_work);
424 cancel_work_sync(&adapter->stat_work); 426 cancel_work_sync(&adapter->stat_work);
425 cancel_work_sync(&adapter->ns_up_work); 427 cancel_work_sync(&adapter->ns_up_work);
426 zfcp_destroy_adapter_work_queue(adapter); 428 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)
56 zfcp_erp_set_adapter_status(adapter, ZFCP_STATUS_COMMON_RUNNING); 56 zfcp_erp_set_adapter_status(adapter, ZFCP_STATUS_COMMON_RUNNING);
57 zfcp_erp_adapter_reopen(adapter, ZFCP_STATUS_COMMON_ERP_FAILED, 57 zfcp_erp_adapter_reopen(adapter, ZFCP_STATUS_COMMON_ERP_FAILED,
58 tag); 58 tag);
59
60 /*
61 * We want to scan ports here, with some random backoff and without
62 * rate limit. Recovery has already scheduled a port scan for us,
63 * but with both random delay and rate limit. Nevertheless we get
64 * what we want here by flushing the scheduled work after sleeping
65 * an equivalent random time.
66 * Let the port scan random delay elapse first. If recovery finishes
67 * up to that point in time, that would be perfect for both recovery
68 * and port scan. If not, i.e. recovery takes ages, there was no
69 * point in waiting a random delay on top of the time consumed by
70 * recovery.
71 */
72 msleep(zfcp_fc_port_scan_backoff());
59 zfcp_erp_wait(adapter); 73 zfcp_erp_wait(adapter);
60 flush_work(&adapter->scan_work); /* ok to call even if nothing queued */ 74 flush_delayed_work(&adapter->scan_work);
61 75
62 zfcp_ccw_adapter_put(adapter); 76 zfcp_ccw_adapter_put(adapter);
63 77
@@ -162,11 +176,19 @@ static int zfcp_ccw_set_online(struct ccw_device *cdev)
162 adapter->req_no = 0; 176 adapter->req_no = 0;
163 177
164 zfcp_ccw_activate(cdev, 0, "ccsonl1"); 178 zfcp_ccw_activate(cdev, 0, "ccsonl1");
165 /* scan for remote ports 179
166 either at the end of any successful adapter recovery 180 /*
167 or only after the adapter recovery for setting a device online */ 181 * We want to scan ports here, always, with some random delay and
182 * without rate limit - basically what zfcp_ccw_activate() has
183 * achieved for us. Not quite! That port scan depended on
184 * !no_auto_port_rescan. So let's cover the no_auto_port_rescan
185 * case here to make sure a port scan is done unconditionally.
186 * Since zfcp_ccw_activate() has waited the desired random time,
187 * we can immediately schedule and flush a port scan for the
188 * remaining cases.
189 */
168 zfcp_fc_inverse_conditional_port_scan(adapter); 190 zfcp_fc_inverse_conditional_port_scan(adapter);
169 flush_work(&adapter->scan_work); /* ok to call even if nothing queued */ 191 flush_delayed_work(&adapter->scan_work);
170 zfcp_ccw_adapter_put(adapter); 192 zfcp_ccw_adapter_put(adapter);
171 return 0; 193 return 0;
172} 194}
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 {
186 struct fc_host_statistics *fc_stats; 186 struct fc_host_statistics *fc_stats;
187 struct fsf_qtcb_bottom_port *stats_reset_data; 187 struct fsf_qtcb_bottom_port *stats_reset_data;
188 unsigned long stats_reset; 188 unsigned long stats_reset;
189 struct work_struct scan_work; 189 struct delayed_work scan_work;
190 struct work_struct ns_up_work; 190 struct work_struct ns_up_work;
191 struct service_level service_level; 191 struct service_level service_level;
192 struct workqueue_struct *work_queue; 192 struct workqueue_struct *work_queue;
193 struct device_dma_parameters dma_parms; 193 struct device_dma_parameters dma_parms;
194 struct zfcp_fc_events events; 194 struct zfcp_fc_events events;
195 unsigned long next_port_scan;
195}; 196};
196 197
197struct zfcp_port { 198struct zfcp_port {
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 *);
85extern int zfcp_fc_exec_bsg_job(struct fc_bsg_job *); 85extern int zfcp_fc_exec_bsg_job(struct fc_bsg_job *);
86extern int zfcp_fc_timeout_bsg_job(struct fc_bsg_job *); 86extern int zfcp_fc_timeout_bsg_job(struct fc_bsg_job *);
87extern void zfcp_fc_sym_name_update(struct work_struct *); 87extern void zfcp_fc_sym_name_update(struct work_struct *);
88extern unsigned int zfcp_fc_port_scan_backoff(void);
88extern void zfcp_fc_conditional_port_scan(struct zfcp_adapter *); 89extern void zfcp_fc_conditional_port_scan(struct zfcp_adapter *);
89extern void zfcp_fc_inverse_conditional_port_scan(struct zfcp_adapter *); 90extern void zfcp_fc_inverse_conditional_port_scan(struct zfcp_adapter *);
90 91
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 @@
12#include <linux/types.h> 12#include <linux/types.h>
13#include <linux/slab.h> 13#include <linux/slab.h>
14#include <linux/utsname.h> 14#include <linux/utsname.h>
15#include <linux/random.h>
15#include <scsi/fc/fc_els.h> 16#include <scsi/fc/fc_els.h>
16#include <scsi/libfc.h> 17#include <scsi/libfc.h>
17#include "zfcp_ext.h" 18#include "zfcp_ext.h"
@@ -31,12 +32,54 @@ module_param_named(no_auto_port_rescan, no_auto_port_rescan, bool, 0600);
31MODULE_PARM_DESC(no_auto_port_rescan, 32MODULE_PARM_DESC(no_auto_port_rescan,
32 "no automatic port_rescan (default off)"); 33 "no automatic port_rescan (default off)");
33 34
35static unsigned int port_scan_backoff = 500;
36module_param(port_scan_backoff, uint, 0600);
37MODULE_PARM_DESC(port_scan_backoff,
38 "upper limit of port scan random backoff in msecs (default 500)");
39
40static unsigned int port_scan_ratelimit = 60000;
41module_param(port_scan_ratelimit, uint, 0600);
42MODULE_PARM_DESC(port_scan_ratelimit,
43 "minimum interval between port scans in msecs (default 60000)");
44
45unsigned int zfcp_fc_port_scan_backoff(void)
46{
47 if (!port_scan_backoff)
48 return 0;
49 return get_random_int() % port_scan_backoff;
50}
51
52static void zfcp_fc_port_scan_time(struct zfcp_adapter *adapter)
53{
54 unsigned long interval = msecs_to_jiffies(port_scan_ratelimit);
55 unsigned long backoff = msecs_to_jiffies(zfcp_fc_port_scan_backoff());
56
57 adapter->next_port_scan = jiffies + interval + backoff;
58}
59
60static void zfcp_fc_port_scan(struct zfcp_adapter *adapter)
61{
62 unsigned long now = jiffies;
63 unsigned long next = adapter->next_port_scan;
64 unsigned long delay = 0, max;
65
66 /* delay only needed within waiting period */
67 if (time_before(now, next)) {
68 delay = next - now;
69 /* paranoia: never ever delay scans longer than specified */
70 max = msecs_to_jiffies(port_scan_ratelimit + port_scan_backoff);
71 delay = min(delay, max);
72 }
73
74 queue_delayed_work(adapter->work_queue, &adapter->scan_work, delay);
75}
76
34void zfcp_fc_conditional_port_scan(struct zfcp_adapter *adapter) 77void zfcp_fc_conditional_port_scan(struct zfcp_adapter *adapter)
35{ 78{
36 if (no_auto_port_rescan) 79 if (no_auto_port_rescan)
37 return; 80 return;
38 81
39 queue_work(adapter->work_queue, &adapter->scan_work); 82 zfcp_fc_port_scan(adapter);
40} 83}
41 84
42void zfcp_fc_inverse_conditional_port_scan(struct zfcp_adapter *adapter) 85void 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)
44 if (!no_auto_port_rescan) 87 if (!no_auto_port_rescan)
45 return; 88 return;
46 89
47 queue_work(adapter->work_queue, &adapter->scan_work); 90 zfcp_fc_port_scan(adapter);
48} 91}
49 92
50/** 93/**
@@ -680,12 +723,15 @@ static int zfcp_fc_eval_gpn_ft(struct zfcp_fc_req *fc_req,
680 */ 723 */
681void zfcp_fc_scan_ports(struct work_struct *work) 724void zfcp_fc_scan_ports(struct work_struct *work)
682{ 725{
683 struct zfcp_adapter *adapter = container_of(work, struct zfcp_adapter, 726 struct delayed_work *dw = to_delayed_work(work);
727 struct zfcp_adapter *adapter = container_of(dw, struct zfcp_adapter,
684 scan_work); 728 scan_work);
685 int ret, i; 729 int ret, i;
686 struct zfcp_fc_req *fc_req; 730 struct zfcp_fc_req *fc_req;
687 int chain, max_entries, buf_num, max_bytes; 731 int chain, max_entries, buf_num, max_bytes;
688 732
733 zfcp_fc_port_scan_time(adapter);
734
689 chain = adapter->adapter_features & FSF_FEATURE_ELS_CT_CHAINED_SBALS; 735 chain = adapter->adapter_features & FSF_FEATURE_ELS_CT_CHAINED_SBALS;
690 buf_num = chain ? ZFCP_FC_GPN_FT_NUM_BUFS : 1; 736 buf_num = chain ? ZFCP_FC_GPN_FT_NUM_BUFS : 1;
691 max_entries = chain ? ZFCP_FC_GPN_FT_MAX_ENT : ZFCP_FC_GPN_FT_ENT_PAGE; 737 max_entries = chain ? ZFCP_FC_GPN_FT_MAX_ENT : ZFCP_FC_GPN_FT_ENT_PAGE;
diff --git a/drivers/s390/scsi/zfcp_sysfs.c b/drivers/s390/scsi/zfcp_sysfs.c
index a488c09f6e25..96a0be13e841 100644
--- a/drivers/s390/scsi/zfcp_sysfs.c
+++ b/drivers/s390/scsi/zfcp_sysfs.c
@@ -221,9 +221,13 @@ static ssize_t zfcp_sysfs_port_rescan_store(struct device *dev,
221 if (!adapter) 221 if (!adapter)
222 return -ENODEV; 222 return -ENODEV;
223 223
224 /* sync the user-space- with the kernel-invocation of scan_work */ 224 /*
225 queue_work(adapter->work_queue, &adapter->scan_work); 225 * Users wish is our command: immediately schedule and flush a
226 flush_work(&adapter->scan_work); 226 * worker to conduct a synchronous port scan, that is, neither
227 * a random delay nor a rate limit is applied here.
228 */
229 queue_delayed_work(adapter->work_queue, &adapter->scan_work, 0);
230 flush_delayed_work(&adapter->scan_work);
227 zfcp_ccw_adapter_put(adapter); 231 zfcp_ccw_adapter_put(adapter);
228 232
229 return (ssize_t) count; 233 return (ssize_t) count;