/*
* zfcp device driver
*
* Fibre Channel related functions for the zfcp device driver.
*
* Copyright IBM Corp. 2008, 2010
*/
#define KMSG_COMPONENT "zfcp"
#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
#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"
#include "zfcp_fc.h"
struct kmem_cache *zfcp_fc_req_cache;
static u32 zfcp_fc_rscn_range_mask[] = {
[ELS_ADDR_FMT_PORT] = 0xFFFFFF,
[ELS_ADDR_FMT_AREA] = 0xFFFF00,
[ELS_ADDR_FMT_DOM] = 0xFF0000,
[ELS_ADDR_FMT_FAB] = 0x000000,
};
static bool no_auto_port_rescan;
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;
zfcp_fc_port_scan(adapter);
}
void zfcp_fc_inverse_conditional_port_scan(struct zfcp_adapter *adapter)
{
if (!no_auto_port_rescan)
return;
zfcp_fc_port_scan(adapter);
}
/**
* zfcp_fc_post_event - post event to userspace via fc_transport
* @work: work struct with enqueued events
*/
void zfcp_fc_post_event(struct work_struct *work)
{
struct zfcp_fc_event *event = NULL, *tmp = NULL;
LIST_HEAD(tmp_lh);
struct zfcp_fc_events *events = container_of(work,
struct zfcp_fc_events, work);
struct zfcp_adapter *adapter = container_of(events, struct zfcp_adapter,
events);
spin_lock_bh(&events->list_lock);
list_splice_init(&events->list, &tmp_lh);
spin_unlock_bh(&events->list_lock);
list_for_each_entry_safe(event, tmp, &tmp_lh, list) {
fc_host_post_event(adapter->scsi_host, fc_get_event_number(),
event->code, event->data);
list_del(&event->list);
kfree(event);
}
}
/**
* zfcp_fc_enqueue_event - safely enqueue FC HBA API event from irq context
* @adapter: The adapter where to enqueue the event
* @event_code: The event code (as defined in fc_host_event_code in
* scsi_transport_fc.h)
* @event_data: The event data (e.g. n_port page in case of els)
*/
void zfcp_fc_enqueue_event(struct zfcp_adapter *adapter,
enum fc_host_event_code event_code, u32 event_data)
{
struct zfcp_fc_event *event;
event = kmalloc(sizeof(struct zfcp_fc_event), GFP_ATOMIC);
if (!event)
return;
event->code = event_code;
event->data = event_data;
spin_lock(&adapter->events.list_lock);
list_add_tail(&event->list, &adapter->events.list);
spin_unlock(&adapter->events.list_lock);
queue_work(adapter->work_queue, &adapter->events.work);
}
static int zfcp_fc_wka_port_get(struct zfcp_fc_wka_port