|
@@ -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;
|