mirror of
https://github.com/adulau/aha.git
synced 2025-04-23 03:36:35 +00:00
Merge git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi-misc-2.6
* git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi-misc-2.6: (102 commits) [SCSI] scsi_dh: fix kconfig related build errors [SCSI] sym53c8xx: Fix bogus sym_que_entry re-implementation of container_of [SCSI] scsi_cmnd.h: remove double inclusion of linux/blkdev.h [SCSI] make struct scsi_{host,target}_type static [SCSI] fix locking in host use of blk_plug_device() [SCSI] zfcp: Cleanup external header file [SCSI] zfcp: Cleanup code in zfcp_erp.c [SCSI] zfcp: zfcp_fsf cleanup. [SCSI] zfcp: consolidate sysfs things into one file. [SCSI] zfcp: Cleanup of code in zfcp_aux.c [SCSI] zfcp: Cleanup of code in zfcp_scsi.c [SCSI] zfcp: Move status accessors from zfcp to SCSI include file. [SCSI] zfcp: Small QDIO cleanups [SCSI] zfcp: Adapter reopen for large number of unsolicited status [SCSI] zfcp: Fix error checking for ELS ADISC requests [SCSI] zfcp: wait until adapter is finished with ERP during auto-port [SCSI] ibmvfc: IBM Power Virtual Fibre Channel Adapter Client Driver [SCSI] sg: Add target reset support [SCSI] lib: Add support for the T10 (SCSI) Data Integrity Field CRC [SCSI] sd: Move scsi_disk() accessor function to sd.h ...
This commit is contained in:
commit
89a93f2f48
106 changed files with 14864 additions and 14654 deletions
|
@ -56,19 +56,33 @@ Supported Cards/Chipsets
|
|||
9005:0285:9005:02d1 Adaptec 5405 (Voodoo40)
|
||||
9005:0285:15d9:02d2 SMC AOC-USAS-S8i-LP
|
||||
9005:0285:15d9:02d3 SMC AOC-USAS-S8iR-LP
|
||||
9005:0285:9005:02d4 Adaptec 2045 (Voodoo04 Lite)
|
||||
9005:0285:9005:02d5 Adaptec 2405 (Voodoo40 Lite)
|
||||
9005:0285:9005:02d6 Adaptec 2445 (Voodoo44 Lite)
|
||||
9005:0285:9005:02d7 Adaptec 2805 (Voodoo80 Lite)
|
||||
9005:0285:9005:02d4 Adaptec ASR-2045 (Voodoo04 Lite)
|
||||
9005:0285:9005:02d5 Adaptec ASR-2405 (Voodoo40 Lite)
|
||||
9005:0285:9005:02d6 Adaptec ASR-2445 (Voodoo44 Lite)
|
||||
9005:0285:9005:02d7 Adaptec ASR-2805 (Voodoo80 Lite)
|
||||
9005:0285:9005:02d8 Adaptec 5405G (Voodoo40 PM)
|
||||
9005:0285:9005:02d9 Adaptec 5445G (Voodoo44 PM)
|
||||
9005:0285:9005:02da Adaptec 5805G (Voodoo80 PM)
|
||||
9005:0285:9005:02db Adaptec 5085G (Voodoo08 PM)
|
||||
9005:0285:9005:02dc Adaptec 51245G (Voodoo124 PM)
|
||||
9005:0285:9005:02dd Adaptec 51645G (Voodoo164 PM)
|
||||
9005:0285:9005:02de Adaptec 52445G (Voodoo244 PM)
|
||||
9005:0285:9005:02df Adaptec ASR-2045G (Voodoo04 Lite PM)
|
||||
9005:0285:9005:02e0 Adaptec ASR-2405G (Voodoo40 Lite PM)
|
||||
9005:0285:9005:02e1 Adaptec ASR-2445G (Voodoo44 Lite PM)
|
||||
9005:0285:9005:02e2 Adaptec ASR-2805G (Voodoo80 Lite PM)
|
||||
1011:0046:9005:0364 Adaptec 5400S (Mustang)
|
||||
1011:0046:9005:0365 Adaptec 5400S (Mustang)
|
||||
9005:0287:9005:0800 Adaptec Themisto (Jupiter)
|
||||
9005:0200:9005:0200 Adaptec Themisto (Jupiter)
|
||||
9005:0286:9005:0800 Adaptec Callisto (Jupiter)
|
||||
1011:0046:9005:1364 Dell PERC 2/QC (Quad Channel, Mustang)
|
||||
1011:0046:9005:1365 Dell PERC 2/QC (Quad Channel, Mustang)
|
||||
1028:0001:1028:0001 Dell PERC 2/Si (Iguana)
|
||||
1028:0003:1028:0003 Dell PERC 3/Si (SlimFast)
|
||||
1028:0002:1028:0002 Dell PERC 3/Di (Opal)
|
||||
1028:0004:1028:0004 Dell PERC 3/DiF (Iguana)
|
||||
1028:0004:1028:0004 Dell PERC 3/SiF (Iguana)
|
||||
1028:0004:1028:00d0 Dell PERC 3/DiF (Iguana)
|
||||
1028:0002:1028:00d1 Dell PERC 3/DiV (Viper)
|
||||
1028:0002:1028:00d9 Dell PERC 3/DiL (Lexus)
|
||||
1028:000a:1028:0106 Dell PERC 3/DiJ (Jaguar)
|
||||
|
|
|
@ -740,8 +740,13 @@ static int bsg_put_device(struct bsg_device *bd)
|
|||
mutex_lock(&bsg_mutex);
|
||||
|
||||
do_free = atomic_dec_and_test(&bd->ref_count);
|
||||
if (!do_free)
|
||||
if (!do_free) {
|
||||
mutex_unlock(&bsg_mutex);
|
||||
goto out;
|
||||
}
|
||||
|
||||
hlist_del(&bd->dev_list);
|
||||
mutex_unlock(&bsg_mutex);
|
||||
|
||||
dprintk("%s: tearing down\n", bd->name);
|
||||
|
||||
|
@ -757,10 +762,8 @@ static int bsg_put_device(struct bsg_device *bd)
|
|||
*/
|
||||
ret = bsg_complete_all_commands(bd);
|
||||
|
||||
hlist_del(&bd->dev_list);
|
||||
kfree(bd);
|
||||
out:
|
||||
mutex_unlock(&bsg_mutex);
|
||||
kref_put(&q->bsg_dev.ref, bsg_kref_release_function);
|
||||
if (do_free)
|
||||
blk_put_queue(q);
|
||||
|
|
|
@ -71,6 +71,10 @@
|
|||
|
||||
#include "iscsi_iser.h"
|
||||
|
||||
static struct scsi_host_template iscsi_iser_sht;
|
||||
static struct iscsi_transport iscsi_iser_transport;
|
||||
static struct scsi_transport_template *iscsi_iser_scsi_transport;
|
||||
|
||||
static unsigned int iscsi_max_lun = 512;
|
||||
module_param_named(max_lun, iscsi_max_lun, uint, S_IRUGO);
|
||||
|
||||
|
@ -91,7 +95,6 @@ iscsi_iser_recv(struct iscsi_conn *conn,
|
|||
struct iscsi_hdr *hdr, char *rx_data, int rx_data_len)
|
||||
{
|
||||
int rc = 0;
|
||||
uint32_t ret_itt;
|
||||
int datalen;
|
||||
int ahslen;
|
||||
|
||||
|
@ -107,12 +110,7 @@ iscsi_iser_recv(struct iscsi_conn *conn,
|
|||
/* read AHS */
|
||||
ahslen = hdr->hlength * 4;
|
||||
|
||||
/* verify itt (itt encoding: age+cid+itt) */
|
||||
rc = iscsi_verify_itt(conn, hdr, &ret_itt);
|
||||
|
||||
if (!rc)
|
||||
rc = iscsi_complete_pdu(conn, hdr, rx_data, rx_data_len);
|
||||
|
||||
rc = iscsi_complete_pdu(conn, hdr, rx_data, rx_data_len);
|
||||
if (rc && rc != ISCSI_ERR_NO_SCSI_CMD)
|
||||
goto error;
|
||||
|
||||
|
@ -123,25 +121,33 @@ error:
|
|||
|
||||
|
||||
/**
|
||||
* iscsi_iser_cmd_init - Initialize iSCSI SCSI_READ or SCSI_WRITE commands
|
||||
* iscsi_iser_task_init - Initialize task
|
||||
* @task: iscsi task
|
||||
*
|
||||
**/
|
||||
* Initialize the task for the scsi command or mgmt command.
|
||||
*/
|
||||
static int
|
||||
iscsi_iser_cmd_init(struct iscsi_cmd_task *ctask)
|
||||
iscsi_iser_task_init(struct iscsi_task *task)
|
||||
{
|
||||
struct iscsi_iser_conn *iser_conn = ctask->conn->dd_data;
|
||||
struct iscsi_iser_cmd_task *iser_ctask = ctask->dd_data;
|
||||
struct iscsi_iser_conn *iser_conn = task->conn->dd_data;
|
||||
struct iscsi_iser_task *iser_task = task->dd_data;
|
||||
|
||||
iser_ctask->command_sent = 0;
|
||||
iser_ctask->iser_conn = iser_conn;
|
||||
iser_ctask_rdma_init(iser_ctask);
|
||||
/* mgmt task */
|
||||
if (!task->sc) {
|
||||
iser_task->desc.data = task->data;
|
||||
return 0;
|
||||
}
|
||||
|
||||
iser_task->command_sent = 0;
|
||||
iser_task->iser_conn = iser_conn;
|
||||
iser_task_rdma_init(iser_task);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* iscsi_mtask_xmit - xmit management(immediate) task
|
||||
* iscsi_iser_mtask_xmit - xmit management(immediate) task
|
||||
* @conn: iscsi connection
|
||||
* @mtask: task management task
|
||||
* @task: task management task
|
||||
*
|
||||
* Notes:
|
||||
* The function can return -EAGAIN in which case caller must
|
||||
|
@ -150,20 +156,19 @@ iscsi_iser_cmd_init(struct iscsi_cmd_task *ctask)
|
|||
*
|
||||
**/
|
||||
static int
|
||||
iscsi_iser_mtask_xmit(struct iscsi_conn *conn,
|
||||
struct iscsi_mgmt_task *mtask)
|
||||
iscsi_iser_mtask_xmit(struct iscsi_conn *conn, struct iscsi_task *task)
|
||||
{
|
||||
int error = 0;
|
||||
|
||||
debug_scsi("mtask deq [cid %d itt 0x%x]\n", conn->id, mtask->itt);
|
||||
debug_scsi("task deq [cid %d itt 0x%x]\n", conn->id, task->itt);
|
||||
|
||||
error = iser_send_control(conn, mtask);
|
||||
error = iser_send_control(conn, task);
|
||||
|
||||
/* since iser xmits control with zero copy, mtasks can not be recycled
|
||||
/* since iser xmits control with zero copy, tasks can not be recycled
|
||||
* right after sending them.
|
||||
* The recycling scheme is based on whether a response is expected
|
||||
* - if yes, the mtask is recycled at iscsi_complete_pdu
|
||||
* - if no, the mtask is recycled at iser_snd_completion
|
||||
* - if yes, the task is recycled at iscsi_complete_pdu
|
||||
* - if no, the task is recycled at iser_snd_completion
|
||||
*/
|
||||
if (error && error != -ENOBUFS)
|
||||
iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
|
||||
|
@ -172,99 +177,88 @@ iscsi_iser_mtask_xmit(struct iscsi_conn *conn,
|
|||
}
|
||||
|
||||
static int
|
||||
iscsi_iser_ctask_xmit_unsol_data(struct iscsi_conn *conn,
|
||||
struct iscsi_cmd_task *ctask)
|
||||
iscsi_iser_task_xmit_unsol_data(struct iscsi_conn *conn,
|
||||
struct iscsi_task *task)
|
||||
{
|
||||
struct iscsi_data hdr;
|
||||
int error = 0;
|
||||
|
||||
/* Send data-out PDUs while there's still unsolicited data to send */
|
||||
while (ctask->unsol_count > 0) {
|
||||
iscsi_prep_unsolicit_data_pdu(ctask, &hdr);
|
||||
while (task->unsol_count > 0) {
|
||||
iscsi_prep_unsolicit_data_pdu(task, &hdr);
|
||||
debug_scsi("Sending data-out: itt 0x%x, data count %d\n",
|
||||
hdr.itt, ctask->data_count);
|
||||
hdr.itt, task->data_count);
|
||||
|
||||
/* the buffer description has been passed with the command */
|
||||
/* Send the command */
|
||||
error = iser_send_data_out(conn, ctask, &hdr);
|
||||
error = iser_send_data_out(conn, task, &hdr);
|
||||
if (error) {
|
||||
ctask->unsol_datasn--;
|
||||
goto iscsi_iser_ctask_xmit_unsol_data_exit;
|
||||
task->unsol_datasn--;
|
||||
goto iscsi_iser_task_xmit_unsol_data_exit;
|
||||
}
|
||||
ctask->unsol_count -= ctask->data_count;
|
||||
task->unsol_count -= task->data_count;
|
||||
debug_scsi("Need to send %d more as data-out PDUs\n",
|
||||
ctask->unsol_count);
|
||||
task->unsol_count);
|
||||
}
|
||||
|
||||
iscsi_iser_ctask_xmit_unsol_data_exit:
|
||||
iscsi_iser_task_xmit_unsol_data_exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
static int
|
||||
iscsi_iser_ctask_xmit(struct iscsi_conn *conn,
|
||||
struct iscsi_cmd_task *ctask)
|
||||
iscsi_iser_task_xmit(struct iscsi_task *task)
|
||||
{
|
||||
struct iscsi_iser_cmd_task *iser_ctask = ctask->dd_data;
|
||||
struct iscsi_conn *conn = task->conn;
|
||||
struct iscsi_iser_task *iser_task = task->dd_data;
|
||||
int error = 0;
|
||||
|
||||
if (ctask->sc->sc_data_direction == DMA_TO_DEVICE) {
|
||||
BUG_ON(scsi_bufflen(ctask->sc) == 0);
|
||||
if (!task->sc)
|
||||
return iscsi_iser_mtask_xmit(conn, task);
|
||||
|
||||
if (task->sc->sc_data_direction == DMA_TO_DEVICE) {
|
||||
BUG_ON(scsi_bufflen(task->sc) == 0);
|
||||
|
||||
debug_scsi("cmd [itt %x total %d imm %d unsol_data %d\n",
|
||||
ctask->itt, scsi_bufflen(ctask->sc),
|
||||
ctask->imm_count, ctask->unsol_count);
|
||||
task->itt, scsi_bufflen(task->sc),
|
||||
task->imm_count, task->unsol_count);
|
||||
}
|
||||
|
||||
debug_scsi("ctask deq [cid %d itt 0x%x]\n",
|
||||
conn->id, ctask->itt);
|
||||
debug_scsi("task deq [cid %d itt 0x%x]\n",
|
||||
conn->id, task->itt);
|
||||
|
||||
/* Send the cmd PDU */
|
||||
if (!iser_ctask->command_sent) {
|
||||
error = iser_send_command(conn, ctask);
|
||||
if (!iser_task->command_sent) {
|
||||
error = iser_send_command(conn, task);
|
||||
if (error)
|
||||
goto iscsi_iser_ctask_xmit_exit;
|
||||
iser_ctask->command_sent = 1;
|
||||
goto iscsi_iser_task_xmit_exit;
|
||||
iser_task->command_sent = 1;
|
||||
}
|
||||
|
||||
/* Send unsolicited data-out PDU(s) if necessary */
|
||||
if (ctask->unsol_count)
|
||||
error = iscsi_iser_ctask_xmit_unsol_data(conn, ctask);
|
||||
if (task->unsol_count)
|
||||
error = iscsi_iser_task_xmit_unsol_data(conn, task);
|
||||
|
||||
iscsi_iser_ctask_xmit_exit:
|
||||
iscsi_iser_task_xmit_exit:
|
||||
if (error && error != -ENOBUFS)
|
||||
iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
|
||||
return error;
|
||||
}
|
||||
|
||||
static void
|
||||
iscsi_iser_cleanup_ctask(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
|
||||
iscsi_iser_cleanup_task(struct iscsi_conn *conn, struct iscsi_task *task)
|
||||
{
|
||||
struct iscsi_iser_cmd_task *iser_ctask = ctask->dd_data;
|
||||
struct iscsi_iser_task *iser_task = task->dd_data;
|
||||
|
||||
if (iser_ctask->status == ISER_TASK_STATUS_STARTED) {
|
||||
iser_ctask->status = ISER_TASK_STATUS_COMPLETED;
|
||||
iser_ctask_rdma_finalize(iser_ctask);
|
||||
/* mgmt tasks do not need special cleanup */
|
||||
if (!task->sc)
|
||||
return;
|
||||
|
||||
if (iser_task->status == ISER_TASK_STATUS_STARTED) {
|
||||
iser_task->status = ISER_TASK_STATUS_COMPLETED;
|
||||
iser_task_rdma_finalize(iser_task);
|
||||
}
|
||||
}
|
||||
|
||||
static struct iser_conn *
|
||||
iscsi_iser_ib_conn_lookup(__u64 ep_handle)
|
||||
{
|
||||
struct iser_conn *ib_conn;
|
||||
struct iser_conn *uib_conn = (struct iser_conn *)(unsigned long)ep_handle;
|
||||
|
||||
mutex_lock(&ig.connlist_mutex);
|
||||
list_for_each_entry(ib_conn, &ig.connlist, conn_list) {
|
||||
if (ib_conn == uib_conn) {
|
||||
mutex_unlock(&ig.connlist_mutex);
|
||||
return ib_conn;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&ig.connlist_mutex);
|
||||
iser_err("no conn exists for eph %llx\n",(unsigned long long)ep_handle);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct iscsi_cls_conn *
|
||||
iscsi_iser_conn_create(struct iscsi_cls_session *cls_session, uint32_t conn_idx)
|
||||
{
|
||||
|
@ -272,7 +266,7 @@ iscsi_iser_conn_create(struct iscsi_cls_session *cls_session, uint32_t conn_idx)
|
|||
struct iscsi_cls_conn *cls_conn;
|
||||
struct iscsi_iser_conn *iser_conn;
|
||||
|
||||
cls_conn = iscsi_conn_setup(cls_session, conn_idx);
|
||||
cls_conn = iscsi_conn_setup(cls_session, sizeof(*iser_conn), conn_idx);
|
||||
if (!cls_conn)
|
||||
return NULL;
|
||||
conn = cls_conn->dd_data;
|
||||
|
@ -283,21 +277,11 @@ iscsi_iser_conn_create(struct iscsi_cls_session *cls_session, uint32_t conn_idx)
|
|||
*/
|
||||
conn->max_recv_dlength = 128;
|
||||
|
||||
iser_conn = kzalloc(sizeof(*iser_conn), GFP_KERNEL);
|
||||
if (!iser_conn)
|
||||
goto conn_alloc_fail;
|
||||
|
||||
/* currently this is the only field which need to be initiated */
|
||||
rwlock_init(&iser_conn->lock);
|
||||
|
||||
iser_conn = conn->dd_data;
|
||||
conn->dd_data = iser_conn;
|
||||
iser_conn->iscsi_conn = conn;
|
||||
|
||||
return cls_conn;
|
||||
|
||||
conn_alloc_fail:
|
||||
iscsi_conn_teardown(cls_conn);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -305,11 +289,18 @@ iscsi_iser_conn_destroy(struct iscsi_cls_conn *cls_conn)
|
|||
{
|
||||
struct iscsi_conn *conn = cls_conn->dd_data;
|
||||
struct iscsi_iser_conn *iser_conn = conn->dd_data;
|
||||
struct iser_conn *ib_conn = iser_conn->ib_conn;
|
||||
|
||||
iscsi_conn_teardown(cls_conn);
|
||||
if (iser_conn->ib_conn)
|
||||
iser_conn->ib_conn->iser_conn = NULL;
|
||||
kfree(iser_conn);
|
||||
/*
|
||||
* Userspace will normally call the stop callback and
|
||||
* already have freed the ib_conn, but if it goofed up then
|
||||
* we free it here.
|
||||
*/
|
||||
if (ib_conn) {
|
||||
ib_conn->iser_conn = NULL;
|
||||
iser_conn_put(ib_conn);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -320,6 +311,7 @@ iscsi_iser_conn_bind(struct iscsi_cls_session *cls_session,
|
|||
struct iscsi_conn *conn = cls_conn->dd_data;
|
||||
struct iscsi_iser_conn *iser_conn;
|
||||
struct iser_conn *ib_conn;
|
||||
struct iscsi_endpoint *ep;
|
||||
int error;
|
||||
|
||||
error = iscsi_conn_bind(cls_session, cls_conn, is_leading);
|
||||
|
@ -328,12 +320,14 @@ iscsi_iser_conn_bind(struct iscsi_cls_session *cls_session,
|
|||
|
||||
/* the transport ep handle comes from user space so it must be
|
||||
* verified against the global ib connections list */
|
||||
ib_conn = iscsi_iser_ib_conn_lookup(transport_eph);
|
||||
if (!ib_conn) {
|
||||
ep = iscsi_lookup_endpoint(transport_eph);
|
||||
if (!ep) {
|
||||
iser_err("can't bind eph %llx\n",
|
||||
(unsigned long long)transport_eph);
|
||||
return -EINVAL;
|
||||
}
|
||||
ib_conn = ep->dd_data;
|
||||
|
||||
/* binds the iSER connection retrieved from the previously
|
||||
* connected ep_handle to the iSCSI layer connection. exchanges
|
||||
* connection pointers */
|
||||
|
@ -341,12 +335,32 @@ iscsi_iser_conn_bind(struct iscsi_cls_session *cls_session,
|
|||
iser_conn = conn->dd_data;
|
||||
ib_conn->iser_conn = iser_conn;
|
||||
iser_conn->ib_conn = ib_conn;
|
||||
|
||||
conn->recv_lock = &iser_conn->lock;
|
||||
|
||||
iser_conn_get(ib_conn);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
iscsi_iser_conn_stop(struct iscsi_cls_conn *cls_conn, int flag)
|
||||
{
|
||||
struct iscsi_conn *conn = cls_conn->dd_data;
|
||||
struct iscsi_iser_conn *iser_conn = conn->dd_data;
|
||||
struct iser_conn *ib_conn = iser_conn->ib_conn;
|
||||
|
||||
/*
|
||||
* Userspace may have goofed up and not bound the connection or
|
||||
* might have only partially setup the connection.
|
||||
*/
|
||||
if (ib_conn) {
|
||||
iscsi_conn_stop(cls_conn, flag);
|
||||
/*
|
||||
* There is no unbind event so the stop callback
|
||||
* must release the ref from the bind.
|
||||
*/
|
||||
iser_conn_put(ib_conn);
|
||||
}
|
||||
iser_conn->ib_conn = NULL;
|
||||
}
|
||||
|
||||
static int
|
||||
iscsi_iser_conn_start(struct iscsi_cls_conn *cls_conn)
|
||||
{
|
||||
|
@ -360,55 +374,75 @@ iscsi_iser_conn_start(struct iscsi_cls_conn *cls_conn)
|
|||
return iscsi_conn_start(cls_conn);
|
||||
}
|
||||
|
||||
static struct iscsi_transport iscsi_iser_transport;
|
||||
static void iscsi_iser_session_destroy(struct iscsi_cls_session *cls_session)
|
||||
{
|
||||
struct Scsi_Host *shost = iscsi_session_to_shost(cls_session);
|
||||
|
||||
iscsi_host_remove(shost);
|
||||
iscsi_host_free(shost);
|
||||
}
|
||||
|
||||
static struct iscsi_cls_session *
|
||||
iscsi_iser_session_create(struct iscsi_transport *iscsit,
|
||||
struct scsi_transport_template *scsit,
|
||||
uint16_t cmds_max, uint16_t qdepth,
|
||||
uint32_t initial_cmdsn, uint32_t *hostno)
|
||||
iscsi_iser_session_create(struct iscsi_endpoint *ep,
|
||||
uint16_t cmds_max, uint16_t qdepth,
|
||||
uint32_t initial_cmdsn, uint32_t *hostno)
|
||||
{
|
||||
struct iscsi_cls_session *cls_session;
|
||||
struct iscsi_session *session;
|
||||
struct Scsi_Host *shost;
|
||||
int i;
|
||||
uint32_t hn;
|
||||
struct iscsi_cmd_task *ctask;
|
||||
struct iscsi_mgmt_task *mtask;
|
||||
struct iscsi_iser_cmd_task *iser_ctask;
|
||||
struct iser_desc *desc;
|
||||
struct iscsi_task *task;
|
||||
struct iscsi_iser_task *iser_task;
|
||||
struct iser_conn *ib_conn;
|
||||
|
||||
shost = iscsi_host_alloc(&iscsi_iser_sht, 0, ISCSI_MAX_CMD_PER_LUN);
|
||||
if (!shost)
|
||||
return NULL;
|
||||
shost->transportt = iscsi_iser_scsi_transport;
|
||||
shost->max_lun = iscsi_max_lun;
|
||||
shost->max_id = 0;
|
||||
shost->max_channel = 0;
|
||||
shost->max_cmd_len = 16;
|
||||
|
||||
/*
|
||||
* older userspace tools (before 2.0-870) did not pass us
|
||||
* the leading conn's ep so this will be NULL;
|
||||
*/
|
||||
if (ep)
|
||||
ib_conn = ep->dd_data;
|
||||
|
||||
if (iscsi_host_add(shost,
|
||||
ep ? ib_conn->device->ib_device->dma_device : NULL))
|
||||
goto free_host;
|
||||
*hostno = shost->host_no;
|
||||
|
||||
/*
|
||||
* we do not support setting can_queue cmd_per_lun from userspace yet
|
||||
* because we preallocate so many resources
|
||||
*/
|
||||
cls_session = iscsi_session_setup(iscsit, scsit,
|
||||
cls_session = iscsi_session_setup(&iscsi_iser_transport, shost,
|
||||
ISCSI_DEF_XMIT_CMDS_MAX,
|
||||
ISCSI_MAX_CMD_PER_LUN,
|
||||
sizeof(struct iscsi_iser_cmd_task),
|
||||
sizeof(struct iser_desc),
|
||||
initial_cmdsn, &hn);
|
||||
sizeof(struct iscsi_iser_task),
|
||||
initial_cmdsn, 0);
|
||||
if (!cls_session)
|
||||
return NULL;
|
||||
|
||||
*hostno = hn;
|
||||
session = class_to_transport_session(cls_session);
|
||||
goto remove_host;
|
||||
session = cls_session->dd_data;
|
||||
|
||||
shost->can_queue = session->scsi_cmds_max;
|
||||
/* libiscsi setup itts, data and pool so just set desc fields */
|
||||
for (i = 0; i < session->cmds_max; i++) {
|
||||
ctask = session->cmds[i];
|
||||
iser_ctask = ctask->dd_data;
|
||||
ctask->hdr = (struct iscsi_cmd *)&iser_ctask->desc.iscsi_header;
|
||||
ctask->hdr_max = sizeof(iser_ctask->desc.iscsi_header);
|
||||
task = session->cmds[i];
|
||||
iser_task = task->dd_data;
|
||||
task->hdr = (struct iscsi_cmd *)&iser_task->desc.iscsi_header;
|
||||
task->hdr_max = sizeof(iser_task->desc.iscsi_header);
|
||||
}
|
||||
|
||||
for (i = 0; i < session->mgmtpool_max; i++) {
|
||||
mtask = session->mgmt_cmds[i];
|
||||
desc = mtask->dd_data;
|
||||
mtask->hdr = &desc->iscsi_header;
|
||||
desc->data = mtask->data;
|
||||
}
|
||||
|
||||
return cls_session;
|
||||
|
||||
remove_host:
|
||||
iscsi_host_remove(shost);
|
||||
free_host:
|
||||
iscsi_host_free(shost);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -481,34 +515,37 @@ iscsi_iser_conn_get_stats(struct iscsi_cls_conn *cls_conn, struct iscsi_stats *s
|
|||
stats->custom[3].value = conn->fmr_unalign_cnt;
|
||||
}
|
||||
|
||||
static int
|
||||
iscsi_iser_ep_connect(struct sockaddr *dst_addr, int non_blocking,
|
||||
__u64 *ep_handle)
|
||||
static struct iscsi_endpoint *
|
||||
iscsi_iser_ep_connect(struct sockaddr *dst_addr, int non_blocking)
|
||||
{
|
||||
int err;
|
||||
struct iser_conn *ib_conn;
|
||||
struct iscsi_endpoint *ep;
|
||||
|
||||
err = iser_conn_init(&ib_conn);
|
||||
if (err)
|
||||
goto out;
|
||||
ep = iscsi_create_endpoint(sizeof(*ib_conn));
|
||||
if (!ep)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
err = iser_connect(ib_conn, NULL, (struct sockaddr_in *)dst_addr, non_blocking);
|
||||
if (!err)
|
||||
*ep_handle = (__u64)(unsigned long)ib_conn;
|
||||
ib_conn = ep->dd_data;
|
||||
ib_conn->ep = ep;
|
||||
iser_conn_init(ib_conn);
|
||||
|
||||
out:
|
||||
return err;
|
||||
err = iser_connect(ib_conn, NULL, (struct sockaddr_in *)dst_addr,
|
||||
non_blocking);
|
||||
if (err) {
|
||||
iscsi_destroy_endpoint(ep);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
return ep;
|
||||
}
|
||||
|
||||
static int
|
||||
iscsi_iser_ep_poll(__u64 ep_handle, int timeout_ms)
|
||||
iscsi_iser_ep_poll(struct iscsi_endpoint *ep, int timeout_ms)
|
||||
{
|
||||
struct iser_conn *ib_conn = iscsi_iser_ib_conn_lookup(ep_handle);
|
||||
struct iser_conn *ib_conn;
|
||||
int rc;
|
||||
|
||||
if (!ib_conn)
|
||||
return -EINVAL;
|
||||
|
||||
ib_conn = ep->dd_data;
|
||||
rc = wait_event_interruptible_timeout(ib_conn->wait,
|
||||
ib_conn->state == ISER_CONN_UP,
|
||||
msecs_to_jiffies(timeout_ms));
|
||||
|
@ -530,13 +567,21 @@ iscsi_iser_ep_poll(__u64 ep_handle, int timeout_ms)
|
|||
}
|
||||
|
||||
static void
|
||||
iscsi_iser_ep_disconnect(__u64 ep_handle)
|
||||
iscsi_iser_ep_disconnect(struct iscsi_endpoint *ep)
|
||||
{
|
||||
struct iser_conn *ib_conn;
|
||||
|
||||
ib_conn = iscsi_iser_ib_conn_lookup(ep_handle);
|
||||
if (!ib_conn)
|
||||
return;
|
||||
ib_conn = ep->dd_data;
|
||||
if (ib_conn->iser_conn)
|
||||
/*
|
||||
* Must suspend xmit path if the ep is bound to the
|
||||
* iscsi_conn, so we know we are not accessing the ib_conn
|
||||
* when we free it.
|
||||
*
|
||||
* This may not be bound if the ep poll failed.
|
||||
*/
|
||||
iscsi_suspend_tx(ib_conn->iser_conn->iscsi_conn);
|
||||
|
||||
|
||||
iser_err("ib conn %p state %d\n",ib_conn, ib_conn->state);
|
||||
iser_conn_terminate(ib_conn);
|
||||
|
@ -547,7 +592,6 @@ static struct scsi_host_template iscsi_iser_sht = {
|
|||
.name = "iSCSI Initiator over iSER, v." DRV_VER,
|
||||
.queuecommand = iscsi_queuecommand,
|
||||
.change_queue_depth = iscsi_change_queue_depth,
|
||||
.can_queue = ISCSI_DEF_XMIT_CMDS_MAX - 1,
|
||||
.sg_tablesize = ISCSI_ISER_SG_TABLESIZE,
|
||||
.max_sectors = 1024,
|
||||
.cmd_per_lun = ISCSI_MAX_CMD_PER_LUN,
|
||||
|
@ -581,17 +625,14 @@ static struct iscsi_transport iscsi_iser_transport = {
|
|||
ISCSI_USERNAME | ISCSI_PASSWORD |
|
||||
ISCSI_USERNAME_IN | ISCSI_PASSWORD_IN |
|
||||
ISCSI_FAST_ABORT | ISCSI_ABORT_TMO |
|
||||
ISCSI_PING_TMO | ISCSI_RECV_TMO,
|
||||
ISCSI_PING_TMO | ISCSI_RECV_TMO |
|
||||
ISCSI_IFACE_NAME | ISCSI_INITIATOR_NAME,
|
||||
.host_param_mask = ISCSI_HOST_HWADDRESS |
|
||||
ISCSI_HOST_NETDEV_NAME |
|
||||
ISCSI_HOST_INITIATOR_NAME,
|
||||
.host_template = &iscsi_iser_sht,
|
||||
.conndata_size = sizeof(struct iscsi_conn),
|
||||
.max_lun = ISCSI_ISER_MAX_LUN,
|
||||
.max_cmd_len = ISCSI_ISER_MAX_CMD_LEN,
|
||||
/* session management */
|
||||
.create_session = iscsi_iser_session_create,
|
||||
.destroy_session = iscsi_session_teardown,
|
||||
.destroy_session = iscsi_iser_session_destroy,
|
||||
/* connection management */
|
||||
.create_conn = iscsi_iser_conn_create,
|
||||
.bind_conn = iscsi_iser_conn_bind,
|
||||
|
@ -600,17 +641,16 @@ static struct iscsi_transport iscsi_iser_transport = {
|
|||
.get_conn_param = iscsi_conn_get_param,
|
||||
.get_session_param = iscsi_session_get_param,
|
||||
.start_conn = iscsi_iser_conn_start,
|
||||
.stop_conn = iscsi_conn_stop,
|
||||
.stop_conn = iscsi_iser_conn_stop,
|
||||
/* iscsi host params */
|
||||
.get_host_param = iscsi_host_get_param,
|
||||
.set_host_param = iscsi_host_set_param,
|
||||
/* IO */
|
||||
.send_pdu = iscsi_conn_send_pdu,
|
||||
.get_stats = iscsi_iser_conn_get_stats,
|
||||
.init_cmd_task = iscsi_iser_cmd_init,
|
||||
.xmit_cmd_task = iscsi_iser_ctask_xmit,
|
||||
.xmit_mgmt_task = iscsi_iser_mtask_xmit,
|
||||
.cleanup_cmd_task = iscsi_iser_cleanup_ctask,
|
||||
.init_task = iscsi_iser_task_init,
|
||||
.xmit_task = iscsi_iser_task_xmit,
|
||||
.cleanup_task = iscsi_iser_cleanup_task,
|
||||
/* recovery */
|
||||
.session_recovery_timedout = iscsi_session_recovery_timedout,
|
||||
|
||||
|
@ -630,8 +670,6 @@ static int __init iser_init(void)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
iscsi_iser_transport.max_lun = iscsi_max_lun;
|
||||
|
||||
memset(&ig, 0, sizeof(struct iser_global));
|
||||
|
||||
ig.desc_cache = kmem_cache_create("iser_descriptors",
|
||||
|
@ -647,7 +685,9 @@ static int __init iser_init(void)
|
|||
mutex_init(&ig.connlist_mutex);
|
||||
INIT_LIST_HEAD(&ig.connlist);
|
||||
|
||||
if (!iscsi_register_transport(&iscsi_iser_transport)) {
|
||||
iscsi_iser_scsi_transport = iscsi_register_transport(
|
||||
&iscsi_iser_transport);
|
||||
if (!iscsi_iser_scsi_transport) {
|
||||
iser_err("iscsi_register_transport failed\n");
|
||||
err = -EINVAL;
|
||||
goto register_transport_failure;
|
||||
|
|
|
@ -94,7 +94,6 @@
|
|||
/* support upto 512KB in one RDMA */
|
||||
#define ISCSI_ISER_SG_TABLESIZE (0x80000 >> SHIFT_4K)
|
||||
#define ISCSI_ISER_MAX_LUN 256
|
||||
#define ISCSI_ISER_MAX_CMD_LEN 16
|
||||
|
||||
/* QP settings */
|
||||
/* Maximal bounds on received asynchronous PDUs */
|
||||
|
@ -172,7 +171,8 @@ struct iser_data_buf {
|
|||
/* fwd declarations */
|
||||
struct iser_device;
|
||||
struct iscsi_iser_conn;
|
||||
struct iscsi_iser_cmd_task;
|
||||
struct iscsi_iser_task;
|
||||
struct iscsi_endpoint;
|
||||
|
||||
struct iser_mem_reg {
|
||||
u32 lkey;
|
||||
|
@ -196,7 +196,7 @@ struct iser_regd_buf {
|
|||
#define MAX_REGD_BUF_VECTOR_LEN 2
|
||||
|
||||
struct iser_dto {
|
||||
struct iscsi_iser_cmd_task *ctask;
|
||||
struct iscsi_iser_task *task;
|
||||
struct iser_conn *ib_conn;
|
||||
int notify_enable;
|
||||
|
||||
|
@ -240,7 +240,9 @@ struct iser_device {
|
|||
|
||||
struct iser_conn {
|
||||
struct iscsi_iser_conn *iser_conn; /* iser conn for upcalls */
|
||||
struct iscsi_endpoint *ep;
|
||||
enum iser_ib_conn_state state; /* rdma connection state */
|
||||
atomic_t refcount;
|
||||
spinlock_t lock; /* used for state changes */
|
||||
struct iser_device *device; /* device context */
|
||||
struct rdma_cm_id *cma_id; /* CMA ID */
|
||||
|
@ -259,11 +261,9 @@ struct iser_conn {
|
|||
struct iscsi_iser_conn {
|
||||
struct iscsi_conn *iscsi_conn;/* ptr to iscsi conn */
|
||||
struct iser_conn *ib_conn; /* iSER IB conn */
|
||||
|
||||
rwlock_t lock;
|
||||
};
|
||||
|
||||
struct iscsi_iser_cmd_task {
|
||||
struct iscsi_iser_task {
|
||||
struct iser_desc desc;
|
||||
struct iscsi_iser_conn *iser_conn;
|
||||
enum iser_task_status status;
|
||||
|
@ -296,22 +296,26 @@ extern int iser_debug_level;
|
|||
/* allocate connection resources needed for rdma functionality */
|
||||
int iser_conn_set_full_featured_mode(struct iscsi_conn *conn);
|
||||
|
||||
int iser_send_control(struct iscsi_conn *conn,
|
||||
struct iscsi_mgmt_task *mtask);
|
||||
int iser_send_control(struct iscsi_conn *conn,
|
||||
struct iscsi_task *task);
|
||||
|
||||
int iser_send_command(struct iscsi_conn *conn,
|
||||
struct iscsi_cmd_task *ctask);
|
||||
int iser_send_command(struct iscsi_conn *conn,
|
||||
struct iscsi_task *task);
|
||||
|
||||
int iser_send_data_out(struct iscsi_conn *conn,
|
||||
struct iscsi_cmd_task *ctask,
|
||||
struct iscsi_data *hdr);
|
||||
int iser_send_data_out(struct iscsi_conn *conn,
|
||||
struct iscsi_task *task,
|
||||
struct iscsi_data *hdr);
|
||||
|
||||
void iscsi_iser_recv(struct iscsi_conn *conn,
|
||||
struct iscsi_hdr *hdr,
|
||||
char *rx_data,
|
||||
int rx_data_len);
|
||||
|
||||
int iser_conn_init(struct iser_conn **ib_conn);
|
||||
void iser_conn_init(struct iser_conn *ib_conn);
|
||||
|
||||
void iser_conn_get(struct iser_conn *ib_conn);
|
||||
|
||||
void iser_conn_put(struct iser_conn *ib_conn);
|
||||
|
||||
void iser_conn_terminate(struct iser_conn *ib_conn);
|
||||
|
||||
|
@ -320,9 +324,9 @@ void iser_rcv_completion(struct iser_desc *desc,
|
|||
|
||||
void iser_snd_completion(struct iser_desc *desc);
|
||||
|
||||
void iser_ctask_rdma_init(struct iscsi_iser_cmd_task *ctask);
|
||||
void iser_task_rdma_init(struct iscsi_iser_task *task);
|
||||
|
||||
void iser_ctask_rdma_finalize(struct iscsi_iser_cmd_task *ctask);
|
||||
void iser_task_rdma_finalize(struct iscsi_iser_task *task);
|
||||
|
||||
void iser_dto_buffs_release(struct iser_dto *dto);
|
||||
|
||||
|
@ -332,10 +336,10 @@ void iser_reg_single(struct iser_device *device,
|
|||
struct iser_regd_buf *regd_buf,
|
||||
enum dma_data_direction direction);
|
||||
|
||||
void iser_finalize_rdma_unaligned_sg(struct iscsi_iser_cmd_task *ctask,
|
||||
void iser_finalize_rdma_unaligned_sg(struct iscsi_iser_task *task,
|
||||
enum iser_data_dir cmd_dir);
|
||||
|
||||
int iser_reg_rdma_mem(struct iscsi_iser_cmd_task *ctask,
|
||||
int iser_reg_rdma_mem(struct iscsi_iser_task *task,
|
||||
enum iser_data_dir cmd_dir);
|
||||
|
||||
int iser_connect(struct iser_conn *ib_conn,
|
||||
|
@ -355,10 +359,10 @@ int iser_post_send(struct iser_desc *tx_desc);
|
|||
int iser_conn_state_comp(struct iser_conn *ib_conn,
|
||||
enum iser_ib_conn_state comp);
|
||||
|
||||
int iser_dma_map_task_data(struct iscsi_iser_cmd_task *iser_ctask,
|
||||
int iser_dma_map_task_data(struct iscsi_iser_task *iser_task,
|
||||
struct iser_data_buf *data,
|
||||
enum iser_data_dir iser_dir,
|
||||
enum dma_data_direction dma_dir);
|
||||
|
||||
void iser_dma_unmap_task_data(struct iscsi_iser_cmd_task *iser_ctask);
|
||||
void iser_dma_unmap_task_data(struct iscsi_iser_task *iser_task);
|
||||
#endif
|
||||
|
|
|
@ -64,46 +64,46 @@ static void iser_dto_add_regd_buff(struct iser_dto *dto,
|
|||
|
||||
/* Register user buffer memory and initialize passive rdma
|
||||
* dto descriptor. Total data size is stored in
|
||||
* iser_ctask->data[ISER_DIR_IN].data_len
|
||||
* iser_task->data[ISER_DIR_IN].data_len
|
||||
*/
|
||||
static int iser_prepare_read_cmd(struct iscsi_cmd_task *ctask,
|
||||
static int iser_prepare_read_cmd(struct iscsi_task *task,
|
||||
unsigned int edtl)
|
||||
|
||||
{
|
||||
struct iscsi_iser_cmd_task *iser_ctask = ctask->dd_data;
|
||||
struct iscsi_iser_task *iser_task = task->dd_data;
|
||||
struct iser_regd_buf *regd_buf;
|
||||
int err;
|
||||
struct iser_hdr *hdr = &iser_ctask->desc.iser_header;
|
||||
struct iser_data_buf *buf_in = &iser_ctask->data[ISER_DIR_IN];
|
||||
struct iser_hdr *hdr = &iser_task->desc.iser_header;
|
||||
struct iser_data_buf *buf_in = &iser_task->data[ISER_DIR_IN];
|
||||
|
||||
err = iser_dma_map_task_data(iser_ctask,
|
||||
err = iser_dma_map_task_data(iser_task,
|
||||
buf_in,
|
||||
ISER_DIR_IN,
|
||||
DMA_FROM_DEVICE);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (edtl > iser_ctask->data[ISER_DIR_IN].data_len) {
|
||||
if (edtl > iser_task->data[ISER_DIR_IN].data_len) {
|
||||
iser_err("Total data length: %ld, less than EDTL: "
|
||||
"%d, in READ cmd BHS itt: %d, conn: 0x%p\n",
|
||||
iser_ctask->data[ISER_DIR_IN].data_len, edtl,
|
||||
ctask->itt, iser_ctask->iser_conn);
|
||||
iser_task->data[ISER_DIR_IN].data_len, edtl,
|
||||
task->itt, iser_task->iser_conn);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = iser_reg_rdma_mem(iser_ctask,ISER_DIR_IN);
|
||||
err = iser_reg_rdma_mem(iser_task,ISER_DIR_IN);
|
||||
if (err) {
|
||||
iser_err("Failed to set up Data-IN RDMA\n");
|
||||
return err;
|
||||
}
|
||||
regd_buf = &iser_ctask->rdma_regd[ISER_DIR_IN];
|
||||
regd_buf = &iser_task->rdma_regd[ISER_DIR_IN];
|
||||
|
||||
hdr->flags |= ISER_RSV;
|
||||
hdr->read_stag = cpu_to_be32(regd_buf->reg.rkey);
|
||||
hdr->read_va = cpu_to_be64(regd_buf->reg.va);
|
||||
|
||||
iser_dbg("Cmd itt:%d READ tags RKEY:%#.4X VA:%#llX\n",
|
||||
ctask->itt, regd_buf->reg.rkey,
|
||||
task->itt, regd_buf->reg.rkey,
|
||||
(unsigned long long)regd_buf->reg.va);
|
||||
|
||||
return 0;
|
||||
|
@ -111,43 +111,43 @@ static int iser_prepare_read_cmd(struct iscsi_cmd_task *ctask,
|
|||
|
||||
/* Register user buffer memory and initialize passive rdma
|
||||
* dto descriptor. Total data size is stored in
|
||||
* ctask->data[ISER_DIR_OUT].data_len
|
||||
* task->data[ISER_DIR_OUT].data_len
|
||||
*/
|
||||
static int
|
||||
iser_prepare_write_cmd(struct iscsi_cmd_task *ctask,
|
||||
iser_prepare_write_cmd(struct iscsi_task *task,
|
||||
unsigned int imm_sz,
|
||||
unsigned int unsol_sz,
|
||||
unsigned int edtl)
|
||||
{
|
||||
struct iscsi_iser_cmd_task *iser_ctask = ctask->dd_data;
|
||||
struct iscsi_iser_task *iser_task = task->dd_data;
|
||||
struct iser_regd_buf *regd_buf;
|
||||
int err;
|
||||
struct iser_dto *send_dto = &iser_ctask->desc.dto;
|
||||
struct iser_hdr *hdr = &iser_ctask->desc.iser_header;
|
||||
struct iser_data_buf *buf_out = &iser_ctask->data[ISER_DIR_OUT];
|
||||
struct iser_dto *send_dto = &iser_task->desc.dto;
|
||||
struct iser_hdr *hdr = &iser_task->desc.iser_header;
|
||||
struct iser_data_buf *buf_out = &iser_task->data[ISER_DIR_OUT];
|
||||
|
||||
err = iser_dma_map_task_data(iser_ctask,
|
||||
err = iser_dma_map_task_data(iser_task,
|
||||
buf_out,
|
||||
ISER_DIR_OUT,
|
||||
DMA_TO_DEVICE);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (edtl > iser_ctask->data[ISER_DIR_OUT].data_len) {
|
||||
if (edtl > iser_task->data[ISER_DIR_OUT].data_len) {
|
||||
iser_err("Total data length: %ld, less than EDTL: %d, "
|
||||
"in WRITE cmd BHS itt: %d, conn: 0x%p\n",
|
||||
iser_ctask->data[ISER_DIR_OUT].data_len,
|
||||
edtl, ctask->itt, ctask->conn);
|
||||
iser_task->data[ISER_DIR_OUT].data_len,
|
||||
edtl, task->itt, task->conn);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = iser_reg_rdma_mem(iser_ctask,ISER_DIR_OUT);
|
||||
err = iser_reg_rdma_mem(iser_task,ISER_DIR_OUT);
|
||||
if (err != 0) {
|
||||
iser_err("Failed to register write cmd RDMA mem\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
regd_buf = &iser_ctask->rdma_regd[ISER_DIR_OUT];
|
||||
regd_buf = &iser_task->rdma_regd[ISER_DIR_OUT];
|
||||
|
||||
if (unsol_sz < edtl) {
|
||||
hdr->flags |= ISER_WSV;
|
||||
|
@ -156,13 +156,13 @@ iser_prepare_write_cmd(struct iscsi_cmd_task *ctask,
|
|||
|
||||
iser_dbg("Cmd itt:%d, WRITE tags, RKEY:%#.4X "
|
||||
"VA:%#llX + unsol:%d\n",
|
||||
ctask->itt, regd_buf->reg.rkey,
|
||||
task->itt, regd_buf->reg.rkey,
|
||||
(unsigned long long)regd_buf->reg.va, unsol_sz);
|
||||
}
|
||||
|
||||
if (imm_sz > 0) {
|
||||
iser_dbg("Cmd itt:%d, WRITE, adding imm.data sz: %d\n",
|
||||
ctask->itt, imm_sz);
|
||||
task->itt, imm_sz);
|
||||
iser_dto_add_regd_buff(send_dto,
|
||||
regd_buf,
|
||||
0,
|
||||
|
@ -314,38 +314,38 @@ iser_check_xmit(struct iscsi_conn *conn, void *task)
|
|||
/**
|
||||
* iser_send_command - send command PDU
|
||||
*/
|
||||
int iser_send_command(struct iscsi_conn *conn,
|
||||
struct iscsi_cmd_task *ctask)
|
||||
int iser_send_command(struct iscsi_conn *conn,
|
||||
struct iscsi_task *task)
|
||||
{
|
||||
struct iscsi_iser_conn *iser_conn = conn->dd_data;
|
||||
struct iscsi_iser_cmd_task *iser_ctask = ctask->dd_data;
|
||||
struct iscsi_iser_task *iser_task = task->dd_data;
|
||||
struct iser_dto *send_dto = NULL;
|
||||
unsigned long edtl;
|
||||
int err = 0;
|
||||
struct iser_data_buf *data_buf;
|
||||
|
||||
struct iscsi_cmd *hdr = ctask->hdr;
|
||||
struct scsi_cmnd *sc = ctask->sc;
|
||||
struct iscsi_cmd *hdr = task->hdr;
|
||||
struct scsi_cmnd *sc = task->sc;
|
||||
|
||||
if (!iser_conn_state_comp(iser_conn->ib_conn, ISER_CONN_UP)) {
|
||||
iser_err("Failed to send, conn: 0x%p is not up\n", iser_conn->ib_conn);
|
||||
return -EPERM;
|
||||
}
|
||||
if (iser_check_xmit(conn, ctask))
|
||||
if (iser_check_xmit(conn, task))
|
||||
return -ENOBUFS;
|
||||
|
||||
edtl = ntohl(hdr->data_length);
|
||||
|
||||
/* build the tx desc regd header and add it to the tx desc dto */
|
||||
iser_ctask->desc.type = ISCSI_TX_SCSI_COMMAND;
|
||||
send_dto = &iser_ctask->desc.dto;
|
||||
send_dto->ctask = iser_ctask;
|
||||
iser_create_send_desc(iser_conn, &iser_ctask->desc);
|
||||
iser_task->desc.type = ISCSI_TX_SCSI_COMMAND;
|
||||
send_dto = &iser_task->desc.dto;
|
||||
send_dto->task = iser_task;
|
||||
iser_create_send_desc(iser_conn, &iser_task->desc);
|
||||
|
||||
if (hdr->flags & ISCSI_FLAG_CMD_READ)
|
||||
data_buf = &iser_ctask->data[ISER_DIR_IN];
|
||||
data_buf = &iser_task->data[ISER_DIR_IN];
|
||||
else
|
||||
data_buf = &iser_ctask->data[ISER_DIR_OUT];
|
||||
data_buf = &iser_task->data[ISER_DIR_OUT];
|
||||
|
||||
if (scsi_sg_count(sc)) { /* using a scatter list */
|
||||
data_buf->buf = scsi_sglist(sc);
|
||||
|
@ -355,15 +355,15 @@ int iser_send_command(struct iscsi_conn *conn,
|
|||
data_buf->data_len = scsi_bufflen(sc);
|
||||
|
||||
if (hdr->flags & ISCSI_FLAG_CMD_READ) {
|
||||
err = iser_prepare_read_cmd(ctask, edtl);
|
||||
err = iser_prepare_read_cmd(task, edtl);
|
||||
if (err)
|
||||
goto send_command_error;
|
||||
}
|
||||
if (hdr->flags & ISCSI_FLAG_CMD_WRITE) {
|
||||
err = iser_prepare_write_cmd(ctask,
|
||||
ctask->imm_count,
|
||||
ctask->imm_count +
|
||||
ctask->unsol_count,
|
||||
err = iser_prepare_write_cmd(task,
|
||||
task->imm_count,
|
||||
task->imm_count +
|
||||
task->unsol_count,
|
||||
edtl);
|
||||
if (err)
|
||||
goto send_command_error;
|
||||
|
@ -378,27 +378,27 @@ int iser_send_command(struct iscsi_conn *conn,
|
|||
goto send_command_error;
|
||||
}
|
||||
|
||||
iser_ctask->status = ISER_TASK_STATUS_STARTED;
|
||||
iser_task->status = ISER_TASK_STATUS_STARTED;
|
||||
|
||||
err = iser_post_send(&iser_ctask->desc);
|
||||
err = iser_post_send(&iser_task->desc);
|
||||
if (!err)
|
||||
return 0;
|
||||
|
||||
send_command_error:
|
||||
iser_dto_buffs_release(send_dto);
|
||||
iser_err("conn %p failed ctask->itt %d err %d\n",conn, ctask->itt, err);
|
||||
iser_err("conn %p failed task->itt %d err %d\n",conn, task->itt, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* iser_send_data_out - send data out PDU
|
||||
*/
|
||||
int iser_send_data_out(struct iscsi_conn *conn,
|
||||
struct iscsi_cmd_task *ctask,
|
||||
int iser_send_data_out(struct iscsi_conn *conn,
|
||||
struct iscsi_task *task,
|
||||
struct iscsi_data *hdr)
|
||||
{
|
||||
struct iscsi_iser_conn *iser_conn = conn->dd_data;
|
||||
struct iscsi_iser_cmd_task *iser_ctask = ctask->dd_data;
|
||||
struct iscsi_iser_task *iser_task = task->dd_data;
|
||||
struct iser_desc *tx_desc = NULL;
|
||||
struct iser_dto *send_dto = NULL;
|
||||
unsigned long buf_offset;
|
||||
|
@ -411,7 +411,7 @@ int iser_send_data_out(struct iscsi_conn *conn,
|
|||
return -EPERM;
|
||||
}
|
||||
|
||||
if (iser_check_xmit(conn, ctask))
|
||||
if (iser_check_xmit(conn, task))
|
||||
return -ENOBUFS;
|
||||
|
||||
itt = (__force uint32_t)hdr->itt;
|
||||
|
@ -432,7 +432,7 @@ int iser_send_data_out(struct iscsi_conn *conn,
|
|||
|
||||
/* build the tx desc regd header and add it to the tx desc dto */
|
||||
send_dto = &tx_desc->dto;
|
||||
send_dto->ctask = iser_ctask;
|
||||
send_dto->task = iser_task;
|
||||
iser_create_send_desc(iser_conn, tx_desc);
|
||||
|
||||
iser_reg_single(iser_conn->ib_conn->device,
|
||||
|
@ -440,15 +440,15 @@ int iser_send_data_out(struct iscsi_conn *conn,
|
|||
|
||||
/* all data was registered for RDMA, we can use the lkey */
|
||||
iser_dto_add_regd_buff(send_dto,
|
||||
&iser_ctask->rdma_regd[ISER_DIR_OUT],
|
||||
&iser_task->rdma_regd[ISER_DIR_OUT],
|
||||
buf_offset,
|
||||
data_seg_len);
|
||||
|
||||
if (buf_offset + data_seg_len > iser_ctask->data[ISER_DIR_OUT].data_len) {
|
||||
if (buf_offset + data_seg_len > iser_task->data[ISER_DIR_OUT].data_len) {
|
||||
iser_err("Offset:%ld & DSL:%ld in Data-Out "
|
||||
"inconsistent with total len:%ld, itt:%d\n",
|
||||
buf_offset, data_seg_len,
|
||||
iser_ctask->data[ISER_DIR_OUT].data_len, itt);
|
||||
iser_task->data[ISER_DIR_OUT].data_len, itt);
|
||||
err = -EINVAL;
|
||||
goto send_data_out_error;
|
||||
}
|
||||
|
@ -468,10 +468,11 @@ send_data_out_error:
|
|||
}
|
||||
|
||||
int iser_send_control(struct iscsi_conn *conn,
|
||||
struct iscsi_mgmt_task *mtask)
|
||||
struct iscsi_task *task)
|
||||
{
|
||||
struct iscsi_iser_conn *iser_conn = conn->dd_data;
|
||||
struct iser_desc *mdesc = mtask->dd_data;
|
||||
struct iscsi_iser_task *iser_task = task->dd_data;
|
||||
struct iser_desc *mdesc = &iser_task->desc;
|
||||
struct iser_dto *send_dto = NULL;
|
||||
unsigned long data_seg_len;
|
||||
int err = 0;
|
||||
|
@ -483,27 +484,27 @@ int iser_send_control(struct iscsi_conn *conn,
|
|||
return -EPERM;
|
||||
}
|
||||
|
||||
if (iser_check_xmit(conn,mtask))
|
||||
if (iser_check_xmit(conn, task))
|
||||
return -ENOBUFS;
|
||||
|
||||
/* build the tx desc regd header and add it to the tx desc dto */
|
||||
mdesc->type = ISCSI_TX_CONTROL;
|
||||
send_dto = &mdesc->dto;
|
||||
send_dto->ctask = NULL;
|
||||
send_dto->task = NULL;
|
||||
iser_create_send_desc(iser_conn, mdesc);
|
||||
|
||||
device = iser_conn->ib_conn->device;
|
||||
|
||||
iser_reg_single(device, send_dto->regd[0], DMA_TO_DEVICE);
|
||||
|
||||
data_seg_len = ntoh24(mtask->hdr->dlength);
|
||||
data_seg_len = ntoh24(task->hdr->dlength);
|
||||
|
||||
if (data_seg_len > 0) {
|
||||
regd_buf = &mdesc->data_regd_buf;
|
||||
memset(regd_buf, 0, sizeof(struct iser_regd_buf));
|
||||
regd_buf->device = device;
|
||||
regd_buf->virt_addr = mtask->data;
|
||||
regd_buf->data_size = mtask->data_count;
|
||||
regd_buf->virt_addr = task->data;
|
||||
regd_buf->data_size = task->data_count;
|
||||
iser_reg_single(device, regd_buf,
|
||||
DMA_TO_DEVICE);
|
||||
iser_dto_add_regd_buff(send_dto, regd_buf,
|
||||
|
@ -533,15 +534,13 @@ send_control_error:
|
|||
void iser_rcv_completion(struct iser_desc *rx_desc,
|
||||
unsigned long dto_xfer_len)
|
||||
{
|
||||
struct iser_dto *dto = &rx_desc->dto;
|
||||
struct iser_dto *dto = &rx_desc->dto;
|
||||
struct iscsi_iser_conn *conn = dto->ib_conn->iser_conn;
|
||||
struct iscsi_session *session = conn->iscsi_conn->session;
|
||||
struct iscsi_cmd_task *ctask;
|
||||
struct iscsi_iser_cmd_task *iser_ctask;
|
||||
struct iscsi_task *task;
|
||||
struct iscsi_iser_task *iser_task;
|
||||
struct iscsi_hdr *hdr;
|
||||
char *rx_data = NULL;
|
||||
int rx_data_len = 0;
|
||||
unsigned int itt;
|
||||
unsigned char opcode;
|
||||
|
||||
hdr = &rx_desc->iscsi_header;
|
||||
|
@ -557,19 +556,24 @@ void iser_rcv_completion(struct iser_desc *rx_desc,
|
|||
opcode = hdr->opcode & ISCSI_OPCODE_MASK;
|
||||
|
||||
if (opcode == ISCSI_OP_SCSI_CMD_RSP) {
|
||||
itt = get_itt(hdr->itt); /* mask out cid and age bits */
|
||||
if (!(itt < session->cmds_max))
|
||||
iser_err("itt can't be matched to task!!! "
|
||||
"conn %p opcode %d cmds_max %d itt %d\n",
|
||||
conn->iscsi_conn,opcode,session->cmds_max,itt);
|
||||
/* use the mapping given with the cmds array indexed by itt */
|
||||
ctask = (struct iscsi_cmd_task *)session->cmds[itt];
|
||||
iser_ctask = ctask->dd_data;
|
||||
iser_dbg("itt %d ctask %p\n",itt,ctask);
|
||||
iser_ctask->status = ISER_TASK_STATUS_COMPLETED;
|
||||
iser_ctask_rdma_finalize(iser_ctask);
|
||||
}
|
||||
spin_lock(&conn->iscsi_conn->session->lock);
|
||||
task = iscsi_itt_to_ctask(conn->iscsi_conn, hdr->itt);
|
||||
if (task)
|
||||
__iscsi_get_task(task);
|
||||
spin_unlock(&conn->iscsi_conn->session->lock);
|
||||
|
||||
if (!task)
|
||||
iser_err("itt can't be matched to task!!! "
|
||||
"conn %p opcode %d itt %d\n",
|
||||
conn->iscsi_conn, opcode, hdr->itt);
|
||||
else {
|
||||
iser_task = task->dd_data;
|
||||
iser_dbg("itt %d task %p\n",hdr->itt, task);
|
||||
iser_task->status = ISER_TASK_STATUS_COMPLETED;
|
||||
iser_task_rdma_finalize(iser_task);
|
||||
iscsi_put_task(task);
|
||||
}
|
||||
}
|
||||
iser_dto_buffs_release(dto);
|
||||
|
||||
iscsi_iser_recv(conn->iscsi_conn, hdr, rx_data, rx_data_len);
|
||||
|
@ -590,7 +594,7 @@ void iser_snd_completion(struct iser_desc *tx_desc)
|
|||
struct iser_conn *ib_conn = dto->ib_conn;
|
||||
struct iscsi_iser_conn *iser_conn = ib_conn->iser_conn;
|
||||
struct iscsi_conn *conn = iser_conn->iscsi_conn;
|
||||
struct iscsi_mgmt_task *mtask;
|
||||
struct iscsi_task *task;
|
||||
int resume_tx = 0;
|
||||
|
||||
iser_dbg("Initiator, Data sent dto=0x%p\n", dto);
|
||||
|
@ -613,36 +617,31 @@ void iser_snd_completion(struct iser_desc *tx_desc)
|
|||
|
||||
if (tx_desc->type == ISCSI_TX_CONTROL) {
|
||||
/* this arithmetic is legal by libiscsi dd_data allocation */
|
||||
mtask = (void *) ((long)(void *)tx_desc -
|
||||
sizeof(struct iscsi_mgmt_task));
|
||||
if (mtask->hdr->itt == RESERVED_ITT) {
|
||||
struct iscsi_session *session = conn->session;
|
||||
|
||||
spin_lock(&conn->session->lock);
|
||||
iscsi_free_mgmt_task(conn, mtask);
|
||||
spin_unlock(&session->lock);
|
||||
}
|
||||
task = (void *) ((long)(void *)tx_desc -
|
||||
sizeof(struct iscsi_task));
|
||||
if (task->hdr->itt == RESERVED_ITT)
|
||||
iscsi_put_task(task);
|
||||
}
|
||||
}
|
||||
|
||||
void iser_ctask_rdma_init(struct iscsi_iser_cmd_task *iser_ctask)
|
||||
void iser_task_rdma_init(struct iscsi_iser_task *iser_task)
|
||||
|
||||
{
|
||||
iser_ctask->status = ISER_TASK_STATUS_INIT;
|
||||
iser_task->status = ISER_TASK_STATUS_INIT;
|
||||
|
||||
iser_ctask->dir[ISER_DIR_IN] = 0;
|
||||
iser_ctask->dir[ISER_DIR_OUT] = 0;
|
||||
iser_task->dir[ISER_DIR_IN] = 0;
|
||||
iser_task->dir[ISER_DIR_OUT] = 0;
|
||||
|
||||
iser_ctask->data[ISER_DIR_IN].data_len = 0;
|
||||
iser_ctask->data[ISER_DIR_OUT].data_len = 0;
|
||||
iser_task->data[ISER_DIR_IN].data_len = 0;
|
||||
iser_task->data[ISER_DIR_OUT].data_len = 0;
|
||||
|
||||
memset(&iser_ctask->rdma_regd[ISER_DIR_IN], 0,
|
||||
memset(&iser_task->rdma_regd[ISER_DIR_IN], 0,
|
||||
sizeof(struct iser_regd_buf));
|
||||
memset(&iser_ctask->rdma_regd[ISER_DIR_OUT], 0,
|
||||
memset(&iser_task->rdma_regd[ISER_DIR_OUT], 0,
|
||||
sizeof(struct iser_regd_buf));
|
||||
}
|
||||
|
||||
void iser_ctask_rdma_finalize(struct iscsi_iser_cmd_task *iser_ctask)
|
||||
void iser_task_rdma_finalize(struct iscsi_iser_task *iser_task)
|
||||
{
|
||||
int deferred;
|
||||
int is_rdma_aligned = 1;
|
||||
|
@ -651,17 +650,17 @@ void iser_ctask_rdma_finalize(struct iscsi_iser_cmd_task *iser_ctask)
|
|||
/* if we were reading, copy back to unaligned sglist,
|
||||
* anyway dma_unmap and free the copy
|
||||
*/
|
||||
if (iser_ctask->data_copy[ISER_DIR_IN].copy_buf != NULL) {
|
||||
if (iser_task->data_copy[ISER_DIR_IN].copy_buf != NULL) {
|
||||
is_rdma_aligned = 0;
|
||||
iser_finalize_rdma_unaligned_sg(iser_ctask, ISER_DIR_IN);
|
||||
iser_finalize_rdma_unaligned_sg(iser_task, ISER_DIR_IN);
|
||||
}
|
||||
if (iser_ctask->data_copy[ISER_DIR_OUT].copy_buf != NULL) {
|
||||
if (iser_task->data_copy[ISER_DIR_OUT].copy_buf != NULL) {
|
||||
is_rdma_aligned = 0;
|
||||
iser_finalize_rdma_unaligned_sg(iser_ctask, ISER_DIR_OUT);
|
||||
iser_finalize_rdma_unaligned_sg(iser_task, ISER_DIR_OUT);
|
||||
}
|
||||
|
||||
if (iser_ctask->dir[ISER_DIR_IN]) {
|
||||
regd = &iser_ctask->rdma_regd[ISER_DIR_IN];
|
||||
if (iser_task->dir[ISER_DIR_IN]) {
|
||||
regd = &iser_task->rdma_regd[ISER_DIR_IN];
|
||||
deferred = iser_regd_buff_release(regd);
|
||||
if (deferred) {
|
||||
iser_err("%d references remain for BUF-IN rdma reg\n",
|
||||
|
@ -669,8 +668,8 @@ void iser_ctask_rdma_finalize(struct iscsi_iser_cmd_task *iser_ctask)
|
|||
}
|
||||
}
|
||||
|
||||
if (iser_ctask->dir[ISER_DIR_OUT]) {
|
||||
regd = &iser_ctask->rdma_regd[ISER_DIR_OUT];
|
||||
if (iser_task->dir[ISER_DIR_OUT]) {
|
||||
regd = &iser_task->rdma_regd[ISER_DIR_OUT];
|
||||
deferred = iser_regd_buff_release(regd);
|
||||
if (deferred) {
|
||||
iser_err("%d references remain for BUF-OUT rdma reg\n",
|
||||
|
@ -680,7 +679,7 @@ void iser_ctask_rdma_finalize(struct iscsi_iser_cmd_task *iser_ctask)
|
|||
|
||||
/* if the data was unaligned, it was already unmapped and then copied */
|
||||
if (is_rdma_aligned)
|
||||
iser_dma_unmap_task_data(iser_ctask);
|
||||
iser_dma_unmap_task_data(iser_task);
|
||||
}
|
||||
|
||||
void iser_dto_buffs_release(struct iser_dto *dto)
|
||||
|
|
|
@ -99,13 +99,13 @@ void iser_reg_single(struct iser_device *device,
|
|||
/**
|
||||
* iser_start_rdma_unaligned_sg
|
||||
*/
|
||||
static int iser_start_rdma_unaligned_sg(struct iscsi_iser_cmd_task *iser_ctask,
|
||||
static int iser_start_rdma_unaligned_sg(struct iscsi_iser_task *iser_task,
|
||||
enum iser_data_dir cmd_dir)
|
||||
{
|
||||
int dma_nents;
|
||||
struct ib_device *dev;
|
||||
char *mem = NULL;
|
||||
struct iser_data_buf *data = &iser_ctask->data[cmd_dir];
|
||||
struct iser_data_buf *data = &iser_task->data[cmd_dir];
|
||||
unsigned long cmd_data_len = data->data_len;
|
||||
|
||||
if (cmd_data_len > ISER_KMALLOC_THRESHOLD)
|
||||
|
@ -138,37 +138,37 @@ static int iser_start_rdma_unaligned_sg(struct iscsi_iser_cmd_task *iser_ctask,
|
|||
}
|
||||
}
|
||||
|
||||
sg_init_one(&iser_ctask->data_copy[cmd_dir].sg_single, mem, cmd_data_len);
|
||||
iser_ctask->data_copy[cmd_dir].buf =
|
||||
&iser_ctask->data_copy[cmd_dir].sg_single;
|
||||
iser_ctask->data_copy[cmd_dir].size = 1;
|
||||
sg_init_one(&iser_task->data_copy[cmd_dir].sg_single, mem, cmd_data_len);
|
||||
iser_task->data_copy[cmd_dir].buf =
|
||||
&iser_task->data_copy[cmd_dir].sg_single;
|
||||
iser_task->data_copy[cmd_dir].size = 1;
|
||||
|
||||
iser_ctask->data_copy[cmd_dir].copy_buf = mem;
|
||||
iser_task->data_copy[cmd_dir].copy_buf = mem;
|
||||
|
||||
dev = iser_ctask->iser_conn->ib_conn->device->ib_device;
|
||||
dev = iser_task->iser_conn->ib_conn->device->ib_device;
|
||||
dma_nents = ib_dma_map_sg(dev,
|
||||
&iser_ctask->data_copy[cmd_dir].sg_single,
|
||||
&iser_task->data_copy[cmd_dir].sg_single,
|
||||
1,
|
||||
(cmd_dir == ISER_DIR_OUT) ?
|
||||
DMA_TO_DEVICE : DMA_FROM_DEVICE);
|
||||
BUG_ON(dma_nents == 0);
|
||||
|
||||
iser_ctask->data_copy[cmd_dir].dma_nents = dma_nents;
|
||||
iser_task->data_copy[cmd_dir].dma_nents = dma_nents;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* iser_finalize_rdma_unaligned_sg
|
||||
*/
|
||||
void iser_finalize_rdma_unaligned_sg(struct iscsi_iser_cmd_task *iser_ctask,
|
||||
void iser_finalize_rdma_unaligned_sg(struct iscsi_iser_task *iser_task,
|
||||
enum iser_data_dir cmd_dir)
|
||||
{
|
||||
struct ib_device *dev;
|
||||
struct iser_data_buf *mem_copy;
|
||||
unsigned long cmd_data_len;
|
||||
|
||||
dev = iser_ctask->iser_conn->ib_conn->device->ib_device;
|
||||
mem_copy = &iser_ctask->data_copy[cmd_dir];
|
||||
dev = iser_task->iser_conn->ib_conn->device->ib_device;
|
||||
mem_copy = &iser_task->data_copy[cmd_dir];
|
||||
|
||||
ib_dma_unmap_sg(dev, &mem_copy->sg_single, 1,
|
||||
(cmd_dir == ISER_DIR_OUT) ?
|
||||
|
@ -184,8 +184,8 @@ void iser_finalize_rdma_unaligned_sg(struct iscsi_iser_cmd_task *iser_ctask,
|
|||
/* copy back read RDMA to unaligned sg */
|
||||
mem = mem_copy->copy_buf;
|
||||
|
||||
sgl = (struct scatterlist *)iser_ctask->data[ISER_DIR_IN].buf;
|
||||
sg_size = iser_ctask->data[ISER_DIR_IN].size;
|
||||
sgl = (struct scatterlist *)iser_task->data[ISER_DIR_IN].buf;
|
||||
sg_size = iser_task->data[ISER_DIR_IN].size;
|
||||
|
||||
p = mem;
|
||||
for_each_sg(sgl, sg, sg_size, i) {
|
||||
|
@ -198,7 +198,7 @@ void iser_finalize_rdma_unaligned_sg(struct iscsi_iser_cmd_task *iser_ctask,
|
|||
}
|
||||
}
|
||||
|
||||
cmd_data_len = iser_ctask->data[cmd_dir].data_len;
|
||||
cmd_data_len = iser_task->data[cmd_dir].data_len;
|
||||
|
||||
if (cmd_data_len > ISER_KMALLOC_THRESHOLD)
|
||||
free_pages((unsigned long)mem_copy->copy_buf,
|
||||
|
@ -376,15 +376,15 @@ static void iser_page_vec_build(struct iser_data_buf *data,
|
|||
}
|
||||
}
|
||||
|
||||
int iser_dma_map_task_data(struct iscsi_iser_cmd_task *iser_ctask,
|
||||
struct iser_data_buf *data,
|
||||
enum iser_data_dir iser_dir,
|
||||
enum dma_data_direction dma_dir)
|
||||
int iser_dma_map_task_data(struct iscsi_iser_task *iser_task,
|
||||
struct iser_data_buf *data,
|
||||
enum iser_data_dir iser_dir,
|
||||
enum dma_data_direction dma_dir)
|
||||
{
|
||||
struct ib_device *dev;
|
||||
|
||||
iser_ctask->dir[iser_dir] = 1;
|
||||
dev = iser_ctask->iser_conn->ib_conn->device->ib_device;
|
||||
iser_task->dir[iser_dir] = 1;
|
||||
dev = iser_task->iser_conn->ib_conn->device->ib_device;
|
||||
|
||||
data->dma_nents = ib_dma_map_sg(dev, data->buf, data->size, dma_dir);
|
||||
if (data->dma_nents == 0) {
|
||||
|
@ -394,20 +394,20 @@ int iser_dma_map_task_data(struct iscsi_iser_cmd_task *iser_ctask,
|
|||
return 0;
|
||||
}
|
||||
|
||||
void iser_dma_unmap_task_data(struct iscsi_iser_cmd_task *iser_ctask)
|
||||
void iser_dma_unmap_task_data(struct iscsi_iser_task *iser_task)
|
||||
{
|
||||
struct ib_device *dev;
|
||||
struct iser_data_buf *data;
|
||||
|
||||
dev = iser_ctask->iser_conn->ib_conn->device->ib_device;
|
||||
dev = iser_task->iser_conn->ib_conn->device->ib_device;
|
||||
|
||||
if (iser_ctask->dir[ISER_DIR_IN]) {
|
||||
data = &iser_ctask->data[ISER_DIR_IN];
|
||||
if (iser_task->dir[ISER_DIR_IN]) {
|
||||
data = &iser_task->data[ISER_DIR_IN];
|
||||
ib_dma_unmap_sg(dev, data->buf, data->size, DMA_FROM_DEVICE);
|
||||
}
|
||||
|
||||
if (iser_ctask->dir[ISER_DIR_OUT]) {
|
||||
data = &iser_ctask->data[ISER_DIR_OUT];
|
||||
if (iser_task->dir[ISER_DIR_OUT]) {
|
||||
data = &iser_task->data[ISER_DIR_OUT];
|
||||
ib_dma_unmap_sg(dev, data->buf, data->size, DMA_TO_DEVICE);
|
||||
}
|
||||
}
|
||||
|
@ -418,21 +418,21 @@ void iser_dma_unmap_task_data(struct iscsi_iser_cmd_task *iser_ctask)
|
|||
*
|
||||
* returns 0 on success, errno code on failure
|
||||
*/
|
||||
int iser_reg_rdma_mem(struct iscsi_iser_cmd_task *iser_ctask,
|
||||
int iser_reg_rdma_mem(struct iscsi_iser_task *iser_task,
|
||||
enum iser_data_dir cmd_dir)
|
||||
{
|
||||
struct iscsi_conn *iscsi_conn = iser_ctask->iser_conn->iscsi_conn;
|
||||
struct iser_conn *ib_conn = iser_ctask->iser_conn->ib_conn;
|
||||
struct iscsi_conn *iscsi_conn = iser_task->iser_conn->iscsi_conn;
|
||||
struct iser_conn *ib_conn = iser_task->iser_conn->ib_conn;
|
||||
struct iser_device *device = ib_conn->device;
|
||||
struct ib_device *ibdev = device->ib_device;
|
||||
struct iser_data_buf *mem = &iser_ctask->data[cmd_dir];
|
||||
struct iser_data_buf *mem = &iser_task->data[cmd_dir];
|
||||
struct iser_regd_buf *regd_buf;
|
||||
int aligned_len;
|
||||
int err;
|
||||
int i;
|
||||
struct scatterlist *sg;
|
||||
|
||||
regd_buf = &iser_ctask->rdma_regd[cmd_dir];
|
||||
regd_buf = &iser_task->rdma_regd[cmd_dir];
|
||||
|
||||
aligned_len = iser_data_buf_aligned_len(mem, ibdev);
|
||||
if (aligned_len != mem->dma_nents) {
|
||||
|
@ -442,13 +442,13 @@ int iser_reg_rdma_mem(struct iscsi_iser_cmd_task *iser_ctask,
|
|||
iser_data_buf_dump(mem, ibdev);
|
||||
|
||||
/* unmap the command data before accessing it */
|
||||
iser_dma_unmap_task_data(iser_ctask);
|
||||
iser_dma_unmap_task_data(iser_task);
|
||||
|
||||
/* allocate copy buf, if we are writing, copy the */
|
||||
/* unaligned scatterlist, dma map the copy */
|
||||
if (iser_start_rdma_unaligned_sg(iser_ctask, cmd_dir) != 0)
|
||||
if (iser_start_rdma_unaligned_sg(iser_task, cmd_dir) != 0)
|
||||
return -ENOMEM;
|
||||
mem = &iser_ctask->data_copy[cmd_dir];
|
||||
mem = &iser_task->data_copy[cmd_dir];
|
||||
}
|
||||
|
||||
/* if there a single dma entry, FMR is not needed */
|
||||
|
@ -472,8 +472,9 @@ int iser_reg_rdma_mem(struct iscsi_iser_cmd_task *iser_ctask,
|
|||
err = iser_reg_page_vec(ib_conn, ib_conn->page_vec, ®d_buf->reg);
|
||||
if (err) {
|
||||
iser_data_buf_dump(mem, ibdev);
|
||||
iser_err("mem->dma_nents = %d (dlength = 0x%x)\n", mem->dma_nents,
|
||||
ntoh24(iser_ctask->desc.iscsi_header.dlength));
|
||||
iser_err("mem->dma_nents = %d (dlength = 0x%x)\n",
|
||||
mem->dma_nents,
|
||||
ntoh24(iser_task->desc.iscsi_header.dlength));
|
||||
iser_err("page_vec: data_size = 0x%x, length = %d, offset = 0x%x\n",
|
||||
ib_conn->page_vec->data_size, ib_conn->page_vec->length,
|
||||
ib_conn->page_vec->offset);
|
||||
|
|
|
@ -323,7 +323,18 @@ static void iser_conn_release(struct iser_conn *ib_conn)
|
|||
iser_device_try_release(device);
|
||||
if (ib_conn->iser_conn)
|
||||
ib_conn->iser_conn->ib_conn = NULL;
|
||||
kfree(ib_conn);
|
||||
iscsi_destroy_endpoint(ib_conn->ep);
|
||||
}
|
||||
|
||||
void iser_conn_get(struct iser_conn *ib_conn)
|
||||
{
|
||||
atomic_inc(&ib_conn->refcount);
|
||||
}
|
||||
|
||||
void iser_conn_put(struct iser_conn *ib_conn)
|
||||
{
|
||||
if (atomic_dec_and_test(&ib_conn->refcount))
|
||||
iser_conn_release(ib_conn);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -347,7 +358,7 @@ void iser_conn_terminate(struct iser_conn *ib_conn)
|
|||
wait_event_interruptible(ib_conn->wait,
|
||||
ib_conn->state == ISER_CONN_DOWN);
|
||||
|
||||
iser_conn_release(ib_conn);
|
||||
iser_conn_put(ib_conn);
|
||||
}
|
||||
|
||||
static void iser_connect_error(struct rdma_cm_id *cma_id)
|
||||
|
@ -481,24 +492,15 @@ static int iser_cma_handler(struct rdma_cm_id *cma_id, struct rdma_cm_event *eve
|
|||
return ret;
|
||||
}
|
||||
|
||||
int iser_conn_init(struct iser_conn **ibconn)
|
||||
void iser_conn_init(struct iser_conn *ib_conn)
|
||||
{
|
||||
struct iser_conn *ib_conn;
|
||||
|
||||
ib_conn = kzalloc(sizeof *ib_conn, GFP_KERNEL);
|
||||
if (!ib_conn) {
|
||||
iser_err("can't alloc memory for struct iser_conn\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
ib_conn->state = ISER_CONN_INIT;
|
||||
init_waitqueue_head(&ib_conn->wait);
|
||||
atomic_set(&ib_conn->post_recv_buf_count, 0);
|
||||
atomic_set(&ib_conn->post_send_buf_count, 0);
|
||||
atomic_set(&ib_conn->refcount, 1);
|
||||
INIT_LIST_HEAD(&ib_conn->conn_list);
|
||||
spin_lock_init(&ib_conn->lock);
|
||||
|
||||
*ibconn = ib_conn;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -252,27 +252,14 @@ config DM_ZERO
|
|||
config DM_MULTIPATH
|
||||
tristate "Multipath target"
|
||||
depends on BLK_DEV_DM
|
||||
# nasty syntax but means make DM_MULTIPATH independent
|
||||
# of SCSI_DH if the latter isn't defined but if
|
||||
# it is, DM_MULTIPATH must depend on it. We get a build
|
||||
# error if SCSI_DH=m and DM_MULTIPATH=y
|
||||
depends on SCSI_DH || !SCSI_DH
|
||||
---help---
|
||||
Allow volume managers to support multipath hardware.
|
||||
|
||||
config DM_MULTIPATH_EMC
|
||||
tristate "EMC CX/AX multipath support"
|
||||
depends on DM_MULTIPATH && BLK_DEV_DM
|
||||
---help---
|
||||
Multipath support for EMC CX/AX series hardware.
|
||||
|
||||
config DM_MULTIPATH_RDAC
|
||||
tristate "LSI/Engenio RDAC multipath support (EXPERIMENTAL)"
|
||||
depends on DM_MULTIPATH && BLK_DEV_DM && SCSI && EXPERIMENTAL
|
||||
---help---
|
||||
Multipath support for LSI/Engenio RDAC.
|
||||
|
||||
config DM_MULTIPATH_HP
|
||||
tristate "HP MSA multipath support (EXPERIMENTAL)"
|
||||
depends on DM_MULTIPATH && BLK_DEV_DM && SCSI && EXPERIMENTAL
|
||||
---help---
|
||||
Multipath support for HP MSA (Active/Passive) series hardware.
|
||||
|
||||
config DM_DELAY
|
||||
tristate "I/O delaying target (EXPERIMENTAL)"
|
||||
depends on BLK_DEV_DM && EXPERIMENTAL
|
||||
|
|
|
@ -4,11 +4,9 @@
|
|||
|
||||
dm-mod-objs := dm.o dm-table.o dm-target.o dm-linear.o dm-stripe.o \
|
||||
dm-ioctl.o dm-io.o dm-kcopyd.o
|
||||
dm-multipath-objs := dm-hw-handler.o dm-path-selector.o dm-mpath.o
|
||||
dm-multipath-objs := dm-path-selector.o dm-mpath.o
|
||||
dm-snapshot-objs := dm-snap.o dm-exception-store.o
|
||||
dm-mirror-objs := dm-raid1.o
|
||||
dm-rdac-objs := dm-mpath-rdac.o
|
||||
dm-hp-sw-objs := dm-mpath-hp-sw.o
|
||||
md-mod-objs := md.o bitmap.o
|
||||
raid456-objs := raid5.o raid6algos.o raid6recov.o raid6tables.o \
|
||||
raid6int1.o raid6int2.o raid6int4.o \
|
||||
|
@ -35,9 +33,6 @@ obj-$(CONFIG_BLK_DEV_DM) += dm-mod.o
|
|||
obj-$(CONFIG_DM_CRYPT) += dm-crypt.o
|
||||
obj-$(CONFIG_DM_DELAY) += dm-delay.o
|
||||
obj-$(CONFIG_DM_MULTIPATH) += dm-multipath.o dm-round-robin.o
|
||||
obj-$(CONFIG_DM_MULTIPATH_EMC) += dm-emc.o
|
||||
obj-$(CONFIG_DM_MULTIPATH_HP) += dm-hp-sw.o
|
||||
obj-$(CONFIG_DM_MULTIPATH_RDAC) += dm-rdac.o
|
||||
obj-$(CONFIG_DM_SNAPSHOT) += dm-snapshot.o
|
||||
obj-$(CONFIG_DM_MIRROR) += dm-mirror.o dm-log.o
|
||||
obj-$(CONFIG_DM_ZERO) += dm-zero.o
|
||||
|
|
|
@ -1,345 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2004 SUSE LINUX Products GmbH. All rights reserved.
|
||||
* Copyright (C) 2004 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This file is released under the GPL.
|
||||
*
|
||||
* Multipath support for EMC CLARiiON AX/CX-series hardware.
|
||||
*/
|
||||
|
||||
#include "dm.h"
|
||||
#include "dm-hw-handler.h"
|
||||
#include <scsi/scsi.h>
|
||||
#include <scsi/scsi_cmnd.h>
|
||||
|
||||
#define DM_MSG_PREFIX "multipath emc"
|
||||
|
||||
struct emc_handler {
|
||||
spinlock_t lock;
|
||||
|
||||
/* Whether we should send the short trespass command (FC-series)
|
||||
* or the long version (default for AX/CX CLARiiON arrays). */
|
||||
unsigned short_trespass;
|
||||
/* Whether or not to honor SCSI reservations when initiating a
|
||||
* switch-over. Default: Don't. */
|
||||
unsigned hr;
|
||||
|
||||
unsigned char sense[SCSI_SENSE_BUFFERSIZE];
|
||||
};
|
||||
|
||||
#define TRESPASS_PAGE 0x22
|
||||
#define EMC_FAILOVER_TIMEOUT (60 * HZ)
|
||||
|
||||
/* Code borrowed from dm-lsi-rdac by Mike Christie */
|
||||
|
||||
static inline void free_bio(struct bio *bio)
|
||||
{
|
||||
__free_page(bio->bi_io_vec[0].bv_page);
|
||||
bio_put(bio);
|
||||
}
|
||||
|
||||
static void emc_endio(struct bio *bio, int error)
|
||||
{
|
||||
struct dm_path *path = bio->bi_private;
|
||||
|
||||
/* We also need to look at the sense keys here whether or not to
|
||||
* switch to the next PG etc.
|
||||
*
|
||||
* For now simple logic: either it works or it doesn't.
|
||||
*/
|
||||
if (error)
|
||||
dm_pg_init_complete(path, MP_FAIL_PATH);
|
||||
else
|
||||
dm_pg_init_complete(path, 0);
|
||||
|
||||
/* request is freed in block layer */
|
||||
free_bio(bio);
|
||||
}
|
||||
|
||||
static struct bio *get_failover_bio(struct dm_path *path, unsigned data_size)
|
||||
{
|
||||
struct bio *bio;
|
||||
struct page *page;
|
||||
|
||||
bio = bio_alloc(GFP_ATOMIC, 1);
|
||||
if (!bio) {
|
||||
DMERR("get_failover_bio: bio_alloc() failed.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bio->bi_rw |= (1 << BIO_RW);
|
||||
bio->bi_bdev = path->dev->bdev;
|
||||
bio->bi_sector = 0;
|
||||
bio->bi_private = path;
|
||||
bio->bi_end_io = emc_endio;
|
||||
|
||||
page = alloc_page(GFP_ATOMIC);
|
||||
if (!page) {
|
||||
DMERR("get_failover_bio: alloc_page() failed.");
|
||||
bio_put(bio);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (bio_add_page(bio, page, data_size, 0) != data_size) {
|
||||
DMERR("get_failover_bio: bio_add_page() failed.");
|
||||
__free_page(page);
|
||||
bio_put(bio);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return bio;
|
||||
}
|
||||
|
||||
static struct request *get_failover_req(struct emc_handler *h,
|
||||
struct bio *bio, struct dm_path *path)
|
||||
{
|
||||
struct request *rq;
|
||||
struct block_device *bdev = bio->bi_bdev;
|
||||
struct request_queue *q = bdev_get_queue(bdev);
|
||||
|
||||
/* FIXME: Figure out why it fails with GFP_ATOMIC. */
|
||||
rq = blk_get_request(q, WRITE, __GFP_WAIT);
|
||||
if (!rq) {
|
||||
DMERR("get_failover_req: blk_get_request failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
blk_rq_append_bio(q, rq, bio);
|
||||
|
||||
rq->sense = h->sense;
|
||||
memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE);
|
||||
rq->sense_len = 0;
|
||||
|
||||
rq->timeout = EMC_FAILOVER_TIMEOUT;
|
||||
rq->cmd_type = REQ_TYPE_BLOCK_PC;
|
||||
rq->cmd_flags |= REQ_FAILFAST | REQ_NOMERGE;
|
||||
|
||||
return rq;
|
||||
}
|
||||
|
||||
static struct request *emc_trespass_get(struct emc_handler *h,
|
||||
struct dm_path *path)
|
||||
{
|
||||
struct bio *bio;
|
||||
struct request *rq;
|
||||
unsigned char *page22;
|
||||
unsigned char long_trespass_pg[] = {
|
||||
0, 0, 0, 0,
|
||||
TRESPASS_PAGE, /* Page code */
|
||||
0x09, /* Page length - 2 */
|
||||
h->hr ? 0x01 : 0x81, /* Trespass code + Honor reservation bit */
|
||||
0xff, 0xff, /* Trespass target */
|
||||
0, 0, 0, 0, 0, 0 /* Reserved bytes / unknown */
|
||||
};
|
||||
unsigned char short_trespass_pg[] = {
|
||||
0, 0, 0, 0,
|
||||
TRESPASS_PAGE, /* Page code */
|
||||
0x02, /* Page length - 2 */
|
||||
h->hr ? 0x01 : 0x81, /* Trespass code + Honor reservation bit */
|
||||
0xff, /* Trespass target */
|
||||
};
|
||||
unsigned data_size = h->short_trespass ? sizeof(short_trespass_pg) :
|
||||
sizeof(long_trespass_pg);
|
||||
|
||||
/* get bio backing */
|
||||
if (data_size > PAGE_SIZE)
|
||||
/* this should never happen */
|
||||
return NULL;
|
||||
|
||||
bio = get_failover_bio(path, data_size);
|
||||
if (!bio) {
|
||||
DMERR("emc_trespass_get: no bio");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
page22 = (unsigned char *)bio_data(bio);
|
||||
memset(page22, 0, data_size);
|
||||
|
||||
memcpy(page22, h->short_trespass ?
|
||||
short_trespass_pg : long_trespass_pg, data_size);
|
||||
|
||||
/* get request for block layer packet command */
|
||||
rq = get_failover_req(h, bio, path);
|
||||
if (!rq) {
|
||||
DMERR("emc_trespass_get: no rq");
|
||||
free_bio(bio);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Prepare the command. */
|
||||
rq->cmd[0] = MODE_SELECT;
|
||||
rq->cmd[1] = 0x10;
|
||||
rq->cmd[4] = data_size;
|
||||
rq->cmd_len = COMMAND_SIZE(rq->cmd[0]);
|
||||
|
||||
return rq;
|
||||
}
|
||||
|
||||
static void emc_pg_init(struct hw_handler *hwh, unsigned bypassed,
|
||||
struct dm_path *path)
|
||||
{
|
||||
struct request *rq;
|
||||
struct request_queue *q = bdev_get_queue(path->dev->bdev);
|
||||
|
||||
/*
|
||||
* We can either blindly init the pg (then look at the sense),
|
||||
* or we can send some commands to get the state here (then
|
||||
* possibly send the fo cmnd), or we can also have the
|
||||
* initial state passed into us and then get an update here.
|
||||
*/
|
||||
if (!q) {
|
||||
DMINFO("emc_pg_init: no queue");
|
||||
goto fail_path;
|
||||
}
|
||||
|
||||
/* FIXME: The request should be pre-allocated. */
|
||||
rq = emc_trespass_get(hwh->context, path);
|
||||
if (!rq) {
|
||||
DMERR("emc_pg_init: no rq");
|
||||
goto fail_path;
|
||||
}
|
||||
|
||||
DMINFO("emc_pg_init: sending switch-over command");
|
||||
elv_add_request(q, rq, ELEVATOR_INSERT_FRONT, 1);
|
||||
return;
|
||||
|
||||
fail_path:
|
||||
dm_pg_init_complete(path, MP_FAIL_PATH);
|
||||
}
|
||||
|
||||
static struct emc_handler *alloc_emc_handler(void)
|
||||
{
|
||||
struct emc_handler *h = kzalloc(sizeof(*h), GFP_KERNEL);
|
||||
|
||||
if (h)
|
||||
spin_lock_init(&h->lock);
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
static int emc_create(struct hw_handler *hwh, unsigned argc, char **argv)
|
||||
{
|
||||
struct emc_handler *h;
|
||||
unsigned hr, short_trespass;
|
||||
|
||||
if (argc == 0) {
|
||||
/* No arguments: use defaults */
|
||||
hr = 0;
|
||||
short_trespass = 0;
|
||||
} else if (argc != 2) {
|
||||
DMWARN("incorrect number of arguments");
|
||||
return -EINVAL;
|
||||
} else {
|
||||
if ((sscanf(argv[0], "%u", &short_trespass) != 1)
|
||||
|| (short_trespass > 1)) {
|
||||
DMWARN("invalid trespass mode selected");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if ((sscanf(argv[1], "%u", &hr) != 1)
|
||||
|| (hr > 1)) {
|
||||
DMWARN("invalid honor reservation flag selected");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
h = alloc_emc_handler();
|
||||
if (!h)
|
||||
return -ENOMEM;
|
||||
|
||||
hwh->context = h;
|
||||
|
||||
if ((h->short_trespass = short_trespass))
|
||||
DMWARN("short trespass command will be send");
|
||||
else
|
||||
DMWARN("long trespass command will be send");
|
||||
|
||||
if ((h->hr = hr))
|
||||
DMWARN("honor reservation bit will be set");
|
||||
else
|
||||
DMWARN("honor reservation bit will not be set (default)");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void emc_destroy(struct hw_handler *hwh)
|
||||
{
|
||||
struct emc_handler *h = (struct emc_handler *) hwh->context;
|
||||
|
||||
kfree(h);
|
||||
hwh->context = NULL;
|
||||
}
|
||||
|
||||
static unsigned emc_error(struct hw_handler *hwh, struct bio *bio)
|
||||
{
|
||||
/* FIXME: Patch from axboe still missing */
|
||||
#if 0
|
||||
int sense;
|
||||
|
||||
if (bio->bi_error & BIO_SENSE) {
|
||||
sense = bio->bi_error & 0xffffff; /* sense key / asc / ascq */
|
||||
|
||||
if (sense == 0x020403) {
|
||||
/* LUN Not Ready - Manual Intervention Required
|
||||
* indicates this is a passive path.
|
||||
*
|
||||
* FIXME: However, if this is seen and EVPD C0
|
||||
* indicates that this is due to a NDU in
|
||||
* progress, we should set FAIL_PATH too.
|
||||
* This indicates we might have to do a SCSI
|
||||
* inquiry in the end_io path. Ugh. */
|
||||
return MP_BYPASS_PG | MP_RETRY_IO;
|
||||
} else if (sense == 0x052501) {
|
||||
/* An array based copy is in progress. Do not
|
||||
* fail the path, do not bypass to another PG,
|
||||
* do not retry. Fail the IO immediately.
|
||||
* (Actually this is the same conclusion as in
|
||||
* the default handler, but lets make sure.) */
|
||||
return 0;
|
||||
} else if (sense == 0x062900) {
|
||||
/* Unit Attention Code. This is the first IO
|
||||
* to the new path, so just retry. */
|
||||
return MP_RETRY_IO;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Try default handler */
|
||||
return dm_scsi_err_handler(hwh, bio);
|
||||
}
|
||||
|
||||
static struct hw_handler_type emc_hwh = {
|
||||
.name = "emc",
|
||||
.module = THIS_MODULE,
|
||||
.create = emc_create,
|
||||
.destroy = emc_destroy,
|
||||
.pg_init = emc_pg_init,
|
||||
.error = emc_error,
|
||||
};
|
||||
|
||||
static int __init dm_emc_init(void)
|
||||
{
|
||||
int r = dm_register_hw_handler(&emc_hwh);
|
||||
|
||||
if (r < 0)
|
||||
DMERR("register failed %d", r);
|
||||
|
||||
DMINFO("version 0.0.3 loaded");
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static void __exit dm_emc_exit(void)
|
||||
{
|
||||
int r = dm_unregister_hw_handler(&emc_hwh);
|
||||
|
||||
if (r < 0)
|
||||
DMERR("unregister failed %d", r);
|
||||
}
|
||||
|
||||
module_init(dm_emc_init);
|
||||
module_exit(dm_emc_exit);
|
||||
|
||||
MODULE_DESCRIPTION(DM_NAME " EMC CX/AX/FC-family multipath");
|
||||
MODULE_AUTHOR("Lars Marowsky-Bree <lmb@suse.de>");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -1,213 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2004 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This file is released under the GPL.
|
||||
*
|
||||
* Multipath hardware handler registration.
|
||||
*/
|
||||
|
||||
#include "dm.h"
|
||||
#include "dm-hw-handler.h"
|
||||
|
||||
#include <linux/slab.h>
|
||||
|
||||
struct hwh_internal {
|
||||
struct hw_handler_type hwht;
|
||||
|
||||
struct list_head list;
|
||||
long use;
|
||||
};
|
||||
|
||||
#define hwht_to_hwhi(__hwht) container_of((__hwht), struct hwh_internal, hwht)
|
||||
|
||||
static LIST_HEAD(_hw_handlers);
|
||||
static DECLARE_RWSEM(_hwh_lock);
|
||||
|
||||
static struct hwh_internal *__find_hw_handler_type(const char *name)
|
||||
{
|
||||
struct hwh_internal *hwhi;
|
||||
|
||||
list_for_each_entry(hwhi, &_hw_handlers, list) {
|
||||
if (!strcmp(name, hwhi->hwht.name))
|
||||
return hwhi;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct hwh_internal *get_hw_handler(const char *name)
|
||||
{
|
||||
struct hwh_internal *hwhi;
|
||||
|
||||
down_read(&_hwh_lock);
|
||||
hwhi = __find_hw_handler_type(name);
|
||||
if (hwhi) {
|
||||
if ((hwhi->use == 0) && !try_module_get(hwhi->hwht.module))
|
||||
hwhi = NULL;
|
||||
else
|
||||
hwhi->use++;
|
||||
}
|
||||
up_read(&_hwh_lock);
|
||||
|
||||
return hwhi;
|
||||
}
|
||||
|
||||
struct hw_handler_type *dm_get_hw_handler(const char *name)
|
||||
{
|
||||
struct hwh_internal *hwhi;
|
||||
|
||||
if (!name)
|
||||
return NULL;
|
||||
|
||||
hwhi = get_hw_handler(name);
|
||||
if (!hwhi) {
|
||||
request_module("dm-%s", name);
|
||||
hwhi = get_hw_handler(name);
|
||||
}
|
||||
|
||||
return hwhi ? &hwhi->hwht : NULL;
|
||||
}
|
||||
|
||||
void dm_put_hw_handler(struct hw_handler_type *hwht)
|
||||
{
|
||||
struct hwh_internal *hwhi;
|
||||
|
||||
if (!hwht)
|
||||
return;
|
||||
|
||||
down_read(&_hwh_lock);
|
||||
hwhi = __find_hw_handler_type(hwht->name);
|
||||
if (!hwhi)
|
||||
goto out;
|
||||
|
||||
if (--hwhi->use == 0)
|
||||
module_put(hwhi->hwht.module);
|
||||
|
||||
BUG_ON(hwhi->use < 0);
|
||||
|
||||
out:
|
||||
up_read(&_hwh_lock);
|
||||
}
|
||||
|
||||
static struct hwh_internal *_alloc_hw_handler(struct hw_handler_type *hwht)
|
||||
{
|
||||
struct hwh_internal *hwhi = kzalloc(sizeof(*hwhi), GFP_KERNEL);
|
||||
|
||||
if (hwhi)
|
||||
hwhi->hwht = *hwht;
|
||||
|
||||
return hwhi;
|
||||
}
|
||||
|
||||
int dm_register_hw_handler(struct hw_handler_type *hwht)
|
||||
{
|
||||
int r = 0;
|
||||
struct hwh_internal *hwhi = _alloc_hw_handler(hwht);
|
||||
|
||||
if (!hwhi)
|
||||
return -ENOMEM;
|
||||
|
||||
down_write(&_hwh_lock);
|
||||
|
||||
if (__find_hw_handler_type(hwht->name)) {
|
||||
kfree(hwhi);
|
||||
r = -EEXIST;
|
||||
} else
|
||||
list_add(&hwhi->list, &_hw_handlers);
|
||||
|
||||
up_write(&_hwh_lock);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int dm_unregister_hw_handler(struct hw_handler_type *hwht)
|
||||
{
|
||||
struct hwh_internal *hwhi;
|
||||
|
||||
down_write(&_hwh_lock);
|
||||
|
||||
hwhi = __find_hw_handler_type(hwht->name);
|
||||
if (!hwhi) {
|
||||
up_write(&_hwh_lock);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (hwhi->use) {
|
||||
up_write(&_hwh_lock);
|
||||
return -ETXTBSY;
|
||||
}
|
||||
|
||||
list_del(&hwhi->list);
|
||||
|
||||
up_write(&_hwh_lock);
|
||||
|
||||
kfree(hwhi);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned dm_scsi_err_handler(struct hw_handler *hwh, struct bio *bio)
|
||||
{
|
||||
#if 0
|
||||
int sense_key, asc, ascq;
|
||||
|
||||
if (bio->bi_error & BIO_SENSE) {
|
||||
/* FIXME: This is just an initial guess. */
|
||||
/* key / asc / ascq */
|
||||
sense_key = (bio->bi_error >> 16) & 0xff;
|
||||
asc = (bio->bi_error >> 8) & 0xff;
|
||||
ascq = bio->bi_error & 0xff;
|
||||
|
||||
switch (sense_key) {
|
||||
/* This block as a whole comes from the device.
|
||||
* So no point retrying on another path. */
|
||||
case 0x03: /* Medium error */
|
||||
case 0x05: /* Illegal request */
|
||||
case 0x07: /* Data protect */
|
||||
case 0x08: /* Blank check */
|
||||
case 0x0a: /* copy aborted */
|
||||
case 0x0c: /* obsolete - no clue ;-) */
|
||||
case 0x0d: /* volume overflow */
|
||||
case 0x0e: /* data miscompare */
|
||||
case 0x0f: /* reserved - no idea either. */
|
||||
return MP_ERROR_IO;
|
||||
|
||||
/* For these errors it's unclear whether they
|
||||
* come from the device or the controller.
|
||||
* So just lets try a different path, and if
|
||||
* it eventually succeeds, user-space will clear
|
||||
* the paths again... */
|
||||
case 0x02: /* Not ready */
|
||||
case 0x04: /* Hardware error */
|
||||
case 0x09: /* vendor specific */
|
||||
case 0x0b: /* Aborted command */
|
||||
return MP_FAIL_PATH;
|
||||
|
||||
case 0x06: /* Unit attention - might want to decode */
|
||||
if (asc == 0x04 && ascq == 0x01)
|
||||
/* "Unit in the process of
|
||||
* becoming ready" */
|
||||
return 0;
|
||||
return MP_FAIL_PATH;
|
||||
|
||||
/* FIXME: For Unit Not Ready we may want
|
||||
* to have a generic pg activation
|
||||
* feature (START_UNIT). */
|
||||
|
||||
/* Should these two ever end up in the
|
||||
* error path? I don't think so. */
|
||||
case 0x00: /* No sense */
|
||||
case 0x01: /* Recovered error */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* We got no idea how to decode the other kinds of errors ->
|
||||
* assume generic error condition. */
|
||||
return MP_FAIL_PATH;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(dm_register_hw_handler);
|
||||
EXPORT_SYMBOL_GPL(dm_unregister_hw_handler);
|
||||
EXPORT_SYMBOL_GPL(dm_scsi_err_handler);
|
|
@ -1,63 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2004 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This file is released under the GPL.
|
||||
*
|
||||
* Multipath hardware handler registration.
|
||||
*/
|
||||
|
||||
#ifndef DM_HW_HANDLER_H
|
||||
#define DM_HW_HANDLER_H
|
||||
|
||||
#include <linux/device-mapper.h>
|
||||
|
||||
#include "dm-mpath.h"
|
||||
|
||||
struct hw_handler_type;
|
||||
struct hw_handler {
|
||||
struct hw_handler_type *type;
|
||||
struct mapped_device *md;
|
||||
void *context;
|
||||
};
|
||||
|
||||
/*
|
||||
* Constructs a hardware handler object, takes custom arguments
|
||||
*/
|
||||
/* Information about a hardware handler type */
|
||||
struct hw_handler_type {
|
||||
char *name;
|
||||
struct module *module;
|
||||
|
||||
int (*create) (struct hw_handler *handler, unsigned int argc,
|
||||
char **argv);
|
||||
void (*destroy) (struct hw_handler *hwh);
|
||||
|
||||
void (*pg_init) (struct hw_handler *hwh, unsigned bypassed,
|
||||
struct dm_path *path);
|
||||
unsigned (*error) (struct hw_handler *hwh, struct bio *bio);
|
||||
int (*status) (struct hw_handler *hwh, status_type_t type,
|
||||
char *result, unsigned int maxlen);
|
||||
};
|
||||
|
||||
/* Register a hardware handler */
|
||||
int dm_register_hw_handler(struct hw_handler_type *type);
|
||||
|
||||
/* Unregister a hardware handler */
|
||||
int dm_unregister_hw_handler(struct hw_handler_type *type);
|
||||
|
||||
/* Returns a registered hardware handler type */
|
||||
struct hw_handler_type *dm_get_hw_handler(const char *name);
|
||||
|
||||
/* Releases a hardware handler */
|
||||
void dm_put_hw_handler(struct hw_handler_type *hwht);
|
||||
|
||||
/* Default err function */
|
||||
unsigned dm_scsi_err_handler(struct hw_handler *hwh, struct bio *bio);
|
||||
|
||||
/* Error flags for err and dm_pg_init_complete */
|
||||
#define MP_FAIL_PATH 1
|
||||
#define MP_BYPASS_PG 2
|
||||
#define MP_ERROR_IO 4 /* Don't retry this I/O */
|
||||
#define MP_RETRY 8
|
||||
|
||||
#endif
|
|
@ -1,247 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2005 Mike Christie, All rights reserved.
|
||||
* Copyright (C) 2007 Red Hat, Inc. All rights reserved.
|
||||
* Authors: Mike Christie
|
||||
* Dave Wysochanski
|
||||
*
|
||||
* This file is released under the GPL.
|
||||
*
|
||||
* This module implements the specific path activation code for
|
||||
* HP StorageWorks and FSC FibreCat Asymmetric (Active/Passive)
|
||||
* storage arrays.
|
||||
* These storage arrays have controller-based failover, not
|
||||
* LUN-based failover. However, LUN-based failover is the design
|
||||
* of dm-multipath. Thus, this module is written for LUN-based failover.
|
||||
*/
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/types.h>
|
||||
#include <scsi/scsi.h>
|
||||
#include <scsi/scsi_cmnd.h>
|
||||
#include <scsi/scsi_dbg.h>
|
||||
|
||||
#include "dm.h"
|
||||
#include "dm-hw-handler.h"
|
||||
|
||||
#define DM_MSG_PREFIX "multipath hp-sw"
|
||||
#define DM_HP_HWH_NAME "hp-sw"
|
||||
#define DM_HP_HWH_VER "1.0.0"
|
||||
|
||||
struct hp_sw_context {
|
||||
unsigned char sense[SCSI_SENSE_BUFFERSIZE];
|
||||
};
|
||||
|
||||
/*
|
||||
* hp_sw_error_is_retryable - Is an HP-specific check condition retryable?
|
||||
* @req: path activation request
|
||||
*
|
||||
* Examine error codes of request and determine whether the error is retryable.
|
||||
* Some error codes are already retried by scsi-ml (see
|
||||
* scsi_decide_disposition), but some HP specific codes are not.
|
||||
* The intent of this routine is to supply the logic for the HP specific
|
||||
* check conditions.
|
||||
*
|
||||
* Returns:
|
||||
* 1 - command completed with retryable error
|
||||
* 0 - command completed with non-retryable error
|
||||
*
|
||||
* Possible optimizations
|
||||
* 1. More hardware-specific error codes
|
||||
*/
|
||||
static int hp_sw_error_is_retryable(struct request *req)
|
||||
{
|
||||
/*
|
||||
* NOT_READY is known to be retryable
|
||||
* For now we just dump out the sense data and call it retryable
|
||||
*/
|
||||
if (status_byte(req->errors) == CHECK_CONDITION)
|
||||
__scsi_print_sense(DM_HP_HWH_NAME, req->sense, req->sense_len);
|
||||
|
||||
/*
|
||||
* At this point we don't have complete information about all the error
|
||||
* codes from this hardware, so we are just conservative and retry
|
||||
* when in doubt.
|
||||
*/
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* hp_sw_end_io - Completion handler for HP path activation.
|
||||
* @req: path activation request
|
||||
* @error: scsi-ml error
|
||||
*
|
||||
* Check sense data, free request structure, and notify dm that
|
||||
* pg initialization has completed.
|
||||
*
|
||||
* Context: scsi-ml softirq
|
||||
*
|
||||
*/
|
||||
static void hp_sw_end_io(struct request *req, int error)
|
||||
{
|
||||
struct dm_path *path = req->end_io_data;
|
||||
unsigned err_flags = 0;
|
||||
|
||||
if (!error) {
|
||||
DMDEBUG("%s path activation command - success",
|
||||
path->dev->name);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (hp_sw_error_is_retryable(req)) {
|
||||
DMDEBUG("%s path activation command - retry",
|
||||
path->dev->name);
|
||||
err_flags = MP_RETRY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
DMWARN("%s path activation fail - error=0x%x",
|
||||
path->dev->name, error);
|
||||
err_flags = MP_FAIL_PATH;
|
||||
|
||||
out:
|
||||
req->end_io_data = NULL;
|
||||
__blk_put_request(req->q, req);
|
||||
dm_pg_init_complete(path, err_flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* hp_sw_get_request - Allocate an HP specific path activation request
|
||||
* @path: path on which request will be sent (needed for request queue)
|
||||
*
|
||||
* The START command is used for path activation request.
|
||||
* These arrays are controller-based failover, not LUN based.
|
||||
* One START command issued to a single path will fail over all
|
||||
* LUNs for the same controller.
|
||||
*
|
||||
* Possible optimizations
|
||||
* 1. Make timeout configurable
|
||||
* 2. Preallocate request
|
||||
*/
|
||||
static struct request *hp_sw_get_request(struct dm_path *path)
|
||||
{
|
||||
struct request *req;
|
||||
struct block_device *bdev = path->dev->bdev;
|
||||
struct request_queue *q = bdev_get_queue(bdev);
|
||||
struct hp_sw_context *h = path->hwhcontext;
|
||||
|
||||
req = blk_get_request(q, WRITE, GFP_NOIO);
|
||||
if (!req)
|
||||
goto out;
|
||||
|
||||
req->timeout = 60 * HZ;
|
||||
|
||||
req->errors = 0;
|
||||
req->cmd_type = REQ_TYPE_BLOCK_PC;
|
||||
req->cmd_flags |= REQ_FAILFAST | REQ_NOMERGE;
|
||||
req->end_io_data = path;
|
||||
req->sense = h->sense;
|
||||
memset(req->sense, 0, SCSI_SENSE_BUFFERSIZE);
|
||||
|
||||
req->cmd[0] = START_STOP;
|
||||
req->cmd[4] = 1;
|
||||
req->cmd_len = COMMAND_SIZE(req->cmd[0]);
|
||||
|
||||
out:
|
||||
return req;
|
||||
}
|
||||
|
||||
/*
|
||||
* hp_sw_pg_init - HP path activation implementation.
|
||||
* @hwh: hardware handler specific data
|
||||
* @bypassed: unused; is the path group bypassed? (see dm-mpath.c)
|
||||
* @path: path to send initialization command
|
||||
*
|
||||
* Send an HP-specific path activation command on 'path'.
|
||||
* Do not try to optimize in any way, just send the activation command.
|
||||
* More than one path activation command may be sent to the same controller.
|
||||
* This seems to work fine for basic failover support.
|
||||
*
|
||||
* Possible optimizations
|
||||
* 1. Detect an in-progress activation request and avoid submitting another one
|
||||
* 2. Model the controller and only send a single activation request at a time
|
||||
* 3. Determine the state of a path before sending an activation request
|
||||
*
|
||||
* Context: kmpathd (see process_queued_ios() in dm-mpath.c)
|
||||
*/
|
||||
static void hp_sw_pg_init(struct hw_handler *hwh, unsigned bypassed,
|
||||
struct dm_path *path)
|
||||
{
|
||||
struct request *req;
|
||||
struct hp_sw_context *h;
|
||||
|
||||
path->hwhcontext = hwh->context;
|
||||
h = hwh->context;
|
||||
|
||||
req = hp_sw_get_request(path);
|
||||
if (!req) {
|
||||
DMERR("%s path activation command - allocation fail",
|
||||
path->dev->name);
|
||||
goto retry;
|
||||
}
|
||||
|
||||
DMDEBUG("%s path activation command - sent", path->dev->name);
|
||||
|
||||
blk_execute_rq_nowait(req->q, NULL, req, 1, hp_sw_end_io);
|
||||
return;
|
||||
|
||||
retry:
|
||||
dm_pg_init_complete(path, MP_RETRY);
|
||||
}
|
||||
|
||||
static int hp_sw_create(struct hw_handler *hwh, unsigned argc, char **argv)
|
||||
{
|
||||
struct hp_sw_context *h;
|
||||
|
||||
h = kmalloc(sizeof(*h), GFP_KERNEL);
|
||||
if (!h)
|
||||
return -ENOMEM;
|
||||
|
||||
hwh->context = h;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void hp_sw_destroy(struct hw_handler *hwh)
|
||||
{
|
||||
struct hp_sw_context *h = hwh->context;
|
||||
|
||||
kfree(h);
|
||||
}
|
||||
|
||||
static struct hw_handler_type hp_sw_hwh = {
|
||||
.name = DM_HP_HWH_NAME,
|
||||
.module = THIS_MODULE,
|
||||
.create = hp_sw_create,
|
||||
.destroy = hp_sw_destroy,
|
||||
.pg_init = hp_sw_pg_init,
|
||||
};
|
||||
|
||||
static int __init hp_sw_init(void)
|
||||
{
|
||||
int r;
|
||||
|
||||
r = dm_register_hw_handler(&hp_sw_hwh);
|
||||
if (r < 0)
|
||||
DMERR("register failed %d", r);
|
||||
else
|
||||
DMINFO("version " DM_HP_HWH_VER " loaded");
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static void __exit hp_sw_exit(void)
|
||||
{
|
||||
int r;
|
||||
|
||||
r = dm_unregister_hw_handler(&hp_sw_hwh);
|
||||
if (r < 0)
|
||||
DMERR("unregister failed %d", r);
|
||||
}
|
||||
|
||||
module_init(hp_sw_init);
|
||||
module_exit(hp_sw_exit);
|
||||
|
||||
MODULE_DESCRIPTION("DM Multipath HP StorageWorks / FSC FibreCat (A/P) support");
|
||||
MODULE_AUTHOR("Mike Christie, Dave Wysochanski <dm-devel@redhat.com>");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_VERSION(DM_HP_HWH_VER);
|
|
@ -1,700 +0,0 @@
|
|||
/*
|
||||
* Engenio/LSI RDAC DM HW handler
|
||||
*
|
||||
* Copyright (C) 2005 Mike Christie. All rights reserved.
|
||||
* Copyright (C) Chandra Seetharaman, IBM Corp. 2007
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*
|
||||
*/
|
||||
#include <scsi/scsi.h>
|
||||
#include <scsi/scsi_cmnd.h>
|
||||
#include <scsi/scsi_eh.h>
|
||||
|
||||
#define DM_MSG_PREFIX "multipath rdac"
|
||||
|
||||
#include "dm.h"
|
||||
#include "dm-hw-handler.h"
|
||||
|
||||
#define RDAC_DM_HWH_NAME "rdac"
|
||||
#define RDAC_DM_HWH_VER "0.4"
|
||||
|
||||
/*
|
||||
* LSI mode page stuff
|
||||
*
|
||||
* These struct definitions and the forming of the
|
||||
* mode page were taken from the LSI RDAC 2.4 GPL'd
|
||||
* driver, and then converted to Linux conventions.
|
||||
*/
|
||||
#define RDAC_QUIESCENCE_TIME 20;
|
||||
/*
|
||||
* Page Codes
|
||||
*/
|
||||
#define RDAC_PAGE_CODE_REDUNDANT_CONTROLLER 0x2c
|
||||
|
||||
/*
|
||||
* Controller modes definitions
|
||||
*/
|
||||
#define RDAC_MODE_TRANSFER_ALL_LUNS 0x01
|
||||
#define RDAC_MODE_TRANSFER_SPECIFIED_LUNS 0x02
|
||||
|
||||
/*
|
||||
* RDAC Options field
|
||||
*/
|
||||
#define RDAC_FORCED_QUIESENCE 0x02
|
||||
|
||||
#define RDAC_FAILOVER_TIMEOUT (60 * HZ)
|
||||
|
||||
struct rdac_mode_6_hdr {
|
||||
u8 data_len;
|
||||
u8 medium_type;
|
||||
u8 device_params;
|
||||
u8 block_desc_len;
|
||||
};
|
||||
|
||||
struct rdac_mode_10_hdr {
|
||||
u16 data_len;
|
||||
u8 medium_type;
|
||||
u8 device_params;
|
||||
u16 reserved;
|
||||
u16 block_desc_len;
|
||||
};
|
||||
|
||||
struct rdac_mode_common {
|
||||
u8 controller_serial[16];
|
||||
u8 alt_controller_serial[16];
|
||||
u8 rdac_mode[2];
|
||||
u8 alt_rdac_mode[2];
|
||||
u8 quiescence_timeout;
|
||||
u8 rdac_options;
|
||||
};
|
||||
|
||||
struct rdac_pg_legacy {
|
||||
struct rdac_mode_6_hdr hdr;
|
||||
u8 page_code;
|
||||
u8 page_len;
|
||||
struct rdac_mode_common common;
|
||||
#define MODE6_MAX_LUN 32
|
||||
u8 lun_table[MODE6_MAX_LUN];
|
||||
u8 reserved2[32];
|
||||
u8 reserved3;
|
||||
u8 reserved4;
|
||||
};
|
||||
|
||||
struct rdac_pg_expanded {
|
||||
struct rdac_mode_10_hdr hdr;
|
||||
u8 page_code;
|
||||
u8 subpage_code;
|
||||
u8 page_len[2];
|
||||
struct rdac_mode_common common;
|
||||
u8 lun_table[256];
|
||||
u8 reserved3;
|
||||
u8 reserved4;
|
||||
};
|
||||
|
||||
struct c9_inquiry {
|
||||
u8 peripheral_info;
|
||||
u8 page_code; /* 0xC9 */
|
||||
u8 reserved1;
|
||||
u8 page_len;
|
||||
u8 page_id[4]; /* "vace" */
|
||||
u8 avte_cvp;
|
||||
u8 path_prio;
|
||||
u8 reserved2[38];
|
||||
};
|
||||
|
||||
#define SUBSYS_ID_LEN 16
|
||||
#define SLOT_ID_LEN 2
|
||||
|
||||
struct c4_inquiry {
|
||||
u8 peripheral_info;
|
||||
u8 page_code; /* 0xC4 */
|
||||
u8 reserved1;
|
||||
u8 page_len;
|
||||
u8 page_id[4]; /* "subs" */
|
||||
u8 subsys_id[SUBSYS_ID_LEN];
|
||||
u8 revision[4];
|
||||
u8 slot_id[SLOT_ID_LEN];
|
||||
u8 reserved[2];
|
||||
};
|
||||
|
||||
struct rdac_controller {
|
||||
u8 subsys_id[SUBSYS_ID_LEN];
|
||||
u8 slot_id[SLOT_ID_LEN];
|
||||
int use_10_ms;
|
||||
struct kref kref;
|
||||
struct list_head node; /* list of all controllers */
|
||||
spinlock_t lock;
|
||||
int submitted;
|
||||
struct list_head cmd_list; /* list of commands to be submitted */
|
||||
union {
|
||||
struct rdac_pg_legacy legacy;
|
||||
struct rdac_pg_expanded expanded;
|
||||
} mode_select;
|
||||
};
|
||||
struct c8_inquiry {
|
||||
u8 peripheral_info;
|
||||
u8 page_code; /* 0xC8 */
|
||||
u8 reserved1;
|
||||
u8 page_len;
|
||||
u8 page_id[4]; /* "edid" */
|
||||
u8 reserved2[3];
|
||||
u8 vol_uniq_id_len;
|
||||
u8 vol_uniq_id[16];
|
||||
u8 vol_user_label_len;
|
||||
u8 vol_user_label[60];
|
||||
u8 array_uniq_id_len;
|
||||
u8 array_unique_id[16];
|
||||
u8 array_user_label_len;
|
||||
u8 array_user_label[60];
|
||||
u8 lun[8];
|
||||
};
|
||||
|
||||
struct c2_inquiry {
|
||||
u8 peripheral_info;
|
||||
u8 page_code; /* 0xC2 */
|
||||
u8 reserved1;
|
||||
u8 page_len;
|
||||
u8 page_id[4]; /* "swr4" */
|
||||
u8 sw_version[3];
|
||||
u8 sw_date[3];
|
||||
u8 features_enabled;
|
||||
u8 max_lun_supported;
|
||||
u8 partitions[239]; /* Total allocation length should be 0xFF */
|
||||
};
|
||||
|
||||
struct rdac_handler {
|
||||
struct list_head entry; /* list waiting to submit MODE SELECT */
|
||||
unsigned timeout;
|
||||
struct rdac_controller *ctlr;
|
||||
#define UNINITIALIZED_LUN (1 << 8)
|
||||
unsigned lun;
|
||||
unsigned char sense[SCSI_SENSE_BUFFERSIZE];
|
||||
struct dm_path *path;
|
||||
struct work_struct work;
|
||||
#define SEND_C2_INQUIRY 1
|
||||
#define SEND_C4_INQUIRY 2
|
||||
#define SEND_C8_INQUIRY 3
|
||||
#define SEND_C9_INQUIRY 4
|
||||
#define SEND_MODE_SELECT 5
|
||||
int cmd_to_send;
|
||||
union {
|
||||
struct c2_inquiry c2;
|
||||
struct c4_inquiry c4;
|
||||
struct c8_inquiry c8;
|
||||
struct c9_inquiry c9;
|
||||
} inq;
|
||||
};
|
||||
|
||||
static LIST_HEAD(ctlr_list);
|
||||
static DEFINE_SPINLOCK(list_lock);
|
||||
static struct workqueue_struct *rdac_wkqd;
|
||||
|
||||
static inline int had_failures(struct request *req, int error)
|
||||
{
|
||||
return (error || host_byte(req->errors) != DID_OK ||
|
||||
msg_byte(req->errors) != COMMAND_COMPLETE);
|
||||
}
|
||||
|
||||
static void rdac_resubmit_all(struct rdac_handler *h)
|
||||
{
|
||||
struct rdac_controller *ctlr = h->ctlr;
|
||||
struct rdac_handler *tmp, *h1;
|
||||
|
||||
spin_lock(&ctlr->lock);
|
||||
list_for_each_entry_safe(h1, tmp, &ctlr->cmd_list, entry) {
|
||||
h1->cmd_to_send = SEND_C9_INQUIRY;
|
||||
queue_work(rdac_wkqd, &h1->work);
|
||||
list_del(&h1->entry);
|
||||
}
|
||||
ctlr->submitted = 0;
|
||||
spin_unlock(&ctlr->lock);
|
||||
}
|
||||
|
||||
static void mode_select_endio(struct request *req, int error)
|
||||
{
|
||||
struct rdac_handler *h = req->end_io_data;
|
||||
struct scsi_sense_hdr sense_hdr;
|
||||
int sense = 0, fail = 0;
|
||||
|
||||
if (had_failures(req, error)) {
|
||||
fail = 1;
|
||||
goto failed;
|
||||
}
|
||||
|
||||
if (status_byte(req->errors) == CHECK_CONDITION) {
|
||||
scsi_normalize_sense(req->sense, SCSI_SENSE_BUFFERSIZE,
|
||||
&sense_hdr);
|
||||
sense = (sense_hdr.sense_key << 16) | (sense_hdr.asc << 8) |
|
||||
sense_hdr.ascq;
|
||||
/* If it is retryable failure, submit the c9 inquiry again */
|
||||
if (sense == 0x59136 || sense == 0x68b02 || sense == 0xb8b02 ||
|
||||
sense == 0x62900) {
|
||||
/* 0x59136 - Command lock contention
|
||||
* 0x[6b]8b02 - Quiesense in progress or achieved
|
||||
* 0x62900 - Power On, Reset, or Bus Device Reset
|
||||
*/
|
||||
h->cmd_to_send = SEND_C9_INQUIRY;
|
||||
queue_work(rdac_wkqd, &h->work);
|
||||
goto done;
|
||||
}
|
||||
if (sense)
|
||||
DMINFO("MODE_SELECT failed on %s with sense 0x%x",
|
||||
h->path->dev->name, sense);
|
||||
}
|
||||
failed:
|
||||
if (fail || sense)
|
||||
dm_pg_init_complete(h->path, MP_FAIL_PATH);
|
||||
else
|
||||
dm_pg_init_complete(h->path, 0);
|
||||
|
||||
done:
|
||||
rdac_resubmit_all(h);
|
||||
__blk_put_request(req->q, req);
|
||||
}
|
||||
|
||||
static struct request *get_rdac_req(struct rdac_handler *h,
|
||||
void *buffer, unsigned buflen, int rw)
|
||||
{
|
||||
struct request *rq;
|
||||
struct request_queue *q = bdev_get_queue(h->path->dev->bdev);
|
||||
|
||||
rq = blk_get_request(q, rw, GFP_KERNEL);
|
||||
|
||||
if (!rq) {
|
||||
DMINFO("get_rdac_req: blk_get_request failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (buflen && blk_rq_map_kern(q, rq, buffer, buflen, GFP_KERNEL)) {
|
||||
blk_put_request(rq);
|
||||
DMINFO("get_rdac_req: blk_rq_map_kern failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
rq->sense = h->sense;
|
||||
memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE);
|
||||
rq->sense_len = 0;
|
||||
|
||||
rq->end_io_data = h;
|
||||
rq->timeout = h->timeout;
|
||||
rq->cmd_type = REQ_TYPE_BLOCK_PC;
|
||||
rq->cmd_flags |= REQ_FAILFAST | REQ_NOMERGE;
|
||||
return rq;
|
||||
}
|
||||
|
||||
static struct request *rdac_failover_get(struct rdac_handler *h)
|
||||
{
|
||||
struct request *rq;
|
||||
struct rdac_mode_common *common;
|
||||
unsigned data_size;
|
||||
|
||||
if (h->ctlr->use_10_ms) {
|
||||
struct rdac_pg_expanded *rdac_pg;
|
||||
|
||||
data_size = sizeof(struct rdac_pg_expanded);
|
||||
rdac_pg = &h->ctlr->mode_select.expanded;
|
||||
memset(rdac_pg, 0, data_size);
|
||||
common = &rdac_pg->common;
|
||||
rdac_pg->page_code = RDAC_PAGE_CODE_REDUNDANT_CONTROLLER + 0x40;
|
||||
rdac_pg->subpage_code = 0x1;
|
||||
rdac_pg->page_len[0] = 0x01;
|
||||
rdac_pg->page_len[1] = 0x28;
|
||||
rdac_pg->lun_table[h->lun] = 0x81;
|
||||
} else {
|
||||
struct rdac_pg_legacy *rdac_pg;
|
||||
|
||||
data_size = sizeof(struct rdac_pg_legacy);
|
||||
rdac_pg = &h->ctlr->mode_select.legacy;
|
||||
memset(rdac_pg, 0, data_size);
|
||||
common = &rdac_pg->common;
|
||||
rdac_pg->page_code = RDAC_PAGE_CODE_REDUNDANT_CONTROLLER;
|
||||
rdac_pg->page_len = 0x68;
|
||||
rdac_pg->lun_table[h->lun] = 0x81;
|
||||
}
|
||||
common->rdac_mode[1] = RDAC_MODE_TRANSFER_SPECIFIED_LUNS;
|
||||
common->quiescence_timeout = RDAC_QUIESCENCE_TIME;
|
||||
common->rdac_options = RDAC_FORCED_QUIESENCE;
|
||||
|
||||
/* get request for block layer packet command */
|
||||
rq = get_rdac_req(h, &h->ctlr->mode_select, data_size, WRITE);
|
||||
if (!rq) {
|
||||
DMERR("rdac_failover_get: no rq");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Prepare the command. */
|
||||
if (h->ctlr->use_10_ms) {
|
||||
rq->cmd[0] = MODE_SELECT_10;
|
||||
rq->cmd[7] = data_size >> 8;
|
||||
rq->cmd[8] = data_size & 0xff;
|
||||
} else {
|
||||
rq->cmd[0] = MODE_SELECT;
|
||||
rq->cmd[4] = data_size;
|
||||
}
|
||||
rq->cmd_len = COMMAND_SIZE(rq->cmd[0]);
|
||||
|
||||
return rq;
|
||||
}
|
||||
|
||||
/* Acquires h->ctlr->lock */
|
||||
static void submit_mode_select(struct rdac_handler *h)
|
||||
{
|
||||
struct request *rq;
|
||||
struct request_queue *q = bdev_get_queue(h->path->dev->bdev);
|
||||
|
||||
spin_lock(&h->ctlr->lock);
|
||||
if (h->ctlr->submitted) {
|
||||
list_add(&h->entry, &h->ctlr->cmd_list);
|
||||
goto drop_lock;
|
||||
}
|
||||
|
||||
if (!q) {
|
||||
DMINFO("submit_mode_select: no queue");
|
||||
goto fail_path;
|
||||
}
|
||||
|
||||
rq = rdac_failover_get(h);
|
||||
if (!rq) {
|
||||
DMERR("submit_mode_select: no rq");
|
||||
goto fail_path;
|
||||
}
|
||||
|
||||
DMINFO("queueing MODE_SELECT command on %s", h->path->dev->name);
|
||||
|
||||
blk_execute_rq_nowait(q, NULL, rq, 1, mode_select_endio);
|
||||
h->ctlr->submitted = 1;
|
||||
goto drop_lock;
|
||||
fail_path:
|
||||
dm_pg_init_complete(h->path, MP_FAIL_PATH);
|
||||
drop_lock:
|
||||
spin_unlock(&h->ctlr->lock);
|
||||
}
|
||||
|
||||
static void release_ctlr(struct kref *kref)
|
||||
{
|
||||
struct rdac_controller *ctlr;
|
||||
ctlr = container_of(kref, struct rdac_controller, kref);
|
||||
|
||||
spin_lock(&list_lock);
|
||||
list_del(&ctlr->node);
|
||||
spin_unlock(&list_lock);
|
||||
kfree(ctlr);
|
||||
}
|
||||
|
||||
static struct rdac_controller *get_controller(u8 *subsys_id, u8 *slot_id)
|
||||
{
|
||||
struct rdac_controller *ctlr, *tmp;
|
||||
|
||||
spin_lock(&list_lock);
|
||||
|
||||
list_for_each_entry(tmp, &ctlr_list, node) {
|
||||
if ((memcmp(tmp->subsys_id, subsys_id, SUBSYS_ID_LEN) == 0) &&
|
||||
(memcmp(tmp->slot_id, slot_id, SLOT_ID_LEN) == 0)) {
|
||||
kref_get(&tmp->kref);
|
||||
spin_unlock(&list_lock);
|
||||
return tmp;
|
||||
}
|
||||
}
|
||||
ctlr = kmalloc(sizeof(*ctlr), GFP_ATOMIC);
|
||||
if (!ctlr)
|
||||
goto done;
|
||||
|
||||
/* initialize fields of controller */
|
||||
memcpy(ctlr->subsys_id, subsys_id, SUBSYS_ID_LEN);
|
||||
memcpy(ctlr->slot_id, slot_id, SLOT_ID_LEN);
|
||||
kref_init(&ctlr->kref);
|
||||
spin_lock_init(&ctlr->lock);
|
||||
ctlr->submitted = 0;
|
||||
ctlr->use_10_ms = -1;
|
||||
INIT_LIST_HEAD(&ctlr->cmd_list);
|
||||
list_add(&ctlr->node, &ctlr_list);
|
||||
done:
|
||||
spin_unlock(&list_lock);
|
||||
return ctlr;
|
||||
}
|
||||
|
||||
static void c4_endio(struct request *req, int error)
|
||||
{
|
||||
struct rdac_handler *h = req->end_io_data;
|
||||
struct c4_inquiry *sp;
|
||||
|
||||
if (had_failures(req, error)) {
|
||||
dm_pg_init_complete(h->path, MP_FAIL_PATH);
|
||||
goto done;
|
||||
}
|
||||
|
||||
sp = &h->inq.c4;
|
||||
|
||||
h->ctlr = get_controller(sp->subsys_id, sp->slot_id);
|
||||
|
||||
if (h->ctlr) {
|
||||
h->cmd_to_send = SEND_C9_INQUIRY;
|
||||
queue_work(rdac_wkqd, &h->work);
|
||||
} else
|
||||
dm_pg_init_complete(h->path, MP_FAIL_PATH);
|
||||
done:
|
||||
__blk_put_request(req->q, req);
|
||||
}
|
||||
|
||||
static void c2_endio(struct request *req, int error)
|
||||
{
|
||||
struct rdac_handler *h = req->end_io_data;
|
||||
struct c2_inquiry *sp;
|
||||
|
||||
if (had_failures(req, error)) {
|
||||
dm_pg_init_complete(h->path, MP_FAIL_PATH);
|
||||
goto done;
|
||||
}
|
||||
|
||||
sp = &h->inq.c2;
|
||||
|
||||
/* If more than MODE6_MAX_LUN luns are supported, use mode select 10 */
|
||||
if (sp->max_lun_supported >= MODE6_MAX_LUN)
|
||||
h->ctlr->use_10_ms = 1;
|
||||
else
|
||||
h->ctlr->use_10_ms = 0;
|
||||
|
||||
h->cmd_to_send = SEND_MODE_SELECT;
|
||||
queue_work(rdac_wkqd, &h->work);
|
||||
done:
|
||||
__blk_put_request(req->q, req);
|
||||
}
|
||||
|
||||
static void c9_endio(struct request *req, int error)
|
||||
{
|
||||
struct rdac_handler *h = req->end_io_data;
|
||||
struct c9_inquiry *sp;
|
||||
|
||||
if (had_failures(req, error)) {
|
||||
dm_pg_init_complete(h->path, MP_FAIL_PATH);
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* We need to look at the sense keys here to take clear action.
|
||||
* For now simple logic: If the host is in AVT mode or if controller
|
||||
* owns the lun, return dm_pg_init_complete(), otherwise submit
|
||||
* MODE SELECT.
|
||||
*/
|
||||
sp = &h->inq.c9;
|
||||
|
||||
/* If in AVT mode, return success */
|
||||
if ((sp->avte_cvp >> 7) == 0x1) {
|
||||
dm_pg_init_complete(h->path, 0);
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* If the controller on this path owns the LUN, return success */
|
||||
if (sp->avte_cvp & 0x1) {
|
||||
dm_pg_init_complete(h->path, 0);
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (h->ctlr) {
|
||||
if (h->ctlr->use_10_ms == -1)
|
||||
h->cmd_to_send = SEND_C2_INQUIRY;
|
||||
else
|
||||
h->cmd_to_send = SEND_MODE_SELECT;
|
||||
} else
|
||||
h->cmd_to_send = SEND_C4_INQUIRY;
|
||||
queue_work(rdac_wkqd, &h->work);
|
||||
done:
|
||||
__blk_put_request(req->q, req);
|
||||
}
|
||||
|
||||
static void c8_endio(struct request *req, int error)
|
||||
{
|
||||
struct rdac_handler *h = req->end_io_data;
|
||||
struct c8_inquiry *sp;
|
||||
|
||||
if (had_failures(req, error)) {
|
||||
dm_pg_init_complete(h->path, MP_FAIL_PATH);
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* We need to look at the sense keys here to take clear action.
|
||||
* For now simple logic: Get the lun from the inquiry page.
|
||||
*/
|
||||
sp = &h->inq.c8;
|
||||
h->lun = sp->lun[7]; /* currently it uses only one byte */
|
||||
h->cmd_to_send = SEND_C9_INQUIRY;
|
||||
queue_work(rdac_wkqd, &h->work);
|
||||
done:
|
||||
__blk_put_request(req->q, req);
|
||||
}
|
||||
|
||||
static void submit_inquiry(struct rdac_handler *h, int page_code,
|
||||
unsigned int len, rq_end_io_fn endio)
|
||||
{
|
||||
struct request *rq;
|
||||
struct request_queue *q = bdev_get_queue(h->path->dev->bdev);
|
||||
|
||||
if (!q)
|
||||
goto fail_path;
|
||||
|
||||
rq = get_rdac_req(h, &h->inq, len, READ);
|
||||
if (!rq)
|
||||
goto fail_path;
|
||||
|
||||
/* Prepare the command. */
|
||||
rq->cmd[0] = INQUIRY;
|
||||
rq->cmd[1] = 1;
|
||||
rq->cmd[2] = page_code;
|
||||
rq->cmd[4] = len;
|
||||
rq->cmd_len = COMMAND_SIZE(INQUIRY);
|
||||
blk_execute_rq_nowait(q, NULL, rq, 1, endio);
|
||||
return;
|
||||
|
||||
fail_path:
|
||||
dm_pg_init_complete(h->path, MP_FAIL_PATH);
|
||||
}
|
||||
|
||||
static void service_wkq(struct work_struct *work)
|
||||
{
|
||||
struct rdac_handler *h = container_of(work, struct rdac_handler, work);
|
||||
|
||||
switch (h->cmd_to_send) {
|
||||
case SEND_C2_INQUIRY:
|
||||
submit_inquiry(h, 0xC2, sizeof(struct c2_inquiry), c2_endio);
|
||||
break;
|
||||
case SEND_C4_INQUIRY:
|
||||
submit_inquiry(h, 0xC4, sizeof(struct c4_inquiry), c4_endio);
|
||||
break;
|
||||
case SEND_C8_INQUIRY:
|
||||
submit_inquiry(h, 0xC8, sizeof(struct c8_inquiry), c8_endio);
|
||||
break;
|
||||
case SEND_C9_INQUIRY:
|
||||
submit_inquiry(h, 0xC9, sizeof(struct c9_inquiry), c9_endio);
|
||||
break;
|
||||
case SEND_MODE_SELECT:
|
||||
submit_mode_select(h);
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
}
|
||||
/*
|
||||
* only support subpage2c until we confirm that this is just a matter of
|
||||
* of updating firmware or not, and RDAC (basic AVT works already) for now
|
||||
* but we can add these in in when we get time and testers
|
||||
*/
|
||||
static int rdac_create(struct hw_handler *hwh, unsigned argc, char **argv)
|
||||
{
|
||||
struct rdac_handler *h;
|
||||
unsigned timeout;
|
||||
|
||||
if (argc == 0) {
|
||||
/* No arguments: use defaults */
|
||||
timeout = RDAC_FAILOVER_TIMEOUT;
|
||||
} else if (argc != 1) {
|
||||
DMWARN("incorrect number of arguments");
|
||||
return -EINVAL;
|
||||
} else {
|
||||
if (sscanf(argv[1], "%u", &timeout) != 1) {
|
||||
DMWARN("invalid timeout value");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
h = kzalloc(sizeof(*h), GFP_KERNEL);
|
||||
if (!h)
|
||||
return -ENOMEM;
|
||||
|
||||
hwh->context = h;
|
||||
h->timeout = timeout;
|
||||
h->lun = UNINITIALIZED_LUN;
|
||||
INIT_WORK(&h->work, service_wkq);
|
||||
DMWARN("using RDAC command with timeout %u", h->timeout);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rdac_destroy(struct hw_handler *hwh)
|
||||
{
|
||||
struct rdac_handler *h = hwh->context;
|
||||
|
||||
if (h->ctlr)
|
||||
kref_put(&h->ctlr->kref, release_ctlr);
|
||||
kfree(h);
|
||||
hwh->context = NULL;
|
||||
}
|
||||
|
||||
static unsigned rdac_error(struct hw_handler *hwh, struct bio *bio)
|
||||
{
|
||||
/* Try default handler */
|
||||
return dm_scsi_err_handler(hwh, bio);
|
||||
}
|
||||
|
||||
static void rdac_pg_init(struct hw_handler *hwh, unsigned bypassed,
|
||||
struct dm_path *path)
|
||||
{
|
||||
struct rdac_handler *h = hwh->context;
|
||||
|
||||
h->path = path;
|
||||
switch (h->lun) {
|
||||
case UNINITIALIZED_LUN:
|
||||
submit_inquiry(h, 0xC8, sizeof(struct c8_inquiry), c8_endio);
|
||||
break;
|
||||
default:
|
||||
submit_inquiry(h, 0xC9, sizeof(struct c9_inquiry), c9_endio);
|
||||
}
|
||||
}
|
||||
|
||||
static struct hw_handler_type rdac_handler = {
|
||||
.name = RDAC_DM_HWH_NAME,
|
||||
.module = THIS_MODULE,
|
||||
.create = rdac_create,
|
||||
.destroy = rdac_destroy,
|
||||
.pg_init = rdac_pg_init,
|
||||
.error = rdac_error,
|
||||
};
|
||||
|
||||
static int __init rdac_init(void)
|
||||
{
|
||||
int r;
|
||||
|
||||
rdac_wkqd = create_singlethread_workqueue("rdac_wkqd");
|
||||
if (!rdac_wkqd) {
|
||||
DMERR("Failed to create workqueue rdac_wkqd.");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
r = dm_register_hw_handler(&rdac_handler);
|
||||
if (r < 0) {
|
||||
DMERR("%s: register failed %d", RDAC_DM_HWH_NAME, r);
|
||||
destroy_workqueue(rdac_wkqd);
|
||||
return r;
|
||||
}
|
||||
|
||||
DMINFO("%s: version %s loaded", RDAC_DM_HWH_NAME, RDAC_DM_HWH_VER);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit rdac_exit(void)
|
||||
{
|
||||
int r = dm_unregister_hw_handler(&rdac_handler);
|
||||
|
||||
destroy_workqueue(rdac_wkqd);
|
||||
if (r < 0)
|
||||
DMERR("%s: unregister failed %d", RDAC_DM_HWH_NAME, r);
|
||||
}
|
||||
|
||||
module_init(rdac_init);
|
||||
module_exit(rdac_exit);
|
||||
|
||||
MODULE_DESCRIPTION("DM Multipath LSI/Engenio RDAC support");
|
||||
MODULE_AUTHOR("Mike Christie, Chandra Seetharaman");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_VERSION(RDAC_DM_HWH_VER);
|
|
@ -7,7 +7,6 @@
|
|||
|
||||
#include "dm.h"
|
||||
#include "dm-path-selector.h"
|
||||
#include "dm-hw-handler.h"
|
||||
#include "dm-bio-list.h"
|
||||
#include "dm-bio-record.h"
|
||||
#include "dm-uevent.h"
|
||||
|
@ -20,6 +19,7 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <scsi/scsi_dh.h>
|
||||
#include <asm/atomic.h>
|
||||
|
||||
#define DM_MSG_PREFIX "multipath"
|
||||
|
@ -61,7 +61,8 @@ struct multipath {
|
|||
|
||||
spinlock_t lock;
|
||||
|
||||
struct hw_handler hw_handler;
|
||||
const char *hw_handler_name;
|
||||
struct work_struct activate_path;
|
||||
unsigned nr_priority_groups;
|
||||
struct list_head priority_groups;
|
||||
unsigned pg_init_required; /* pg_init needs calling? */
|
||||
|
@ -106,9 +107,10 @@ typedef int (*action_fn) (struct pgpath *pgpath);
|
|||
|
||||
static struct kmem_cache *_mpio_cache;
|
||||
|
||||
static struct workqueue_struct *kmultipathd;
|
||||
static struct workqueue_struct *kmultipathd, *kmpath_handlerd;
|
||||
static void process_queued_ios(struct work_struct *work);
|
||||
static void trigger_event(struct work_struct *work);
|
||||
static void activate_path(struct work_struct *work);
|
||||
|
||||
|
||||
/*-----------------------------------------------
|
||||
|
@ -178,6 +180,7 @@ static struct multipath *alloc_multipath(struct dm_target *ti)
|
|||
m->queue_io = 1;
|
||||
INIT_WORK(&m->process_queued_ios, process_queued_ios);
|
||||
INIT_WORK(&m->trigger_event, trigger_event);
|
||||
INIT_WORK(&m->activate_path, activate_path);
|
||||
m->mpio_pool = mempool_create_slab_pool(MIN_IOS, _mpio_cache);
|
||||
if (!m->mpio_pool) {
|
||||
kfree(m);
|
||||
|
@ -193,18 +196,13 @@ static struct multipath *alloc_multipath(struct dm_target *ti)
|
|||
static void free_multipath(struct multipath *m)
|
||||
{
|
||||
struct priority_group *pg, *tmp;
|
||||
struct hw_handler *hwh = &m->hw_handler;
|
||||
|
||||
list_for_each_entry_safe(pg, tmp, &m->priority_groups, list) {
|
||||
list_del(&pg->list);
|
||||
free_priority_group(pg, m->ti);
|
||||
}
|
||||
|
||||
if (hwh->type) {
|
||||
hwh->type->destroy(hwh);
|
||||
dm_put_hw_handler(hwh->type);
|
||||
}
|
||||
|
||||
kfree(m->hw_handler_name);
|
||||
mempool_destroy(m->mpio_pool);
|
||||
kfree(m);
|
||||
}
|
||||
|
@ -216,12 +214,10 @@ static void free_multipath(struct multipath *m)
|
|||
|
||||
static void __switch_pg(struct multipath *m, struct pgpath *pgpath)
|
||||
{
|
||||
struct hw_handler *hwh = &m->hw_handler;
|
||||
|
||||
m->current_pg = pgpath->pg;
|
||||
|
||||
/* Must we initialise the PG first, and queue I/O till it's ready? */
|
||||
if (hwh->type && hwh->type->pg_init) {
|
||||
if (m->hw_handler_name) {
|
||||
m->pg_init_required = 1;
|
||||
m->queue_io = 1;
|
||||
} else {
|
||||
|
@ -409,7 +405,6 @@ static void process_queued_ios(struct work_struct *work)
|
|||
{
|
||||
struct multipath *m =
|
||||
container_of(work, struct multipath, process_queued_ios);
|
||||
struct hw_handler *hwh = &m->hw_handler;
|
||||
struct pgpath *pgpath = NULL;
|
||||
unsigned init_required = 0, must_queue = 1;
|
||||
unsigned long flags;
|
||||
|
@ -439,7 +434,7 @@ out:
|
|||
spin_unlock_irqrestore(&m->lock, flags);
|
||||
|
||||
if (init_required)
|
||||
hwh->type->pg_init(hwh, pgpath->pg->bypassed, &pgpath->path);
|
||||
queue_work(kmpath_handlerd, &m->activate_path);
|
||||
|
||||
if (!must_queue)
|
||||
dispatch_queued_ios(m);
|
||||
|
@ -652,8 +647,6 @@ static struct priority_group *parse_priority_group(struct arg_set *as,
|
|||
|
||||
static int parse_hw_handler(struct arg_set *as, struct multipath *m)
|
||||
{
|
||||
int r;
|
||||
struct hw_handler_type *hwht;
|
||||
unsigned hw_argc;
|
||||
struct dm_target *ti = m->ti;
|
||||
|
||||
|
@ -661,30 +654,20 @@ static int parse_hw_handler(struct arg_set *as, struct multipath *m)
|
|||
{0, 1024, "invalid number of hardware handler args"},
|
||||
};
|
||||
|
||||
r = read_param(_params, shift(as), &hw_argc, &ti->error);
|
||||
if (r)
|
||||
if (read_param(_params, shift(as), &hw_argc, &ti->error))
|
||||
return -EINVAL;
|
||||
|
||||
if (!hw_argc)
|
||||
return 0;
|
||||
|
||||
hwht = dm_get_hw_handler(shift(as));
|
||||
if (!hwht) {
|
||||
m->hw_handler_name = kstrdup(shift(as), GFP_KERNEL);
|
||||
request_module("scsi_dh_%s", m->hw_handler_name);
|
||||
if (scsi_dh_handler_exist(m->hw_handler_name) == 0) {
|
||||
ti->error = "unknown hardware handler type";
|
||||
kfree(m->hw_handler_name);
|
||||
m->hw_handler_name = NULL;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
m->hw_handler.md = dm_table_get_md(ti->table);
|
||||
dm_put(m->hw_handler.md);
|
||||
|
||||
r = hwht->create(&m->hw_handler, hw_argc - 1, as->argv);
|
||||
if (r) {
|
||||
dm_put_hw_handler(hwht);
|
||||
ti->error = "hardware handler constructor failed";
|
||||
return r;
|
||||
}
|
||||
|
||||
m->hw_handler.type = hwht;
|
||||
consume(as, hw_argc - 1);
|
||||
|
||||
return 0;
|
||||
|
@ -808,6 +791,7 @@ static void multipath_dtr(struct dm_target *ti)
|
|||
{
|
||||
struct multipath *m = (struct multipath *) ti->private;
|
||||
|
||||
flush_workqueue(kmpath_handlerd);
|
||||
flush_workqueue(kmultipathd);
|
||||
free_multipath(m);
|
||||
}
|
||||
|
@ -1025,52 +1009,85 @@ static int pg_init_limit_reached(struct multipath *m, struct pgpath *pgpath)
|
|||
return limit_reached;
|
||||
}
|
||||
|
||||
/*
|
||||
* pg_init must call this when it has completed its initialisation
|
||||
*/
|
||||
void dm_pg_init_complete(struct dm_path *path, unsigned err_flags)
|
||||
static void pg_init_done(struct dm_path *path, int errors)
|
||||
{
|
||||
struct pgpath *pgpath = path_to_pgpath(path);
|
||||
struct priority_group *pg = pgpath->pg;
|
||||
struct multipath *m = pg->m;
|
||||
unsigned long flags;
|
||||
|
||||
/*
|
||||
* If requested, retry pg_init until maximum number of retries exceeded.
|
||||
* If retry not requested and PG already bypassed, always fail the path.
|
||||
*/
|
||||
if (err_flags & MP_RETRY) {
|
||||
if (pg_init_limit_reached(m, pgpath))
|
||||
err_flags |= MP_FAIL_PATH;
|
||||
} else if (err_flags && pg->bypassed)
|
||||
err_flags |= MP_FAIL_PATH;
|
||||
|
||||
if (err_flags & MP_FAIL_PATH)
|
||||
/* device or driver problems */
|
||||
switch (errors) {
|
||||
case SCSI_DH_OK:
|
||||
break;
|
||||
case SCSI_DH_NOSYS:
|
||||
if (!m->hw_handler_name) {
|
||||
errors = 0;
|
||||
break;
|
||||
}
|
||||
DMERR("Cannot failover device because scsi_dh_%s was not "
|
||||
"loaded.", m->hw_handler_name);
|
||||
/*
|
||||
* Fail path for now, so we do not ping pong
|
||||
*/
|
||||
fail_path(pgpath);
|
||||
|
||||
if (err_flags & MP_BYPASS_PG)
|
||||
break;
|
||||
case SCSI_DH_DEV_TEMP_BUSY:
|
||||
/*
|
||||
* Probably doing something like FW upgrade on the
|
||||
* controller so try the other pg.
|
||||
*/
|
||||
bypass_pg(m, pg, 1);
|
||||
break;
|
||||
/* TODO: For SCSI_DH_RETRY we should wait a couple seconds */
|
||||
case SCSI_DH_RETRY:
|
||||
case SCSI_DH_IMM_RETRY:
|
||||
case SCSI_DH_RES_TEMP_UNAVAIL:
|
||||
if (pg_init_limit_reached(m, pgpath))
|
||||
fail_path(pgpath);
|
||||
errors = 0;
|
||||
break;
|
||||
default:
|
||||
/*
|
||||
* We probably do not want to fail the path for a device
|
||||
* error, but this is what the old dm did. In future
|
||||
* patches we can do more advanced handling.
|
||||
*/
|
||||
fail_path(pgpath);
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&m->lock, flags);
|
||||
if (err_flags & ~MP_RETRY) {
|
||||
if (errors) {
|
||||
DMERR("Could not failover device. Error %d.", errors);
|
||||
m->current_pgpath = NULL;
|
||||
m->current_pg = NULL;
|
||||
} else if (!m->pg_init_required)
|
||||
} else if (!m->pg_init_required) {
|
||||
m->queue_io = 0;
|
||||
pg->bypassed = 0;
|
||||
}
|
||||
|
||||
m->pg_init_in_progress = 0;
|
||||
queue_work(kmultipathd, &m->process_queued_ios);
|
||||
spin_unlock_irqrestore(&m->lock, flags);
|
||||
}
|
||||
|
||||
static void activate_path(struct work_struct *work)
|
||||
{
|
||||
int ret;
|
||||
struct multipath *m =
|
||||
container_of(work, struct multipath, activate_path);
|
||||
struct dm_path *path = &m->current_pgpath->path;
|
||||
|
||||
ret = scsi_dh_activate(bdev_get_queue(path->dev->bdev));
|
||||
pg_init_done(path, ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* end_io handling
|
||||
*/
|
||||
static int do_end_io(struct multipath *m, struct bio *bio,
|
||||
int error, struct dm_mpath_io *mpio)
|
||||
{
|
||||
struct hw_handler *hwh = &m->hw_handler;
|
||||
unsigned err_flags = MP_FAIL_PATH; /* Default behavior */
|
||||
unsigned long flags;
|
||||
|
||||
if (!error)
|
||||
|
@ -1097,19 +1114,8 @@ static int do_end_io(struct multipath *m, struct bio *bio,
|
|||
}
|
||||
spin_unlock_irqrestore(&m->lock, flags);
|
||||
|
||||
if (hwh->type && hwh->type->error)
|
||||
err_flags = hwh->type->error(hwh, bio);
|
||||
|
||||
if (mpio->pgpath) {
|
||||
if (err_flags & MP_FAIL_PATH)
|
||||
fail_path(mpio->pgpath);
|
||||
|
||||
if (err_flags & MP_BYPASS_PG)
|
||||
bypass_pg(m, mpio->pgpath->pg, 1);
|
||||
}
|
||||
|
||||
if (err_flags & MP_ERROR_IO)
|
||||
return -EIO;
|
||||
if (mpio->pgpath)
|
||||
fail_path(mpio->pgpath);
|
||||
|
||||
requeue:
|
||||
dm_bio_restore(&mpio->details, bio);
|
||||
|
@ -1194,7 +1200,6 @@ static int multipath_status(struct dm_target *ti, status_type_t type,
|
|||
int sz = 0;
|
||||
unsigned long flags;
|
||||
struct multipath *m = (struct multipath *) ti->private;
|
||||
struct hw_handler *hwh = &m->hw_handler;
|
||||
struct priority_group *pg;
|
||||
struct pgpath *p;
|
||||
unsigned pg_num;
|
||||
|
@ -1214,12 +1219,10 @@ static int multipath_status(struct dm_target *ti, status_type_t type,
|
|||
DMEMIT("pg_init_retries %u ", m->pg_init_retries);
|
||||
}
|
||||
|
||||
if (hwh->type && hwh->type->status)
|
||||
sz += hwh->type->status(hwh, type, result + sz, maxlen - sz);
|
||||
else if (!hwh->type || type == STATUSTYPE_INFO)
|
||||
if (!m->hw_handler_name || type == STATUSTYPE_INFO)
|
||||
DMEMIT("0 ");
|
||||
else
|
||||
DMEMIT("1 %s ", hwh->type->name);
|
||||
DMEMIT("1 %s ", m->hw_handler_name);
|
||||
|
||||
DMEMIT("%u ", m->nr_priority_groups);
|
||||
|
||||
|
@ -1422,6 +1425,21 @@ static int __init dm_multipath_init(void)
|
|||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/*
|
||||
* A separate workqueue is used to handle the device handlers
|
||||
* to avoid overloading existing workqueue. Overloading the
|
||||
* old workqueue would also create a bottleneck in the
|
||||
* path of the storage hardware device activation.
|
||||
*/
|
||||
kmpath_handlerd = create_singlethread_workqueue("kmpath_handlerd");
|
||||
if (!kmpath_handlerd) {
|
||||
DMERR("failed to create workqueue kmpath_handlerd");
|
||||
destroy_workqueue(kmultipathd);
|
||||
dm_unregister_target(&multipath_target);
|
||||
kmem_cache_destroy(_mpio_cache);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
DMINFO("version %u.%u.%u loaded",
|
||||
multipath_target.version[0], multipath_target.version[1],
|
||||
multipath_target.version[2]);
|
||||
|
@ -1433,6 +1451,7 @@ static void __exit dm_multipath_exit(void)
|
|||
{
|
||||
int r;
|
||||
|
||||
destroy_workqueue(kmpath_handlerd);
|
||||
destroy_workqueue(kmultipathd);
|
||||
|
||||
r = dm_unregister_target(&multipath_target);
|
||||
|
@ -1441,8 +1460,6 @@ static void __exit dm_multipath_exit(void)
|
|||
kmem_cache_destroy(_mpio_cache);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(dm_pg_init_complete);
|
||||
|
||||
module_init(dm_multipath_init);
|
||||
module_exit(dm_multipath_exit);
|
||||
|
||||
|
|
|
@ -16,7 +16,6 @@ struct dm_path {
|
|||
unsigned is_active; /* Read-only */
|
||||
|
||||
void *pscontext; /* For path-selector use */
|
||||
void *hwhcontext; /* For hw-handler use */
|
||||
};
|
||||
|
||||
/* Callback for hwh_pg_init_fn to use when complete */
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2000-2007 LSI Corporation.
|
||||
* Copyright (c) 2000-2008 LSI Corporation.
|
||||
*
|
||||
*
|
||||
* Name: mpi.h
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2000-2007 LSI Corporation.
|
||||
* Copyright (c) 2000-2008 LSI Corporation.
|
||||
*
|
||||
*
|
||||
* Name: mpi_cnfg.h
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* For use with LSI PCI chip/adapter(s)
|
||||
* running LSI Fusion MPT (Message Passing Technology) firmware.
|
||||
*
|
||||
* Copyright (c) 1999-2007 LSI Corporation
|
||||
* Copyright (c) 1999-2008 LSI Corporation
|
||||
* (mailto:DL-MPTFusionLinux@lsi.com)
|
||||
*
|
||||
*/
|
||||
|
@ -103,7 +103,7 @@ static int mfcounter = 0;
|
|||
* Public data...
|
||||
*/
|
||||
|
||||
struct proc_dir_entry *mpt_proc_root_dir;
|
||||
static struct proc_dir_entry *mpt_proc_root_dir;
|
||||
|
||||
#define WHOINIT_UNKNOWN 0xAA
|
||||
|
||||
|
@ -253,6 +253,55 @@ mpt_get_cb_idx(MPT_DRIVER_CLASS dclass)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* mpt_fault_reset_work - work performed on workq after ioc fault
|
||||
* @work: input argument, used to derive ioc
|
||||
*
|
||||
**/
|
||||
static void
|
||||
mpt_fault_reset_work(struct work_struct *work)
|
||||
{
|
||||
MPT_ADAPTER *ioc =
|
||||
container_of(work, MPT_ADAPTER, fault_reset_work.work);
|
||||
u32 ioc_raw_state;
|
||||
int rc;
|
||||
unsigned long flags;
|
||||
|
||||
if (ioc->diagPending || !ioc->active)
|
||||
goto out;
|
||||
|
||||
ioc_raw_state = mpt_GetIocState(ioc, 0);
|
||||
if ((ioc_raw_state & MPI_IOC_STATE_MASK) == MPI_IOC_STATE_FAULT) {
|
||||
printk(MYIOC_s_WARN_FMT "IOC is in FAULT state (%04xh)!!!\n",
|
||||
ioc->name, ioc_raw_state & MPI_DOORBELL_DATA_MASK);
|
||||
printk(MYIOC_s_WARN_FMT "Issuing HardReset from %s!!\n",
|
||||
ioc->name, __FUNCTION__);
|
||||
rc = mpt_HardResetHandler(ioc, CAN_SLEEP);
|
||||
printk(MYIOC_s_WARN_FMT "%s: HardReset: %s\n", ioc->name,
|
||||
__FUNCTION__, (rc == 0) ? "success" : "failed");
|
||||
ioc_raw_state = mpt_GetIocState(ioc, 0);
|
||||
if ((ioc_raw_state & MPI_IOC_STATE_MASK) == MPI_IOC_STATE_FAULT)
|
||||
printk(MYIOC_s_WARN_FMT "IOC is in FAULT state after "
|
||||
"reset (%04xh)\n", ioc->name, ioc_raw_state &
|
||||
MPI_DOORBELL_DATA_MASK);
|
||||
}
|
||||
|
||||
out:
|
||||
/*
|
||||
* Take turns polling alternate controller
|
||||
*/
|
||||
if (ioc->alt_ioc)
|
||||
ioc = ioc->alt_ioc;
|
||||
|
||||
/* rearm the timer */
|
||||
spin_lock_irqsave(&ioc->fault_reset_work_lock, flags);
|
||||
if (ioc->reset_work_q)
|
||||
queue_delayed_work(ioc->reset_work_q, &ioc->fault_reset_work,
|
||||
msecs_to_jiffies(MPT_POLLING_INTERVAL));
|
||||
spin_unlock_irqrestore(&ioc->fault_reset_work_lock, flags);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Process turbo (context) reply...
|
||||
*/
|
||||
|
@ -1616,6 +1665,22 @@ mpt_attach(struct pci_dev *pdev, const struct pci_device_id *id)
|
|||
/* Find lookup slot. */
|
||||
INIT_LIST_HEAD(&ioc->list);
|
||||
|
||||
|
||||
/* Initialize workqueue */
|
||||
INIT_DELAYED_WORK(&ioc->fault_reset_work, mpt_fault_reset_work);
|
||||
spin_lock_init(&ioc->fault_reset_work_lock);
|
||||
|
||||
snprintf(ioc->reset_work_q_name, KOBJ_NAME_LEN, "mpt_poll_%d", ioc->id);
|
||||
ioc->reset_work_q =
|
||||
create_singlethread_workqueue(ioc->reset_work_q_name);
|
||||
if (!ioc->reset_work_q) {
|
||||
printk(MYIOC_s_ERR_FMT "Insufficient memory to add adapter!\n",
|
||||
ioc->name);
|
||||
pci_release_selected_regions(pdev, ioc->bars);
|
||||
kfree(ioc);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
dinitprintk(ioc, printk(MYIOC_s_INFO_FMT "facts @ %p, pfacts[0] @ %p\n",
|
||||
ioc->name, &ioc->facts, &ioc->pfacts[0]));
|
||||
|
||||
|
@ -1727,6 +1792,10 @@ mpt_attach(struct pci_dev *pdev, const struct pci_device_id *id)
|
|||
iounmap(ioc->memmap);
|
||||
if (r != -5)
|
||||
pci_release_selected_regions(pdev, ioc->bars);
|
||||
|
||||
destroy_workqueue(ioc->reset_work_q);
|
||||
ioc->reset_work_q = NULL;
|
||||
|
||||
kfree(ioc);
|
||||
pci_set_drvdata(pdev, NULL);
|
||||
return r;
|
||||
|
@ -1759,6 +1828,10 @@ mpt_attach(struct pci_dev *pdev, const struct pci_device_id *id)
|
|||
}
|
||||
#endif
|
||||
|
||||
if (!ioc->alt_ioc)
|
||||
queue_delayed_work(ioc->reset_work_q, &ioc->fault_reset_work,
|
||||
msecs_to_jiffies(MPT_POLLING_INTERVAL));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1774,6 +1847,19 @@ mpt_detach(struct pci_dev *pdev)
|
|||
MPT_ADAPTER *ioc = pci_get_drvdata(pdev);
|
||||
char pname[32];
|
||||
u8 cb_idx;
|
||||
unsigned long flags;
|
||||
struct workqueue_struct *wq;
|
||||
|
||||
/*
|
||||
* Stop polling ioc for fault condition
|
||||
*/
|
||||
spin_lock_irqsave(&ioc->fault_reset_work_lock, flags);
|
||||
wq = ioc->reset_work_q;
|
||||
ioc->reset_work_q = NULL;
|
||||
spin_unlock_irqrestore(&ioc->fault_reset_work_lock, flags);
|
||||
cancel_delayed_work(&ioc->fault_reset_work);
|
||||
destroy_workqueue(wq);
|
||||
|
||||
|
||||
sprintf(pname, MPT_PROCFS_MPTBASEDIR "/%s/summary", ioc->name);
|
||||
remove_proc_entry(pname, NULL);
|
||||
|
@ -7456,7 +7542,6 @@ EXPORT_SYMBOL(mpt_resume);
|
|||
EXPORT_SYMBOL(mpt_suspend);
|
||||
#endif
|
||||
EXPORT_SYMBOL(ioc_list);
|
||||
EXPORT_SYMBOL(mpt_proc_root_dir);
|
||||
EXPORT_SYMBOL(mpt_register);
|
||||
EXPORT_SYMBOL(mpt_deregister);
|
||||
EXPORT_SYMBOL(mpt_event_register);
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* LSIFC9xx/LSI409xx Fibre Channel
|
||||
* running LSI Fusion MPT (Message Passing Technology) firmware.
|
||||
*
|
||||
* Copyright (c) 1999-2007 LSI Corporation
|
||||
* Copyright (c) 1999-2008 LSI Corporation
|
||||
* (mailto:DL-MPTFusionLinux@lsi.com)
|
||||
*
|
||||
*/
|
||||
|
@ -73,11 +73,11 @@
|
|||
#endif
|
||||
|
||||
#ifndef COPYRIGHT
|
||||
#define COPYRIGHT "Copyright (c) 1999-2007 " MODULEAUTHOR
|
||||
#define COPYRIGHT "Copyright (c) 1999-2008 " MODULEAUTHOR
|
||||
#endif
|
||||
|
||||
#define MPT_LINUX_VERSION_COMMON "3.04.06"
|
||||
#define MPT_LINUX_PACKAGE_NAME "@(#)mptlinux-3.04.06"
|
||||
#define MPT_LINUX_VERSION_COMMON "3.04.07"
|
||||
#define MPT_LINUX_PACKAGE_NAME "@(#)mptlinux-3.04.07"
|
||||
#define WHAT_MAGIC_STRING "@" "(" "#" ")"
|
||||
|
||||
#define show_mptmod_ver(s,ver) \
|
||||
|
@ -176,6 +176,8 @@
|
|||
/* debug print string length used for events and iocstatus */
|
||||
# define EVENT_DESCR_STR_SZ 100
|
||||
|
||||
#define MPT_POLLING_INTERVAL 1000 /* in milliseconds */
|
||||
|
||||
#ifdef __KERNEL__ /* { */
|
||||
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
|
||||
|
||||
|
@ -709,6 +711,12 @@ typedef struct _MPT_ADAPTER
|
|||
struct workqueue_struct *fc_rescan_work_q;
|
||||
struct scsi_cmnd **ScsiLookup;
|
||||
spinlock_t scsi_lookup_lock;
|
||||
|
||||
char reset_work_q_name[KOBJ_NAME_LEN];
|
||||
struct workqueue_struct *reset_work_q;
|
||||
struct delayed_work fault_reset_work;
|
||||
spinlock_t fault_reset_work_lock;
|
||||
|
||||
} MPT_ADAPTER;
|
||||
|
||||
/*
|
||||
|
@ -919,7 +927,6 @@ extern int mpt_raid_phys_disk_pg0(MPT_ADAPTER *ioc, u8 phys_disk_num, pRaidPhys
|
|||
* Public data decl's...
|
||||
*/
|
||||
extern struct list_head ioc_list;
|
||||
extern struct proc_dir_entry *mpt_proc_root_dir;
|
||||
|
||||
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
|
||||
#endif /* } __KERNEL__ */
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* For use with LSI PCI chip/adapters
|
||||
* running LSI Fusion MPT (Message Passing Technology) firmware.
|
||||
*
|
||||
* Copyright (c) 1999-2007 LSI Corporation
|
||||
* Copyright (c) 1999-2008 LSI Corporation
|
||||
* (mailto:DL-MPTFusionLinux@lsi.com)
|
||||
*
|
||||
*/
|
||||
|
@ -66,7 +66,7 @@
|
|||
#include <scsi/scsi_host.h>
|
||||
#include <scsi/scsi_tcq.h>
|
||||
|
||||
#define COPYRIGHT "Copyright (c) 1999-2007 LSI Corporation"
|
||||
#define COPYRIGHT "Copyright (c) 1999-2008 LSI Corporation"
|
||||
#define MODULEAUTHOR "LSI Corporation"
|
||||
#include "mptbase.h"
|
||||
#include "mptctl.h"
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* LSIFC9xx/LSI409xx Fibre Channel
|
||||
* running LSI Fusion MPT (Message Passing Technology) firmware.
|
||||
*
|
||||
* Copyright (c) 1999-2007 LSI Corporation
|
||||
* Copyright (c) 1999-2008 LSI Corporation
|
||||
* (mailto:DL-MPTFusionLinux@lsi.com)
|
||||
*
|
||||
*/
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* For use with LSI PCI chip/adapter(s)
|
||||
* running LSI Fusion MPT (Message Passing Technology) firmware.
|
||||
*
|
||||
* Copyright (c) 1999-2007 LSI Corporation
|
||||
* Copyright (c) 1999-2008 LSI Corporation
|
||||
* (mailto:DL-MPTFusionLinux@lsi.com)
|
||||
*
|
||||
*/
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* For use with LSI PCI chip/adapter(s)
|
||||
* running LSI Fusion MPT (Message Passing Technology) firmware.
|
||||
*
|
||||
* Copyright (c) 1999-2007 LSI Corporation
|
||||
* Copyright (c) 1999-2008 LSI Corporation
|
||||
* (mailto:DL-MPTFusionLinux@lsi.com)
|
||||
*
|
||||
*/
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* For use with LSI Fibre Channel PCI chip/adapters
|
||||
* running LSI Fusion MPT (Message Passing Technology) firmware.
|
||||
*
|
||||
* Copyright (c) 2000-2007 LSI Corporation
|
||||
* Copyright (c) 2000-2008 LSI Corporation
|
||||
* (mailto:DL-MPTFusionLinux@lsi.com)
|
||||
*
|
||||
*/
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* For use with LSI Fibre Channel PCI chip/adapters
|
||||
* running LSI Fusion MPT (Message Passing Technology) firmware.
|
||||
*
|
||||
* Copyright (c) 2000-2007 LSI Corporation
|
||||
* Copyright (c) 2000-2008 LSI Corporation
|
||||
* (mailto:DL-MPTFusionLinux@lsi.com)
|
||||
*
|
||||
*/
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* For use with LSI PCI chip/adapter(s)
|
||||
* running LSI Fusion MPT (Message Passing Technology) firmware.
|
||||
*
|
||||
* Copyright (c) 1999-2007 LSI Corporation
|
||||
* Copyright (c) 1999-2008 LSI Corporation
|
||||
* (mailto:DL-MPTFusionLinux@lsi.com)
|
||||
*/
|
||||
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* LSIFC9xx/LSI409xx Fibre Channel
|
||||
* running LSI MPT (Message Passing Technology) firmware.
|
||||
*
|
||||
* Copyright (c) 1999-2007 LSI Corporation
|
||||
* Copyright (c) 1999-2008 LSI Corporation
|
||||
* (mailto:DL-MPTFusionLinux@lsi.com)
|
||||
*
|
||||
*/
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* For use with LSI PCI chip/adapter(s)
|
||||
* running LSI Fusion MPT (Message Passing Technology) firmware.
|
||||
*
|
||||
* Copyright (c) 1999-2007 LSI Corporation
|
||||
* Copyright (c) 1999-2008 LSI Corporation
|
||||
* (mailto:DL-MPTFusionLinux@lsi.com)
|
||||
*
|
||||
*/
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* LSIFC9xx/LSI409xx Fibre Channel
|
||||
* running LSI Fusion MPT (Message Passing Technology) firmware.
|
||||
*
|
||||
* Copyright (c) 1999-2007 LSI Corporation
|
||||
* Copyright (c) 1999-2008 LSI Corporation
|
||||
* (mailto:DL-MPTFusionLinux@lsi.com)
|
||||
*
|
||||
*/
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* For use with LSI PCI chip/adapter(s)
|
||||
* running LSI Fusion MPT (Message Passing Technology) firmware.
|
||||
*
|
||||
* Copyright (c) 1999-2007 LSI Corporation
|
||||
* Copyright (c) 1999-2008 LSI Corporation
|
||||
* (mailto:DL-MPTFusionLinux@lsi.com)
|
||||
*
|
||||
*/
|
||||
|
@ -447,6 +447,7 @@ static int mptspi_target_alloc(struct scsi_target *starget)
|
|||
spi_max_offset(starget) = ioc->spi_data.maxSyncOffset;
|
||||
|
||||
spi_offset(starget) = 0;
|
||||
spi_period(starget) = 0xFF;
|
||||
mptspi_write_width(starget, 0);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
#
|
||||
|
||||
zfcp-objs := zfcp_aux.o zfcp_ccw.o zfcp_scsi.o zfcp_erp.o zfcp_qdio.o \
|
||||
zfcp_fsf.o zfcp_dbf.o zfcp_sysfs_adapter.o zfcp_sysfs_port.o \
|
||||
zfcp_sysfs_unit.o zfcp_sysfs_driver.o
|
||||
zfcp_fsf.o zfcp_dbf.o zfcp_sysfs.o zfcp_fc.o zfcp_cfdc.o
|
||||
|
||||
obj-$(CONFIG_ZFCP) += zfcp.o
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,64 +1,13 @@
|
|||
/*
|
||||
* This file is part of the zfcp device driver for
|
||||
* FCP adapters for IBM System z9 and zSeries.
|
||||
* zfcp device driver
|
||||
*
|
||||
* (C) Copyright IBM Corp. 2002, 2006
|
||||
* Registration and callback for the s390 common I/O layer.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
* Copyright IBM Corporation 2002, 2008
|
||||
*/
|
||||
|
||||
#include "zfcp_ext.h"
|
||||
|
||||
#define ZFCP_LOG_AREA ZFCP_LOG_AREA_CONFIG
|
||||
|
||||
static int zfcp_ccw_probe(struct ccw_device *);
|
||||
static void zfcp_ccw_remove(struct ccw_device *);
|
||||
static int zfcp_ccw_set_online(struct ccw_device *);
|
||||
static int zfcp_ccw_set_offline(struct ccw_device *);
|
||||
static int zfcp_ccw_notify(struct ccw_device *, int);
|
||||
static void zfcp_ccw_shutdown(struct ccw_device *);
|
||||
|
||||
static struct ccw_device_id zfcp_ccw_device_id[] = {
|
||||
{CCW_DEVICE_DEVTYPE(ZFCP_CONTROL_UNIT_TYPE,
|
||||
ZFCP_CONTROL_UNIT_MODEL,
|
||||
ZFCP_DEVICE_TYPE,
|
||||
ZFCP_DEVICE_MODEL)},
|
||||
{CCW_DEVICE_DEVTYPE(ZFCP_CONTROL_UNIT_TYPE,
|
||||
ZFCP_CONTROL_UNIT_MODEL,
|
||||
ZFCP_DEVICE_TYPE,
|
||||
ZFCP_DEVICE_MODEL_PRIV)},
|
||||
{},
|
||||
};
|
||||
|
||||
static struct ccw_driver zfcp_ccw_driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = ZFCP_NAME,
|
||||
.ids = zfcp_ccw_device_id,
|
||||
.probe = zfcp_ccw_probe,
|
||||
.remove = zfcp_ccw_remove,
|
||||
.set_online = zfcp_ccw_set_online,
|
||||
.set_offline = zfcp_ccw_set_offline,
|
||||
.notify = zfcp_ccw_notify,
|
||||
.shutdown = zfcp_ccw_shutdown,
|
||||
.driver = {
|
||||
.groups = zfcp_driver_attr_groups,
|
||||
},
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(ccw, zfcp_ccw_device_id);
|
||||
|
||||
/**
|
||||
* zfcp_ccw_probe - probe function of zfcp driver
|
||||
* @ccw_device: pointer to belonging ccw device
|
||||
|
@ -69,19 +18,16 @@ MODULE_DEVICE_TABLE(ccw, zfcp_ccw_device_id);
|
|||
* In addition the nameserver port will be added to the ports of the adapter
|
||||
* and its sysfs representation will be created too.
|
||||
*/
|
||||
static int
|
||||
zfcp_ccw_probe(struct ccw_device *ccw_device)
|
||||
static int zfcp_ccw_probe(struct ccw_device *ccw_device)
|
||||
{
|
||||
struct zfcp_adapter *adapter;
|
||||
int retval = 0;
|
||||
|
||||
down(&zfcp_data.config_sema);
|
||||
adapter = zfcp_adapter_enqueue(ccw_device);
|
||||
if (!adapter)
|
||||
if (zfcp_adapter_enqueue(ccw_device)) {
|
||||
dev_err(&ccw_device->dev,
|
||||
"Setup of data structures failed.\n");
|
||||
retval = -EINVAL;
|
||||
else
|
||||
ZFCP_LOG_DEBUG("Probed adapter %s\n",
|
||||
zfcp_get_busid_by_adapter(adapter));
|
||||
}
|
||||
up(&zfcp_data.config_sema);
|
||||
return retval;
|
||||
}
|
||||
|
@ -95,8 +41,7 @@ zfcp_ccw_probe(struct ccw_device *ccw_device)
|
|||
* ports that belong to this adapter. And in addition all resources of this
|
||||
* adapter will be freed too.
|
||||
*/
|
||||
static void
|
||||
zfcp_ccw_remove(struct ccw_device *ccw_device)
|
||||
static void zfcp_ccw_remove(struct ccw_device *ccw_device)
|
||||
{
|
||||
struct zfcp_adapter *adapter;
|
||||
struct zfcp_port *port, *p;
|
||||
|
@ -106,8 +51,6 @@ zfcp_ccw_remove(struct ccw_device *ccw_device)
|
|||
down(&zfcp_data.config_sema);
|
||||
adapter = dev_get_drvdata(&ccw_device->dev);
|
||||
|
||||
ZFCP_LOG_DEBUG("Removing adapter %s\n",
|
||||
zfcp_get_busid_by_adapter(adapter));
|
||||
write_lock_irq(&zfcp_data.config_lock);
|
||||
list_for_each_entry_safe(port, p, &adapter->port_list_head, list) {
|
||||
list_for_each_entry_safe(unit, u, &port->unit_list_head, list) {
|
||||
|
@ -145,8 +88,7 @@ zfcp_ccw_remove(struct ccw_device *ccw_device)
|
|||
* registered with the SCSI stack, that the QDIO queues will be set up
|
||||
* and that the adapter will be opened (asynchronously).
|
||||
*/
|
||||
static int
|
||||
zfcp_ccw_set_online(struct ccw_device *ccw_device)
|
||||
static int zfcp_ccw_set_online(struct ccw_device *ccw_device)
|
||||
{
|
||||
struct zfcp_adapter *adapter;
|
||||
int retval;
|
||||
|
@ -155,12 +97,8 @@ zfcp_ccw_set_online(struct ccw_device *ccw_device)
|
|||
adapter = dev_get_drvdata(&ccw_device->dev);
|
||||
|
||||
retval = zfcp_erp_thread_setup(adapter);
|
||||
if (retval) {
|
||||
ZFCP_LOG_INFO("error: start of error recovery thread for "
|
||||
"adapter %s failed\n",
|
||||
zfcp_get_busid_by_adapter(adapter));
|
||||
if (retval)
|
||||
goto out;
|
||||
}
|
||||
|
||||
retval = zfcp_adapter_scsi_register(adapter);
|
||||
if (retval)
|
||||
|
@ -191,8 +129,7 @@ zfcp_ccw_set_online(struct ccw_device *ccw_device)
|
|||
* This function gets called by the common i/o layer and sets an adapter
|
||||
* into state offline.
|
||||
*/
|
||||
static int
|
||||
zfcp_ccw_set_offline(struct ccw_device *ccw_device)
|
||||
static int zfcp_ccw_set_offline(struct ccw_device *ccw_device)
|
||||
{
|
||||
struct zfcp_adapter *adapter;
|
||||
|
||||
|
@ -206,15 +143,14 @@ zfcp_ccw_set_offline(struct ccw_device *ccw_device)
|
|||
}
|
||||
|
||||
/**
|
||||
* zfcp_ccw_notify
|
||||
* zfcp_ccw_notify - ccw notify function
|
||||
* @ccw_device: pointer to belonging ccw device
|
||||
* @event: indicates if adapter was detached or attached
|
||||
*
|
||||
* This function gets called by the common i/o layer if an adapter has gone
|
||||
* or reappeared.
|
||||
*/
|
||||
static int
|
||||
zfcp_ccw_notify(struct ccw_device *ccw_device, int event)
|
||||
static int zfcp_ccw_notify(struct ccw_device *ccw_device, int event)
|
||||
{
|
||||
struct zfcp_adapter *adapter;
|
||||
|
||||
|
@ -222,18 +158,15 @@ zfcp_ccw_notify(struct ccw_device *ccw_device, int event)
|
|||
adapter = dev_get_drvdata(&ccw_device->dev);
|
||||
switch (event) {
|
||||
case CIO_GONE:
|
||||
ZFCP_LOG_NORMAL("adapter %s: device gone\n",
|
||||
zfcp_get_busid_by_adapter(adapter));
|
||||
dev_warn(&adapter->ccw_device->dev, "device gone\n");
|
||||
zfcp_erp_adapter_shutdown(adapter, 0, 87, NULL);
|
||||
break;
|
||||
case CIO_NO_PATH:
|
||||
ZFCP_LOG_NORMAL("adapter %s: no path\n",
|
||||
zfcp_get_busid_by_adapter(adapter));
|
||||
dev_warn(&adapter->ccw_device->dev, "no path\n");
|
||||
zfcp_erp_adapter_shutdown(adapter, 0, 88, NULL);
|
||||
break;
|
||||
case CIO_OPER:
|
||||
ZFCP_LOG_NORMAL("adapter %s: operational again\n",
|
||||
zfcp_get_busid_by_adapter(adapter));
|
||||
dev_info(&adapter->ccw_device->dev, "operational again\n");
|
||||
zfcp_erp_modify_adapter_status(adapter, 11, NULL,
|
||||
ZFCP_STATUS_COMMON_RUNNING,
|
||||
ZFCP_SET);
|
||||
|
@ -247,24 +180,10 @@ zfcp_ccw_notify(struct ccw_device *ccw_device, int event)
|
|||
}
|
||||
|
||||
/**
|
||||
* zfcp_ccw_register - ccw register function
|
||||
*
|
||||
* Registers the driver at the common i/o layer. This function will be called
|
||||
* at module load time/system start.
|
||||
* zfcp_ccw_shutdown - handle shutdown from cio
|
||||
* @cdev: device for adapter to shutdown.
|
||||
*/
|
||||
int __init
|
||||
zfcp_ccw_register(void)
|
||||
{
|
||||
return ccw_driver_register(&zfcp_ccw_driver);
|
||||
}
|
||||
|
||||
/**
|
||||
* zfcp_ccw_shutdown - gets called on reboot/shutdown
|
||||
*
|
||||
* Makes sure that QDIO queues are down when the system gets stopped.
|
||||
*/
|
||||
static void
|
||||
zfcp_ccw_shutdown(struct ccw_device *cdev)
|
||||
static void zfcp_ccw_shutdown(struct ccw_device *cdev)
|
||||
{
|
||||
struct zfcp_adapter *adapter;
|
||||
|
||||
|
@ -275,4 +194,33 @@ zfcp_ccw_shutdown(struct ccw_device *cdev)
|
|||
up(&zfcp_data.config_sema);
|
||||
}
|
||||
|
||||
#undef ZFCP_LOG_AREA
|
||||
static struct ccw_device_id zfcp_ccw_device_id[] = {
|
||||
{ CCW_DEVICE_DEVTYPE(0x1731, 0x3, 0x1732, 0x3) },
|
||||
{ CCW_DEVICE_DEVTYPE(0x1731, 0x3, 0x1732, 0x4) }, /* priv. */
|
||||
{},
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(ccw, zfcp_ccw_device_id);
|
||||
|
||||
static struct ccw_driver zfcp_ccw_driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "zfcp",
|
||||
.ids = zfcp_ccw_device_id,
|
||||
.probe = zfcp_ccw_probe,
|
||||
.remove = zfcp_ccw_remove,
|
||||
.set_online = zfcp_ccw_set_online,
|
||||
.set_offline = zfcp_ccw_set_offline,
|
||||
.notify = zfcp_ccw_notify,
|
||||
.shutdown = zfcp_ccw_shutdown,
|
||||
};
|
||||
|
||||
/**
|
||||
* zfcp_ccw_register - ccw register function
|
||||
*
|
||||
* Registers the driver at the common i/o layer. This function will be called
|
||||
* at module load time/system start.
|
||||
*/
|
||||
int __init zfcp_ccw_register(void)
|
||||
{
|
||||
return ccw_driver_register(&zfcp_ccw_driver);
|
||||
}
|
||||
|
|
259
drivers/s390/scsi/zfcp_cfdc.c
Normal file
259
drivers/s390/scsi/zfcp_cfdc.c
Normal file
|
@ -0,0 +1,259 @@
|
|||
/*
|
||||
* zfcp device driver
|
||||
*
|
||||
* Userspace interface for accessing the
|
||||
* Access Control Lists / Control File Data Channel
|
||||
*
|
||||
* Copyright IBM Corporation 2008
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <asm/ccwdev.h>
|
||||
#include "zfcp_def.h"
|
||||
#include "zfcp_ext.h"
|
||||
#include "zfcp_fsf.h"
|
||||
|
||||
#define ZFCP_CFDC_CMND_DOWNLOAD_NORMAL 0x00010001
|
||||
#define ZFCP_CFDC_CMND_DOWNLOAD_FORCE 0x00010101
|
||||
#define ZFCP_CFDC_CMND_FULL_ACCESS 0x00000201
|
||||
#define ZFCP_CFDC_CMND_RESTRICTED_ACCESS 0x00000401
|
||||
#define ZFCP_CFDC_CMND_UPLOAD 0x00010002
|
||||
|
||||
#define ZFCP_CFDC_DOWNLOAD 0x00000001
|
||||
#define ZFCP_CFDC_UPLOAD 0x00000002
|
||||
#define ZFCP_CFDC_WITH_CONTROL_FILE 0x00010000
|
||||
|
||||
#define ZFCP_CFDC_IOC_MAGIC 0xDD
|
||||
#define ZFCP_CFDC_IOC \
|
||||
_IOWR(ZFCP_CFDC_IOC_MAGIC, 0, struct zfcp_cfdc_data)
|
||||
|
||||
/**
|
||||
* struct zfcp_cfdc_data - data for ioctl cfdc interface
|
||||
* @signature: request signature
|
||||
* @devno: FCP adapter device number
|
||||
* @command: command code
|
||||
* @fsf_status: returns status of FSF command to userspace
|
||||
* @fsf_status_qual: returned to userspace
|
||||
* @payloads: access conflicts list
|
||||
* @control_file: access control table
|
||||
*/
|
||||
struct zfcp_cfdc_data {
|
||||
u32 signature;
|
||||
u32 devno;
|
||||
u32 command;
|
||||
u32 fsf_status;
|
||||
u8 fsf_status_qual[FSF_STATUS_QUALIFIER_SIZE];
|
||||
u8 payloads[256];
|
||||
u8 control_file[0];
|
||||
};
|
||||
|
||||
static int zfcp_cfdc_copy_from_user(struct scatterlist *sg,
|
||||
void __user *user_buffer)
|
||||
{
|
||||
unsigned int length;
|
||||
unsigned int size = ZFCP_CFDC_MAX_SIZE;
|
||||
|
||||
while (size) {
|
||||
length = min((unsigned int)size, sg->length);
|
||||
if (copy_from_user(sg_virt(sg++), user_buffer, length))
|
||||
return -EFAULT;
|
||||
user_buffer += length;
|
||||
size -= length;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int zfcp_cfdc_copy_to_user(void __user *user_buffer,
|
||||
struct scatterlist *sg)
|
||||
{
|
||||
unsigned int length;
|
||||
unsigned int size = ZFCP_CFDC_MAX_SIZE;
|
||||
|
||||
while (size) {
|
||||
length = min((unsigned int) size, sg->length);
|
||||
if (copy_to_user(user_buffer, sg_virt(sg++), length))
|
||||
return -EFAULT;
|
||||
user_buffer += length;
|
||||
size -= length;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct zfcp_adapter *zfcp_cfdc_get_adapter(u32 devno)
|
||||
{
|
||||
struct zfcp_adapter *adapter = NULL, *cur_adapter;
|
||||
struct ccw_dev_id dev_id;
|
||||
|
||||
read_lock_irq(&zfcp_data.config_lock);
|
||||
list_for_each_entry(cur_adapter, &zfcp_data.adapter_list_head, list) {
|
||||
ccw_device_get_id(cur_adapter->ccw_device, &dev_id);
|
||||
if (dev_id.devno == devno) {
|
||||
adapter = cur_adapter;
|
||||
zfcp_adapter_get(adapter);
|
||||
break;
|
||||
}
|
||||
}
|
||||
read_unlock_irq(&zfcp_data.config_lock);
|
||||
return adapter;
|
||||
}
|
||||
|
||||
static int zfcp_cfdc_set_fsf(struct zfcp_fsf_cfdc *fsf_cfdc, int command)
|
||||
{
|
||||
switch (command) {
|
||||
case ZFCP_CFDC_CMND_DOWNLOAD_NORMAL:
|
||||
fsf_cfdc->command = FSF_QTCB_DOWNLOAD_CONTROL_FILE;
|
||||
fsf_cfdc->option = FSF_CFDC_OPTION_NORMAL_MODE;
|
||||
break;
|
||||
case ZFCP_CFDC_CMND_DOWNLOAD_FORCE:
|
||||
fsf_cfdc->command = FSF_QTCB_DOWNLOAD_CONTROL_FILE;
|
||||
fsf_cfdc->option = FSF_CFDC_OPTION_FORCE;
|
||||
break;
|
||||
case ZFCP_CFDC_CMND_FULL_ACCESS:
|
||||
fsf_cfdc->command = FSF_QTCB_DOWNLOAD_CONTROL_FILE;
|
||||
fsf_cfdc->option = FSF_CFDC_OPTION_FULL_ACCESS;
|
||||
break;
|
||||
case ZFCP_CFDC_CMND_RESTRICTED_ACCESS:
|
||||
fsf_cfdc->command = FSF_QTCB_DOWNLOAD_CONTROL_FILE;
|
||||
fsf_cfdc->option = FSF_CFDC_OPTION_RESTRICTED_ACCESS;
|
||||
break;
|
||||
case ZFCP_CFDC_CMND_UPLOAD:
|
||||
fsf_cfdc->command = FSF_QTCB_UPLOAD_CONTROL_FILE;
|
||||
fsf_cfdc->option = 0;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int zfcp_cfdc_sg_setup(int command, struct scatterlist *sg,
|
||||
u8 __user *control_file)
|
||||
{
|
||||
int retval;
|
||||
retval = zfcp_sg_setup_table(sg, ZFCP_CFDC_PAGES);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
sg[ZFCP_CFDC_PAGES - 1].length = ZFCP_CFDC_MAX_SIZE % PAGE_SIZE;
|
||||
|
||||
if (command & ZFCP_CFDC_WITH_CONTROL_FILE &&
|
||||
command & ZFCP_CFDC_DOWNLOAD) {
|
||||
retval = zfcp_cfdc_copy_from_user(sg, control_file);
|
||||
if (retval) {
|
||||
zfcp_sg_free_table(sg, ZFCP_CFDC_PAGES);
|
||||
return -EFAULT;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void zfcp_cfdc_req_to_sense(struct zfcp_cfdc_data *data,
|
||||
struct zfcp_fsf_req *req)
|
||||
{
|
||||
data->fsf_status = req->qtcb->header.fsf_status;
|
||||
memcpy(&data->fsf_status_qual, &req->qtcb->header.fsf_status_qual,
|
||||
sizeof(union fsf_status_qual));
|
||||
memcpy(&data->payloads, &req->qtcb->bottom.support.els,
|
||||
sizeof(req->qtcb->bottom.support.els));
|
||||
}
|
||||
|
||||
static long zfcp_cfdc_dev_ioctl(struct file *file, unsigned int command,
|
||||
unsigned long buffer)
|
||||
{
|
||||
struct zfcp_cfdc_data *data;
|
||||
struct zfcp_cfdc_data __user *data_user;
|
||||
struct zfcp_adapter *adapter;
|
||||
struct zfcp_fsf_req *req;
|
||||
struct zfcp_fsf_cfdc *fsf_cfdc;
|
||||
int retval;
|
||||
|
||||
if (command != ZFCP_CFDC_IOC)
|
||||
return -ENOTTY;
|
||||
|
||||
data_user = (void __user *) buffer;
|
||||
if (!data_user)
|
||||
return -EINVAL;
|
||||
|
||||
fsf_cfdc = kmalloc(sizeof(struct zfcp_fsf_cfdc), GFP_KERNEL);
|
||||
if (!fsf_cfdc)
|
||||
return -ENOMEM;
|
||||
|
||||
data = kmalloc(sizeof(struct zfcp_cfdc_data), GFP_KERNEL);
|
||||
if (!data) {
|
||||
retval = -ENOMEM;
|
||||
goto no_mem_sense;
|
||||
}
|
||||
|
||||
retval = copy_from_user(data, data_user, sizeof(*data));
|
||||
if (retval) {
|
||||
retval = -EFAULT;
|
||||
goto free_buffer;
|
||||
}
|
||||
|
||||
if (data->signature != 0xCFDCACDF) {
|
||||
retval = -EINVAL;
|
||||
goto free_buffer;
|
||||
}
|
||||
|
||||
retval = zfcp_cfdc_set_fsf(fsf_cfdc, data->command);
|
||||
|
||||
adapter = zfcp_cfdc_get_adapter(data->devno);
|
||||
if (!adapter) {
|
||||
retval = -ENXIO;
|
||||
goto free_buffer;
|
||||
}
|
||||
|
||||
retval = zfcp_cfdc_sg_setup(data->command, fsf_cfdc->sg,
|
||||
data_user->control_file);
|
||||
if (retval)
|
||||
goto adapter_put;
|
||||
req = zfcp_fsf_control_file(adapter, fsf_cfdc);
|
||||
if (IS_ERR(req)) {
|
||||
retval = PTR_ERR(req);
|
||||
goto free_sg;
|
||||
}
|
||||
|
||||
if (req->status & ZFCP_STATUS_FSFREQ_ERROR) {
|
||||
retval = -ENXIO;
|
||||
goto free_fsf;
|
||||
}
|
||||
|
||||
zfcp_cfdc_req_to_sense(data, req);
|
||||
retval = copy_to_user(data_user, data, sizeof(*data_user));
|
||||
if (retval) {
|
||||
retval = -EFAULT;
|
||||
goto free_fsf;
|
||||
}
|
||||
|
||||
if (data->command & ZFCP_CFDC_UPLOAD)
|
||||
retval = zfcp_cfdc_copy_to_user(&data_user->control_file,
|
||||
fsf_cfdc->sg);
|
||||
|
||||
free_fsf:
|
||||
zfcp_fsf_req_free(req);
|
||||
free_sg:
|
||||
zfcp_sg_free_table(fsf_cfdc->sg, ZFCP_CFDC_PAGES);
|
||||
adapter_put:
|
||||
zfcp_adapter_put(adapter);
|
||||
free_buffer:
|
||||
kfree(data);
|
||||
no_mem_sense:
|
||||
kfree(fsf_cfdc);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static const struct file_operations zfcp_cfdc_fops = {
|
||||
.unlocked_ioctl = zfcp_cfdc_dev_ioctl,
|
||||
#ifdef CONFIG_COMPAT
|
||||
.compat_ioctl = zfcp_cfdc_dev_ioctl
|
||||
#endif
|
||||
};
|
||||
|
||||
struct miscdevice zfcp_cfdc_misc = {
|
||||
.minor = MISC_DYNAMIC_MINOR,
|
||||
.name = "zfcp_cfdc",
|
||||
.fops = &zfcp_cfdc_fops,
|
||||
};
|
|
@ -1,22 +1,9 @@
|
|||
/*
|
||||
* This file is part of the zfcp device driver for
|
||||
* FCP adapters for IBM System z9 and zSeries.
|
||||
* zfcp device driver
|
||||
*
|
||||
* (C) Copyright IBM Corp. 2002, 2006
|
||||
* Debug traces for zfcp.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
* Copyright IBM Corporation 2002, 2008
|
||||
*/
|
||||
|
||||
#include <linux/ctype.h>
|
||||
|
@ -29,8 +16,6 @@ module_param(dbfsize, uint, 0400);
|
|||
MODULE_PARM_DESC(dbfsize,
|
||||
"number of pages for each debug feature area (default 4)");
|
||||
|
||||
#define ZFCP_LOG_AREA ZFCP_LOG_AREA_OTHER
|
||||
|
||||
static void zfcp_dbf_hexdump(debug_info_t *dbf, void *to, int to_len,
|
||||
int level, char *from, int from_len)
|
||||
{
|
||||
|
@ -186,8 +171,8 @@ void zfcp_hba_dbf_event_fsf_response(struct zfcp_fsf_req *fsf_req)
|
|||
fsf_status_qual, FSF_STATUS_QUALIFIER_SIZE);
|
||||
response->fsf_req_status = fsf_req->status;
|
||||
response->sbal_first = fsf_req->sbal_first;
|
||||
response->sbal_curr = fsf_req->sbal_curr;
|
||||
response->sbal_last = fsf_req->sbal_last;
|
||||
response->sbal_response = fsf_req->sbal_response;
|
||||
response->pool = fsf_req->pool != NULL;
|
||||
response->erp_action = (unsigned long)fsf_req->erp_action;
|
||||
|
||||
|
@ -268,7 +253,7 @@ void zfcp_hba_dbf_event_fsf_unsol(const char *tag, struct zfcp_adapter *adapter,
|
|||
strncpy(rec->tag, "stat", ZFCP_DBF_TAG_SIZE);
|
||||
strncpy(rec->tag2, tag, ZFCP_DBF_TAG_SIZE);
|
||||
|
||||
rec->u.status.failed = adapter->status_read_failed;
|
||||
rec->u.status.failed = atomic_read(&adapter->stat_miss);
|
||||
if (status_buffer != NULL) {
|
||||
rec->u.status.status_type = status_buffer->status_type;
|
||||
rec->u.status.status_subtype = status_buffer->status_subtype;
|
||||
|
@ -355,8 +340,8 @@ static void zfcp_hba_dbf_view_response(char **p,
|
|||
FSF_STATUS_QUALIFIER_SIZE, 0, FSF_STATUS_QUALIFIER_SIZE);
|
||||
zfcp_dbf_out(p, "fsf_req_status", "0x%08x", r->fsf_req_status);
|
||||
zfcp_dbf_out(p, "sbal_first", "0x%02x", r->sbal_first);
|
||||
zfcp_dbf_out(p, "sbal_curr", "0x%02x", r->sbal_curr);
|
||||
zfcp_dbf_out(p, "sbal_last", "0x%02x", r->sbal_last);
|
||||
zfcp_dbf_out(p, "sbal_response", "0x%02x", r->sbal_response);
|
||||
zfcp_dbf_out(p, "pool", "0x%02x", r->pool);
|
||||
|
||||
switch (r->fsf_command) {
|
||||
|
@ -515,13 +500,13 @@ static const char *zfcp_rec_dbf_ids[] = {
|
|||
[52] = "port boxed close unit",
|
||||
[53] = "port boxed fcp",
|
||||
[54] = "unit boxed fcp",
|
||||
[55] = "port access denied ct",
|
||||
[56] = "port access denied els",
|
||||
[57] = "port access denied open port",
|
||||
[58] = "port access denied close physical",
|
||||
[59] = "unit access denied open unit",
|
||||
[55] = "port access denied",
|
||||
[56] = "",
|
||||
[57] = "",
|
||||
[58] = "",
|
||||
[59] = "unit access denied",
|
||||
[60] = "shared unit access denied open unit",
|
||||
[61] = "unit access denied fcp",
|
||||
[61] = "",
|
||||
[62] = "request timeout",
|
||||
[63] = "adisc link test reject or timeout",
|
||||
[64] = "adisc link test d_id changed",
|
||||
|
@ -546,8 +531,8 @@ static const char *zfcp_rec_dbf_ids[] = {
|
|||
[80] = "exclusive read-only unit access unsupported",
|
||||
[81] = "shared read-write unit access unsupported",
|
||||
[82] = "incoming rscn",
|
||||
[83] = "incoming plogi",
|
||||
[84] = "incoming logo",
|
||||
[83] = "incoming wwpn",
|
||||
[84] = "",
|
||||
[85] = "online",
|
||||
[86] = "offline",
|
||||
[87] = "ccw device gone",
|
||||
|
@ -586,8 +571,8 @@ static const char *zfcp_rec_dbf_ids[] = {
|
|||
[120] = "unknown fsf command",
|
||||
[121] = "no recommendation for status qualifier",
|
||||
[122] = "status read physical port closed in error",
|
||||
[123] = "fc service class not supported ct",
|
||||
[124] = "fc service class not supported els",
|
||||
[123] = "fc service class not supported",
|
||||
[124] = "",
|
||||
[125] = "need newer zfcp",
|
||||
[126] = "need newer microcode",
|
||||
[127] = "arbitrated loop not supported",
|
||||
|
@ -595,7 +580,7 @@ static const char *zfcp_rec_dbf_ids[] = {
|
|||
[129] = "qtcb size mismatch",
|
||||
[130] = "unknown fsf status ecd",
|
||||
[131] = "fcp request too big",
|
||||
[132] = "fc service class not supported fcp",
|
||||
[132] = "",
|
||||
[133] = "data direction not valid fcp",
|
||||
[134] = "command length not valid fcp",
|
||||
[135] = "status read act update",
|
||||
|
@ -603,13 +588,18 @@ static const char *zfcp_rec_dbf_ids[] = {
|
|||
[137] = "hbaapi port open",
|
||||
[138] = "hbaapi unit open",
|
||||
[139] = "hbaapi unit shutdown",
|
||||
[140] = "qdio error",
|
||||
[140] = "qdio error outbound",
|
||||
[141] = "scsi host reset",
|
||||
[142] = "dismissing fsf request for recovery action",
|
||||
[143] = "recovery action timed out",
|
||||
[144] = "recovery action gone",
|
||||
[145] = "recovery action being processed",
|
||||
[146] = "recovery action ready for next step",
|
||||
[147] = "qdio error inbound",
|
||||
[148] = "nameserver needed for port scan",
|
||||
[149] = "port scan",
|
||||
[150] = "ptp attach",
|
||||
[151] = "port validation failed",
|
||||
};
|
||||
|
||||
static int zfcp_rec_dbf_view_format(debug_info_t *id, struct debug_view *view,
|
||||
|
@ -670,24 +660,20 @@ static struct debug_view zfcp_rec_dbf_view = {
|
|||
* zfcp_rec_dbf_event_thread - trace event related to recovery thread operation
|
||||
* @id2: identifier for event
|
||||
* @adapter: adapter
|
||||
* @lock: non-zero value indicates that erp_lock has not yet been acquired
|
||||
* This function assumes that the caller is holding erp_lock.
|
||||
*/
|
||||
void zfcp_rec_dbf_event_thread(u8 id2, struct zfcp_adapter *adapter, int lock)
|
||||
void zfcp_rec_dbf_event_thread(u8 id2, struct zfcp_adapter *adapter)
|
||||
{
|
||||
struct zfcp_rec_dbf_record *r = &adapter->rec_dbf_buf;
|
||||
unsigned long flags = 0;
|
||||
struct list_head *entry;
|
||||
unsigned ready = 0, running = 0, total;
|
||||
|
||||
if (lock)
|
||||
read_lock_irqsave(&adapter->erp_lock, flags);
|
||||
list_for_each(entry, &adapter->erp_ready_head)
|
||||
ready++;
|
||||
list_for_each(entry, &adapter->erp_running_head)
|
||||
running++;
|
||||
total = adapter->erp_total_count;
|
||||
if (lock)
|
||||
read_unlock_irqrestore(&adapter->erp_lock, flags);
|
||||
|
||||
spin_lock_irqsave(&adapter->rec_dbf_lock, flags);
|
||||
memset(r, 0, sizeof(*r));
|
||||
|
@ -696,10 +682,25 @@ void zfcp_rec_dbf_event_thread(u8 id2, struct zfcp_adapter *adapter, int lock)
|
|||
r->u.thread.total = total;
|
||||
r->u.thread.ready = ready;
|
||||
r->u.thread.running = running;
|
||||
debug_event(adapter->rec_dbf, 5, r, sizeof(*r));
|
||||
debug_event(adapter->rec_dbf, 6, r, sizeof(*r));
|
||||
spin_unlock_irqrestore(&adapter->rec_dbf_lock, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* zfcp_rec_dbf_event_thread - trace event related to recovery thread operation
|
||||
* @id2: identifier for event
|
||||
* @adapter: adapter
|
||||
* This function assumes that the caller does not hold erp_lock.
|
||||
*/
|
||||
void zfcp_rec_dbf_event_thread_lock(u8 id2, struct zfcp_adapter *adapter)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
read_lock_irqsave(&adapter->erp_lock, flags);
|
||||
zfcp_rec_dbf_event_thread(id2, adapter);
|
||||
read_unlock_irqrestore(&adapter->erp_lock, flags);
|
||||
}
|
||||
|
||||
static void zfcp_rec_dbf_event_target(u8 id2, void *ref,
|
||||
struct zfcp_adapter *adapter,
|
||||
atomic_t *status, atomic_t *erp_count,
|
||||
|
@ -823,7 +824,7 @@ void zfcp_rec_dbf_event_action(u8 id2, struct zfcp_erp_action *erp_action)
|
|||
r->u.action.status = erp_action->status;
|
||||
r->u.action.step = erp_action->step;
|
||||
r->u.action.fsf_req = (unsigned long)erp_action->fsf_req;
|
||||
debug_event(adapter->rec_dbf, 4, r, sizeof(*r));
|
||||
debug_event(adapter->rec_dbf, 5, r, sizeof(*r));
|
||||
spin_unlock_irqrestore(&adapter->rec_dbf_lock, flags);
|
||||
}
|
||||
|
||||
|
@ -960,7 +961,7 @@ void zfcp_san_dbf_event_incoming_els(struct zfcp_fsf_req *fsf_req)
|
|||
|
||||
zfcp_san_dbf_event_els("iels", 1, fsf_req, buf->d_id,
|
||||
fc_host_port_id(adapter->scsi_host),
|
||||
*(u8 *)buf->payload, (void *)buf->payload,
|
||||
buf->payload.data[0], (void *)buf->payload.data,
|
||||
length);
|
||||
}
|
||||
|
||||
|
@ -1064,8 +1065,7 @@ static void zfcp_scsi_dbf_event(const char *tag, const char *tag2, int level,
|
|||
if (fsf_req != NULL) {
|
||||
fcp_rsp = (struct fcp_rsp_iu *)
|
||||
&(fsf_req->qtcb->bottom.io.fcp_rsp);
|
||||
fcp_rsp_info =
|
||||
zfcp_get_fcp_rsp_info_ptr(fcp_rsp);
|
||||
fcp_rsp_info = (unsigned char *) &fcp_rsp[1];
|
||||
fcp_sns_info =
|
||||
zfcp_get_fcp_sns_info_ptr(fcp_rsp);
|
||||
|
||||
|
@ -1279,5 +1279,3 @@ void zfcp_adapter_debug_unregister(struct zfcp_adapter *adapter)
|
|||
adapter->hba_dbf = NULL;
|
||||
adapter->rec_dbf = NULL;
|
||||
}
|
||||
|
||||
#undef ZFCP_LOG_AREA
|
||||
|
|
|
@ -38,7 +38,7 @@ struct zfcp_rec_dbf_record_thread {
|
|||
u32 total;
|
||||
u32 ready;
|
||||
u32 running;
|
||||
} __attribute__ ((packed));
|
||||
};
|
||||
|
||||
struct zfcp_rec_dbf_record_target {
|
||||
u64 ref;
|
||||
|
@ -47,7 +47,7 @@ struct zfcp_rec_dbf_record_target {
|
|||
u64 wwpn;
|
||||
u64 fcp_lun;
|
||||
u32 erp_count;
|
||||
} __attribute__ ((packed));
|
||||
};
|
||||
|
||||
struct zfcp_rec_dbf_record_trigger {
|
||||
u8 want;
|
||||
|
@ -59,14 +59,14 @@ struct zfcp_rec_dbf_record_trigger {
|
|||
u64 action;
|
||||
u64 wwpn;
|
||||
u64 fcp_lun;
|
||||
} __attribute__ ((packed));
|
||||
};
|
||||
|
||||
struct zfcp_rec_dbf_record_action {
|
||||
u32 status;
|
||||
u32 step;
|
||||
u64 action;
|
||||
u64 fsf_req;
|
||||
} __attribute__ ((packed));
|
||||
};
|
||||
|
||||
struct zfcp_rec_dbf_record {
|
||||
u8 id;
|
||||
|
@ -77,7 +77,7 @@ struct zfcp_rec_dbf_record {
|
|||
struct zfcp_rec_dbf_record_target target;
|
||||
struct zfcp_rec_dbf_record_trigger trigger;
|
||||
} u;
|
||||
} __attribute__ ((packed));
|
||||
};
|
||||
|
||||
enum {
|
||||
ZFCP_REC_DBF_ID_ACTION,
|
||||
|
@ -97,8 +97,8 @@ struct zfcp_hba_dbf_record_response {
|
|||
u8 fsf_status_qual[FSF_STATUS_QUALIFIER_SIZE];
|
||||
u32 fsf_req_status;
|
||||
u8 sbal_first;
|
||||
u8 sbal_curr;
|
||||
u8 sbal_last;
|
||||
u8 sbal_response;
|
||||
u8 pool;
|
||||
u64 erp_action;
|
||||
union {
|
||||
|
|
|
@ -1,22 +1,9 @@
|
|||
/*
|
||||
* This file is part of the zfcp device driver for
|
||||
* FCP adapters for IBM System z9 and zSeries.
|
||||
* zfcp device driver
|
||||
*
|
||||
* (C) Copyright IBM Corp. 2002, 2006
|
||||
* Global definitions for the zfcp device driver.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
* Copyright IBM Corporation 2002, 2008
|
||||
*/
|
||||
|
||||
#ifndef ZFCP_DEF_H
|
||||
|
@ -26,7 +13,6 @@
|
|||
|
||||
#include <linux/init.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/major.h>
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/delay.h>
|
||||
|
@ -53,9 +39,6 @@
|
|||
|
||||
/********************* GENERAL DEFINES *********************************/
|
||||
|
||||
/* zfcp version number, it consists of major, minor, and patch-level number */
|
||||
#define ZFCP_VERSION "4.8.0"
|
||||
|
||||
/**
|
||||
* zfcp_sg_to_address - determine kernel address from struct scatterlist
|
||||
* @list: struct scatterlist
|
||||
|
@ -93,11 +76,6 @@ zfcp_address_to_sg(void *address, struct scatterlist *list, unsigned int size)
|
|||
#define ZFCP_DEVICE_MODEL 0x03
|
||||
#define ZFCP_DEVICE_MODEL_PRIV 0x04
|
||||
|
||||
/* allow as many chained SBALs as are supported by hardware */
|
||||
#define ZFCP_MAX_SBALS_PER_REQ FSF_MAX_SBALS_PER_REQ
|
||||
#define ZFCP_MAX_SBALS_PER_CT_REQ FSF_MAX_SBALS_PER_REQ
|
||||
#define ZFCP_MAX_SBALS_PER_ELS_REQ FSF_MAX_SBALS_PER_ELS_REQ
|
||||
|
||||
/* DMQ bug workaround: don't use last SBALE */
|
||||
#define ZFCP_MAX_SBALES_PER_SBAL (QDIO_MAX_ELEMENTS_PER_BUFFER - 1)
|
||||
|
||||
|
@ -106,42 +84,17 @@ zfcp_address_to_sg(void *address, struct scatterlist *list, unsigned int size)
|
|||
|
||||
/* max. number of (data buffer) SBALEs in largest SBAL chain */
|
||||
#define ZFCP_MAX_SBALES_PER_REQ \
|
||||
(ZFCP_MAX_SBALS_PER_REQ * ZFCP_MAX_SBALES_PER_SBAL - 2)
|
||||
(FSF_MAX_SBALS_PER_REQ * ZFCP_MAX_SBALES_PER_SBAL - 2)
|
||||
/* request ID + QTCB in SBALE 0 + 1 of first SBAL in chain */
|
||||
|
||||
#define ZFCP_MAX_SECTORS (ZFCP_MAX_SBALES_PER_REQ * 8)
|
||||
/* max. number of (data buffer) SBALEs in largest SBAL chain
|
||||
multiplied with number of sectors per 4k block */
|
||||
|
||||
/* FIXME(tune): free space should be one max. SBAL chain plus what? */
|
||||
#define ZFCP_QDIO_PCI_INTERVAL (QDIO_MAX_BUFFERS_PER_Q \
|
||||
- (ZFCP_MAX_SBALS_PER_REQ + 4))
|
||||
|
||||
#define ZFCP_SBAL_TIMEOUT (5*HZ)
|
||||
|
||||
#define ZFCP_TYPE2_RECOVERY_TIME 8 /* seconds */
|
||||
|
||||
/* queue polling (values in microseconds) */
|
||||
#define ZFCP_MAX_INPUT_THRESHOLD 5000 /* FIXME: tune */
|
||||
#define ZFCP_MAX_OUTPUT_THRESHOLD 1000 /* FIXME: tune */
|
||||
#define ZFCP_MIN_INPUT_THRESHOLD 1 /* ignored by QDIO layer */
|
||||
#define ZFCP_MIN_OUTPUT_THRESHOLD 1 /* ignored by QDIO layer */
|
||||
|
||||
#define QDIO_SCSI_QFMT 1 /* 1 for FSF */
|
||||
#define QBUFF_PER_PAGE (PAGE_SIZE / sizeof(struct qdio_buffer))
|
||||
|
||||
/********************* FSF SPECIFIC DEFINES *********************************/
|
||||
|
||||
#define ZFCP_ULP_INFO_VERSION 26
|
||||
#define ZFCP_QTCB_VERSION FSF_QTCB_CURRENT_VERSION
|
||||
/* ATTENTION: value must not be used by hardware */
|
||||
#define FSF_QTCB_UNSOLICITED_STATUS 0x6305
|
||||
#define ZFCP_STATUS_READ_FAILED_THRESHOLD 3
|
||||
#define ZFCP_STATUS_READS_RECOM FSF_STATUS_READS_RECOM
|
||||
|
||||
/* Do 1st retry in 1 second, then double the timeout for each following retry */
|
||||
#define ZFCP_EXCHANGE_CONFIG_DATA_FIRST_SLEEP 1
|
||||
#define ZFCP_EXCHANGE_CONFIG_DATA_RETRIES 7
|
||||
|
||||
/* timeout value for "default timer" for fsf requests */
|
||||
#define ZFCP_FSF_REQUEST_TIMEOUT (60*HZ)
|
||||
|
@ -153,17 +106,9 @@ typedef unsigned long long fcp_lun_t;
|
|||
/* data length field may be at variable position in FCP-2 FCP_CMND IU */
|
||||
typedef unsigned int fcp_dl_t;
|
||||
|
||||
#define ZFCP_FC_SERVICE_CLASS_DEFAULT FSF_CLASS_3
|
||||
|
||||
/* timeout for name-server lookup (in seconds) */
|
||||
#define ZFCP_NS_GID_PN_TIMEOUT 10
|
||||
|
||||
/* largest SCSI command we can process */
|
||||
/* FCP-2 (FCP_CMND IU) allows up to (255-3+16) */
|
||||
#define ZFCP_MAX_SCSI_CMND_LENGTH 255
|
||||
/* maximum number of commands in LUN queue (tagged queueing) */
|
||||
#define ZFCP_CMND_PER_LUN 32
|
||||
|
||||
/* task attribute values in FCP-2 FCP_CMND IU */
|
||||
#define SIMPLE_Q 0
|
||||
#define HEAD_OF_Q 1
|
||||
|
@ -224,9 +169,9 @@ struct fcp_rsp_iu {
|
|||
#define RSP_CODE_TASKMAN_FAILED 5
|
||||
|
||||
/* see fc-fs */
|
||||
#define LS_RSCN 0x61040000
|
||||
#define LS_LOGO 0x05000000
|
||||
#define LS_PLOGI 0x03000000
|
||||
#define LS_RSCN 0x61
|
||||
#define LS_LOGO 0x05
|
||||
#define LS_PLOGI 0x03
|
||||
|
||||
struct fcp_rscn_head {
|
||||
u8 command;
|
||||
|
@ -266,7 +211,6 @@ struct fcp_logo {
|
|||
* FC-FS stuff
|
||||
*/
|
||||
#define R_A_TOV 10 /* seconds */
|
||||
#define ZFCP_ELS_TIMEOUT (2 * R_A_TOV)
|
||||
|
||||
#define ZFCP_LS_RLS 0x0f
|
||||
#define ZFCP_LS_ADISC 0x52
|
||||
|
@ -311,7 +255,10 @@ struct zfcp_rc_entry {
|
|||
#define ZFCP_CT_DIRECTORY_SERVICE 0xFC
|
||||
#define ZFCP_CT_NAME_SERVER 0x02
|
||||
#define ZFCP_CT_SYNCHRONOUS 0x00
|
||||
#define ZFCP_CT_SCSI_FCP 0x08
|
||||
#define ZFCP_CT_UNABLE_TO_PERFORM_CMD 0x09
|
||||
#define ZFCP_CT_GID_PN 0x0121
|
||||
#define ZFCP_CT_GPN_FT 0x0172
|
||||
#define ZFCP_CT_MAX_SIZE 0x1020
|
||||
#define ZFCP_CT_ACCEPT 0x8002
|
||||
#define ZFCP_CT_REJECT 0x8001
|
||||
|
@ -321,107 +268,6 @@ struct zfcp_rc_entry {
|
|||
*/
|
||||
#define ZFCP_CT_TIMEOUT (3 * R_A_TOV)
|
||||
|
||||
/******************** LOGGING MACROS AND DEFINES *****************************/
|
||||
|
||||
/*
|
||||
* Logging may be applied on certain kinds of driver operations
|
||||
* independently. Additionally, different log-levels are supported for
|
||||
* each of these areas.
|
||||
*/
|
||||
|
||||
#define ZFCP_NAME "zfcp"
|
||||
|
||||
/* independent log areas */
|
||||
#define ZFCP_LOG_AREA_OTHER 0
|
||||
#define ZFCP_LOG_AREA_SCSI 1
|
||||
#define ZFCP_LOG_AREA_FSF 2
|
||||
#define ZFCP_LOG_AREA_CONFIG 3
|
||||
#define ZFCP_LOG_AREA_CIO 4
|
||||
#define ZFCP_LOG_AREA_QDIO 5
|
||||
#define ZFCP_LOG_AREA_ERP 6
|
||||
#define ZFCP_LOG_AREA_FC 7
|
||||
|
||||
/* log level values*/
|
||||
#define ZFCP_LOG_LEVEL_NORMAL 0
|
||||
#define ZFCP_LOG_LEVEL_INFO 1
|
||||
#define ZFCP_LOG_LEVEL_DEBUG 2
|
||||
#define ZFCP_LOG_LEVEL_TRACE 3
|
||||
|
||||
/*
|
||||
* this allows removal of logging code by the preprocessor
|
||||
* (the most detailed log level still to be compiled in is specified,
|
||||
* higher log levels are removed)
|
||||
*/
|
||||
#define ZFCP_LOG_LEVEL_LIMIT ZFCP_LOG_LEVEL_TRACE
|
||||
|
||||
/* get "loglevel" nibble assignment */
|
||||
#define ZFCP_GET_LOG_VALUE(zfcp_lognibble) \
|
||||
((atomic_read(&zfcp_data.loglevel) >> (zfcp_lognibble<<2)) & 0xF)
|
||||
|
||||
/* set "loglevel" nibble */
|
||||
#define ZFCP_SET_LOG_NIBBLE(value, zfcp_lognibble) \
|
||||
(value << (zfcp_lognibble << 2))
|
||||
|
||||
/* all log-level defaults are combined to generate initial log-level */
|
||||
#define ZFCP_LOG_LEVEL_DEFAULTS \
|
||||
(ZFCP_SET_LOG_NIBBLE(ZFCP_LOG_LEVEL_NORMAL, ZFCP_LOG_AREA_OTHER) | \
|
||||
ZFCP_SET_LOG_NIBBLE(ZFCP_LOG_LEVEL_NORMAL, ZFCP_LOG_AREA_SCSI) | \
|
||||
ZFCP_SET_LOG_NIBBLE(ZFCP_LOG_LEVEL_NORMAL, ZFCP_LOG_AREA_FSF) | \
|
||||
ZFCP_SET_LOG_NIBBLE(ZFCP_LOG_LEVEL_NORMAL, ZFCP_LOG_AREA_CONFIG) | \
|
||||
ZFCP_SET_LOG_NIBBLE(ZFCP_LOG_LEVEL_NORMAL, ZFCP_LOG_AREA_CIO) | \
|
||||
ZFCP_SET_LOG_NIBBLE(ZFCP_LOG_LEVEL_NORMAL, ZFCP_LOG_AREA_QDIO) | \
|
||||
ZFCP_SET_LOG_NIBBLE(ZFCP_LOG_LEVEL_NORMAL, ZFCP_LOG_AREA_ERP) | \
|
||||
ZFCP_SET_LOG_NIBBLE(ZFCP_LOG_LEVEL_NORMAL, ZFCP_LOG_AREA_FC))
|
||||
|
||||
/* check whether we have the right level for logging */
|
||||
#define ZFCP_LOG_CHECK(level) \
|
||||
((ZFCP_GET_LOG_VALUE(ZFCP_LOG_AREA)) >= level)
|
||||
|
||||
/* logging routine for zfcp */
|
||||
#define _ZFCP_LOG(fmt, args...) \
|
||||
printk(KERN_ERR ZFCP_NAME": %s(%d): " fmt, __func__, \
|
||||
__LINE__ , ##args)
|
||||
|
||||
#define ZFCP_LOG(level, fmt, args...) \
|
||||
do { \
|
||||
if (ZFCP_LOG_CHECK(level)) \
|
||||
_ZFCP_LOG(fmt, ##args); \
|
||||
} while (0)
|
||||
|
||||
#if ZFCP_LOG_LEVEL_LIMIT < ZFCP_LOG_LEVEL_NORMAL
|
||||
# define ZFCP_LOG_NORMAL(fmt, args...) do { } while (0)
|
||||
#else
|
||||
# define ZFCP_LOG_NORMAL(fmt, args...) \
|
||||
do { \
|
||||
if (ZFCP_LOG_CHECK(ZFCP_LOG_LEVEL_NORMAL)) \
|
||||
printk(KERN_ERR ZFCP_NAME": " fmt, ##args); \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
#if ZFCP_LOG_LEVEL_LIMIT < ZFCP_LOG_LEVEL_INFO
|
||||
# define ZFCP_LOG_INFO(fmt, args...) do { } while (0)
|
||||
#else
|
||||
# define ZFCP_LOG_INFO(fmt, args...) \
|
||||
do { \
|
||||
if (ZFCP_LOG_CHECK(ZFCP_LOG_LEVEL_INFO)) \
|
||||
printk(KERN_ERR ZFCP_NAME": " fmt, ##args); \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
#if ZFCP_LOG_LEVEL_LIMIT < ZFCP_LOG_LEVEL_DEBUG
|
||||
# define ZFCP_LOG_DEBUG(fmt, args...) do { } while (0)
|
||||
#else
|
||||
# define ZFCP_LOG_DEBUG(fmt, args...) \
|
||||
ZFCP_LOG(ZFCP_LOG_LEVEL_DEBUG, fmt , ##args)
|
||||
#endif
|
||||
|
||||
#if ZFCP_LOG_LEVEL_LIMIT < ZFCP_LOG_LEVEL_TRACE
|
||||
# define ZFCP_LOG_TRACE(fmt, args...) do { } while (0)
|
||||
#else
|
||||
# define ZFCP_LOG_TRACE(fmt, args...) \
|
||||
ZFCP_LOG(ZFCP_LOG_LEVEL_TRACE, fmt , ##args)
|
||||
#endif
|
||||
|
||||
/*************** ADAPTER/PORT/UNIT AND FSF_REQ STATUS FLAGS ******************/
|
||||
|
||||
/*
|
||||
|
@ -441,6 +287,7 @@ do { \
|
|||
#define ZFCP_STATUS_COMMON_ERP_INUSE 0x01000000
|
||||
#define ZFCP_STATUS_COMMON_ACCESS_DENIED 0x00800000
|
||||
#define ZFCP_STATUS_COMMON_ACCESS_BOXED 0x00400000
|
||||
#define ZFCP_STATUS_COMMON_NOESC 0x00200000
|
||||
|
||||
/* adapter status */
|
||||
#define ZFCP_STATUS_ADAPTER_QDIOUP 0x00000002
|
||||
|
@ -496,77 +343,6 @@ do { \
|
|||
#define ZFCP_STATUS_FSFREQ_RETRY 0x00000800
|
||||
#define ZFCP_STATUS_FSFREQ_DISMISSED 0x00001000
|
||||
|
||||
/*********************** ERROR RECOVERY PROCEDURE DEFINES ********************/
|
||||
|
||||
#define ZFCP_MAX_ERPS 3
|
||||
|
||||
#define ZFCP_ERP_FSFREQ_TIMEOUT (30 * HZ)
|
||||
#define ZFCP_ERP_MEMWAIT_TIMEOUT HZ
|
||||
|
||||
#define ZFCP_STATUS_ERP_TIMEDOUT 0x10000000
|
||||
#define ZFCP_STATUS_ERP_CLOSE_ONLY 0x01000000
|
||||
#define ZFCP_STATUS_ERP_DISMISSING 0x00100000
|
||||
#define ZFCP_STATUS_ERP_DISMISSED 0x00200000
|
||||
#define ZFCP_STATUS_ERP_LOWMEM 0x00400000
|
||||
|
||||
#define ZFCP_ERP_STEP_UNINITIALIZED 0x00000000
|
||||
#define ZFCP_ERP_STEP_FSF_XCONFIG 0x00000001
|
||||
#define ZFCP_ERP_STEP_PHYS_PORT_CLOSING 0x00000010
|
||||
#define ZFCP_ERP_STEP_PORT_CLOSING 0x00000100
|
||||
#define ZFCP_ERP_STEP_NAMESERVER_OPEN 0x00000200
|
||||
#define ZFCP_ERP_STEP_NAMESERVER_LOOKUP 0x00000400
|
||||
#define ZFCP_ERP_STEP_PORT_OPENING 0x00000800
|
||||
#define ZFCP_ERP_STEP_UNIT_CLOSING 0x00001000
|
||||
#define ZFCP_ERP_STEP_UNIT_OPENING 0x00002000
|
||||
|
||||
/* Ordered by escalation level (necessary for proper erp-code operation) */
|
||||
#define ZFCP_ERP_ACTION_REOPEN_ADAPTER 0x4
|
||||
#define ZFCP_ERP_ACTION_REOPEN_PORT_FORCED 0x3
|
||||
#define ZFCP_ERP_ACTION_REOPEN_PORT 0x2
|
||||
#define ZFCP_ERP_ACTION_REOPEN_UNIT 0x1
|
||||
|
||||
#define ZFCP_ERP_ACTION_RUNNING 0x1
|
||||
#define ZFCP_ERP_ACTION_READY 0x2
|
||||
|
||||
#define ZFCP_ERP_SUCCEEDED 0x0
|
||||
#define ZFCP_ERP_FAILED 0x1
|
||||
#define ZFCP_ERP_CONTINUES 0x2
|
||||
#define ZFCP_ERP_EXIT 0x3
|
||||
#define ZFCP_ERP_DISMISSED 0x4
|
||||
#define ZFCP_ERP_NOMEM 0x5
|
||||
|
||||
|
||||
/******************** CFDC SPECIFIC STUFF *****************************/
|
||||
|
||||
/* Firewall data channel sense data record */
|
||||
struct zfcp_cfdc_sense_data {
|
||||
u32 signature; /* Request signature */
|
||||
u32 devno; /* FCP adapter device number */
|
||||
u32 command; /* Command code */
|
||||
u32 fsf_status; /* FSF request status and status qualifier */
|
||||
u8 fsf_status_qual[FSF_STATUS_QUALIFIER_SIZE];
|
||||
u8 payloads[256]; /* Access conflicts list */
|
||||
u8 control_file[0]; /* Access control table */
|
||||
};
|
||||
|
||||
#define ZFCP_CFDC_SIGNATURE 0xCFDCACDF
|
||||
|
||||
#define ZFCP_CFDC_CMND_DOWNLOAD_NORMAL 0x00010001
|
||||
#define ZFCP_CFDC_CMND_DOWNLOAD_FORCE 0x00010101
|
||||
#define ZFCP_CFDC_CMND_FULL_ACCESS 0x00000201
|
||||
#define ZFCP_CFDC_CMND_RESTRICTED_ACCESS 0x00000401
|
||||
#define ZFCP_CFDC_CMND_UPLOAD 0x00010002
|
||||
|
||||
#define ZFCP_CFDC_DOWNLOAD 0x00000001
|
||||
#define ZFCP_CFDC_UPLOAD 0x00000002
|
||||
#define ZFCP_CFDC_WITH_CONTROL_FILE 0x00010000
|
||||
|
||||
#define ZFCP_CFDC_DEV_NAME "zfcp_cfdc"
|
||||
#define ZFCP_CFDC_DEV_MAJOR MISC_MAJOR
|
||||
#define ZFCP_CFDC_DEV_MINOR MISC_DYNAMIC_MINOR
|
||||
|
||||
#define ZFCP_CFDC_MAX_CONTROL_FILE_SIZE 127 * 1024
|
||||
|
||||
/************************* STRUCTURE DEFINITIONS *****************************/
|
||||
|
||||
struct zfcp_fsf_req;
|
||||
|
@ -623,7 +399,6 @@ typedef void (*zfcp_send_ct_handler_t)(unsigned long);
|
|||
* @resp_count: number of elements in response scatter-gather list
|
||||
* @handler: handler function (called for response to the request)
|
||||
* @handler_data: data passed to handler function
|
||||
* @pool: pointer to memory pool for ct request structure
|
||||
* @timeout: FSF timeout for this request
|
||||
* @completion: completion for synchronization purposes
|
||||
* @status: used to pass error status to calling function
|
||||
|
@ -636,7 +411,6 @@ struct zfcp_send_ct {
|
|||
unsigned int resp_count;
|
||||
zfcp_send_ct_handler_t handler;
|
||||
unsigned long handler_data;
|
||||
mempool_t *pool;
|
||||
int timeout;
|
||||
struct completion *completion;
|
||||
int status;
|
||||
|
@ -685,13 +459,13 @@ struct zfcp_send_els {
|
|||
};
|
||||
|
||||
struct zfcp_qdio_queue {
|
||||
struct qdio_buffer *buffer[QDIO_MAX_BUFFERS_PER_Q]; /* SBALs */
|
||||
u8 free_index; /* index of next free bfr
|
||||
struct qdio_buffer *sbal[QDIO_MAX_BUFFERS_PER_Q]; /* SBALs */
|
||||
u8 first; /* index of next free bfr
|
||||
in queue (free_count>0) */
|
||||
atomic_t free_count; /* number of free buffers
|
||||
atomic_t count; /* number of free buffers
|
||||
in queue */
|
||||
rwlock_t queue_lock; /* lock for operations on queue */
|
||||
int distance_from_int; /* SBALs used since PCI indication
|
||||
spinlock_t lock; /* lock for operations on queue */
|
||||
int pci_batch; /* SBALs since PCI indication
|
||||
was last set */
|
||||
};
|
||||
|
||||
|
@ -708,6 +482,24 @@ struct zfcp_erp_action {
|
|||
struct timer_list timer;
|
||||
};
|
||||
|
||||
struct fsf_latency_record {
|
||||
u32 min;
|
||||
u32 max;
|
||||
u64 sum;
|
||||
};
|
||||
|
||||
struct latency_cont {
|
||||
struct fsf_latency_record channel;
|
||||
struct fsf_latency_record fabric;
|
||||
u64 counter;
|
||||
};
|
||||
|
||||
struct zfcp_latencies {
|
||||
struct latency_cont read;
|
||||
struct latency_cont write;
|
||||
struct latency_cont cmd;
|
||||
spinlock_t lock;
|
||||
};
|
||||
|
||||
struct zfcp_adapter {
|
||||
struct list_head list; /* list of adapters */
|
||||
|
@ -723,24 +515,25 @@ struct zfcp_adapter {
|
|||
u32 adapter_features; /* FCP channel features */
|
||||
u32 connection_features; /* host connection features */
|
||||
u32 hardware_version; /* of FCP channel */
|
||||
u16 timer_ticks; /* time int for a tick */
|
||||
struct Scsi_Host *scsi_host; /* Pointer to mid-layer */
|
||||
struct list_head port_list_head; /* remote port list */
|
||||
struct list_head port_remove_lh; /* head of ports to be
|
||||
removed */
|
||||
u32 ports; /* number of remote ports */
|
||||
atomic_t reqs_active; /* # active FSF reqs */
|
||||
unsigned long req_no; /* unique FSF req number */
|
||||
struct list_head *req_list; /* list of pending reqs */
|
||||
spinlock_t req_list_lock; /* request list lock */
|
||||
struct zfcp_qdio_queue request_queue; /* request queue */
|
||||
struct zfcp_qdio_queue req_q; /* request queue */
|
||||
u32 fsf_req_seq_no; /* FSF cmnd seq number */
|
||||
wait_queue_head_t request_wq; /* can be used to wait for
|
||||
more avaliable SBALs */
|
||||
struct zfcp_qdio_queue response_queue; /* response queue */
|
||||
struct zfcp_qdio_queue resp_q; /* response queue */
|
||||
rwlock_t abort_lock; /* Protects against SCSI
|
||||
stack abort/command
|
||||
completion races */
|
||||
u16 status_read_failed; /* # failed status reads */
|
||||
atomic_t stat_miss; /* # missing status reads*/
|
||||
struct work_struct stat_work;
|
||||
atomic_t status; /* status of this adapter */
|
||||
struct list_head erp_ready_head; /* error recovery for this
|
||||
adapter/devices */
|
||||
|
@ -774,13 +567,9 @@ 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;
|
||||
};
|
||||
|
||||
/*
|
||||
* the struct device sysfs_device must be at the beginning of this structure.
|
||||
* pointer to struct device is used to free port structure in release function
|
||||
* of the device. don't change!
|
||||
*/
|
||||
struct zfcp_port {
|
||||
struct device sysfs_device; /* sysfs device */
|
||||
struct fc_rport *rport; /* rport of fc transport class */
|
||||
|
@ -804,10 +593,6 @@ struct zfcp_port {
|
|||
u32 supported_classes;
|
||||
};
|
||||
|
||||
/* the struct device sysfs_device must be at the beginning of this structure.
|
||||
* pointer to struct device is used to free unit structure in release function
|
||||
* of the device. don't change!
|
||||
*/
|
||||
struct zfcp_unit {
|
||||
struct device sysfs_device; /* sysfs device */
|
||||
struct list_head list; /* list of logical units */
|
||||
|
@ -822,6 +607,7 @@ struct zfcp_unit {
|
|||
struct scsi_device *device; /* scsi device struct pointer */
|
||||
struct zfcp_erp_action erp_action; /* pending error recovery */
|
||||
atomic_t erp_counter;
|
||||
struct zfcp_latencies latencies;
|
||||
};
|
||||
|
||||
/* FSF request */
|
||||
|
@ -831,19 +617,19 @@ struct zfcp_fsf_req {
|
|||
struct zfcp_adapter *adapter; /* adapter request belongs to */
|
||||
u8 sbal_number; /* nr of SBALs free for use */
|
||||
u8 sbal_first; /* first SBAL for this request */
|
||||
u8 sbal_last; /* last possible SBAL for
|
||||
u8 sbal_last; /* last SBAL for this request */
|
||||
u8 sbal_limit; /* last possible SBAL for
|
||||
this reuest */
|
||||
u8 sbal_curr; /* current SBAL during creation
|
||||
of request */
|
||||
u8 sbale_curr; /* current SBALE during creation
|
||||
of request */
|
||||
u8 sbal_response; /* SBAL used in interrupt */
|
||||
wait_queue_head_t completion_wq; /* can be used by a routine
|
||||
to wait for completion */
|
||||
volatile u32 status; /* status of this request */
|
||||
u32 fsf_command; /* FSF Command copy */
|
||||
struct fsf_qtcb *qtcb; /* address of associated QTCB */
|
||||
u32 seq_no; /* Sequence number of request */
|
||||
unsigned long data; /* private data of request */
|
||||
void *data; /* private data of request */
|
||||
struct timer_list timer; /* used for erp or scsi er */
|
||||
struct zfcp_erp_action *erp_action; /* used if this request is
|
||||
issued on behalf of erp */
|
||||
|
@ -851,10 +637,9 @@ struct zfcp_fsf_req {
|
|||
from emergency pool */
|
||||
unsigned long long issued; /* request sent time (STCK) */
|
||||
struct zfcp_unit *unit;
|
||||
void (*handler)(struct zfcp_fsf_req *);
|
||||
};
|
||||
|
||||
typedef void zfcp_fsf_req_handler_t(struct zfcp_fsf_req*);
|
||||
|
||||
/* driver data */
|
||||
struct zfcp_data {
|
||||
struct scsi_host_template scsi_host_template;
|
||||
|
@ -873,29 +658,11 @@ struct zfcp_data {
|
|||
char init_busid[BUS_ID_SIZE];
|
||||
wwn_t init_wwpn;
|
||||
fcp_lun_t init_fcp_lun;
|
||||
char *driver_version;
|
||||
struct kmem_cache *fsf_req_qtcb_cache;
|
||||
struct kmem_cache *sr_buffer_cache;
|
||||
struct kmem_cache *gid_pn_cache;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct zfcp_sg_list - struct describing a scatter-gather list
|
||||
* @sg: pointer to array of (struct scatterlist)
|
||||
* @count: number of elements in scatter-gather list
|
||||
*/
|
||||
struct zfcp_sg_list {
|
||||
struct scatterlist *sg;
|
||||
unsigned int count;
|
||||
};
|
||||
|
||||
/* number of elements for various memory pools */
|
||||
#define ZFCP_POOL_FSF_REQ_ERP_NR 1
|
||||
#define ZFCP_POOL_FSF_REQ_SCSI_NR 1
|
||||
#define ZFCP_POOL_FSF_REQ_ABORT_NR 1
|
||||
#define ZFCP_POOL_STATUS_READ_NR ZFCP_STATUS_READS_RECOM
|
||||
#define ZFCP_POOL_DATA_GID_PN_NR 1
|
||||
|
||||
/* struct used by memory pools for fsf_requests */
|
||||
struct zfcp_fsf_req_qtcb {
|
||||
struct zfcp_fsf_req fsf_req;
|
||||
|
@ -905,7 +672,6 @@ struct zfcp_fsf_req_qtcb {
|
|||
/********************** ZFCP SPECIFIC DEFINES ********************************/
|
||||
|
||||
#define ZFCP_REQ_AUTO_CLEANUP 0x00000002
|
||||
#define ZFCP_WAIT_FOR_SBAL 0x00000004
|
||||
#define ZFCP_REQ_NO_QTCB 0x00000008
|
||||
|
||||
#define ZFCP_SET 0x00000100
|
||||
|
@ -916,12 +682,6 @@ struct zfcp_fsf_req_qtcb {
|
|||
((atomic_read(target) & mask) == mask)
|
||||
#endif
|
||||
|
||||
extern void _zfcp_hex_dump(char *, int);
|
||||
#define ZFCP_HEX_DUMP(level, addr, count) \
|
||||
if (ZFCP_LOG_CHECK(level)) { \
|
||||
_zfcp_hex_dump(addr, count); \
|
||||
}
|
||||
|
||||
#define zfcp_get_busid_by_adapter(adapter) (adapter->ccw_device->dev.bus_id)
|
||||
#define zfcp_get_busid_by_port(port) (zfcp_get_busid_by_adapter(port->adapter))
|
||||
#define zfcp_get_busid_by_unit(unit) (zfcp_get_busid_by_port(unit->port))
|
||||
|
@ -934,15 +694,6 @@ static inline int zfcp_reqlist_hash(unsigned long req_id)
|
|||
return req_id % REQUEST_LIST_SIZE;
|
||||
}
|
||||
|
||||
static inline void zfcp_reqlist_add(struct zfcp_adapter *adapter,
|
||||
struct zfcp_fsf_req *fsf_req)
|
||||
{
|
||||
unsigned int idx;
|
||||
|
||||
idx = zfcp_reqlist_hash(fsf_req->req_id);
|
||||
list_add_tail(&fsf_req->list, &adapter->req_list[idx]);
|
||||
}
|
||||
|
||||
static inline void zfcp_reqlist_remove(struct zfcp_adapter *adapter,
|
||||
struct zfcp_fsf_req *fsf_req)
|
||||
{
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,22 +1,9 @@
|
|||
/*
|
||||
* This file is part of the zfcp device driver for
|
||||
* FCP adapters for IBM System z9 and zSeries.
|
||||
* zfcp device driver
|
||||
*
|
||||
* (C) Copyright IBM Corp. 2002, 2006
|
||||
* External function declarations.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
* Copyright IBM Corporation 2002, 2008
|
||||
*/
|
||||
|
||||
#ifndef ZFCP_EXT_H
|
||||
|
@ -24,172 +11,51 @@
|
|||
|
||||
#include "zfcp_def.h"
|
||||
|
||||
extern struct zfcp_data zfcp_data;
|
||||
|
||||
/******************************** SYSFS *************************************/
|
||||
extern struct attribute_group *zfcp_driver_attr_groups[];
|
||||
extern int zfcp_sysfs_adapter_create_files(struct device *);
|
||||
extern void zfcp_sysfs_adapter_remove_files(struct device *);
|
||||
extern int zfcp_sysfs_port_create_files(struct device *, u32);
|
||||
extern void zfcp_sysfs_port_remove_files(struct device *, u32);
|
||||
extern int zfcp_sysfs_unit_create_files(struct device *);
|
||||
extern void zfcp_sysfs_unit_remove_files(struct device *);
|
||||
extern void zfcp_sysfs_port_release(struct device *);
|
||||
extern void zfcp_sysfs_unit_release(struct device *);
|
||||
|
||||
/**************************** CONFIGURATION *********************************/
|
||||
extern struct zfcp_unit *zfcp_get_unit_by_lun(struct zfcp_port *, fcp_lun_t);
|
||||
extern struct zfcp_port *zfcp_get_port_by_wwpn(struct zfcp_adapter *, wwn_t);
|
||||
extern struct zfcp_port *zfcp_get_port_by_did(struct zfcp_adapter *, u32);
|
||||
struct zfcp_adapter *zfcp_get_adapter_by_busid(char *);
|
||||
extern struct zfcp_adapter *zfcp_adapter_enqueue(struct ccw_device *);
|
||||
extern int zfcp_adapter_debug_register(struct zfcp_adapter *);
|
||||
extern void zfcp_adapter_dequeue(struct zfcp_adapter *);
|
||||
extern void zfcp_adapter_debug_unregister(struct zfcp_adapter *);
|
||||
extern struct zfcp_port *zfcp_port_enqueue(struct zfcp_adapter *, wwn_t,
|
||||
u32, u32);
|
||||
extern void zfcp_port_dequeue(struct zfcp_port *);
|
||||
/* zfcp_aux.c */
|
||||
extern struct zfcp_unit *zfcp_get_unit_by_lun(struct zfcp_port *,
|
||||
fcp_lun_t);
|
||||
extern struct zfcp_port *zfcp_get_port_by_wwpn(struct zfcp_adapter *,
|
||||
wwn_t);
|
||||
extern int zfcp_adapter_enqueue(struct ccw_device *);
|
||||
extern void zfcp_adapter_dequeue(struct zfcp_adapter *);
|
||||
extern struct zfcp_port *zfcp_port_enqueue(struct zfcp_adapter *, wwn_t, u32,
|
||||
u32);
|
||||
extern void zfcp_port_dequeue(struct zfcp_port *);
|
||||
extern struct zfcp_unit *zfcp_unit_enqueue(struct zfcp_port *, fcp_lun_t);
|
||||
extern void zfcp_unit_dequeue(struct zfcp_unit *);
|
||||
extern void zfcp_unit_dequeue(struct zfcp_unit *);
|
||||
extern int zfcp_reqlist_isempty(struct zfcp_adapter *);
|
||||
extern void zfcp_sg_free_table(struct scatterlist *, int);
|
||||
extern int zfcp_sg_setup_table(struct scatterlist *, int);
|
||||
|
||||
/******************************* S/390 IO ************************************/
|
||||
extern int zfcp_ccw_register(void);
|
||||
/* zfcp_ccw.c */
|
||||
extern int zfcp_ccw_register(void);
|
||||
|
||||
extern void zfcp_qdio_zero_sbals(struct qdio_buffer **, int, int);
|
||||
extern int zfcp_qdio_allocate(struct zfcp_adapter *);
|
||||
extern int zfcp_qdio_allocate_queues(struct zfcp_adapter *);
|
||||
extern void zfcp_qdio_free_queues(struct zfcp_adapter *);
|
||||
extern int zfcp_qdio_determine_pci(struct zfcp_qdio_queue *,
|
||||
struct zfcp_fsf_req *);
|
||||
/* zfcp_cfdc.c */
|
||||
extern struct miscdevice zfcp_cfdc_misc;
|
||||
|
||||
extern volatile struct qdio_buffer_element *zfcp_qdio_sbale_req
|
||||
(struct zfcp_fsf_req *, int, int);
|
||||
extern volatile struct qdio_buffer_element *zfcp_qdio_sbale_curr
|
||||
(struct zfcp_fsf_req *);
|
||||
extern int zfcp_qdio_sbals_from_sg
|
||||
(struct zfcp_fsf_req *, unsigned long, struct scatterlist *, int, int);
|
||||
extern int zfcp_qdio_sbals_from_scsicmnd
|
||||
(struct zfcp_fsf_req *, unsigned long, struct scsi_cmnd *);
|
||||
|
||||
|
||||
/******************************** FSF ****************************************/
|
||||
extern int zfcp_fsf_open_port(struct zfcp_erp_action *);
|
||||
extern int zfcp_fsf_close_port(struct zfcp_erp_action *);
|
||||
extern int zfcp_fsf_close_physical_port(struct zfcp_erp_action *);
|
||||
|
||||
extern int zfcp_fsf_open_unit(struct zfcp_erp_action *);
|
||||
extern int zfcp_fsf_close_unit(struct zfcp_erp_action *);
|
||||
|
||||
extern int zfcp_fsf_exchange_config_data(struct zfcp_erp_action *);
|
||||
extern int zfcp_fsf_exchange_config_data_sync(struct zfcp_adapter *,
|
||||
struct fsf_qtcb_bottom_config *);
|
||||
extern int zfcp_fsf_exchange_port_data(struct zfcp_erp_action *);
|
||||
extern int zfcp_fsf_exchange_port_data_sync(struct zfcp_adapter *,
|
||||
struct fsf_qtcb_bottom_port *);
|
||||
extern int zfcp_fsf_control_file(struct zfcp_adapter *, struct zfcp_fsf_req **,
|
||||
u32, u32, struct zfcp_sg_list *);
|
||||
extern void zfcp_fsf_start_timer(struct zfcp_fsf_req *, unsigned long);
|
||||
extern void zfcp_erp_start_timer(struct zfcp_fsf_req *);
|
||||
extern void zfcp_fsf_req_dismiss_all(struct zfcp_adapter *);
|
||||
extern int zfcp_fsf_status_read(struct zfcp_adapter *, int);
|
||||
extern int zfcp_fsf_req_create(struct zfcp_adapter *, u32, int, mempool_t *,
|
||||
unsigned long *, struct zfcp_fsf_req **);
|
||||
extern int zfcp_fsf_send_ct(struct zfcp_send_ct *, mempool_t *,
|
||||
struct zfcp_erp_action *);
|
||||
extern int zfcp_fsf_send_els(struct zfcp_send_els *);
|
||||
extern int zfcp_fsf_send_fcp_command_task(struct zfcp_adapter *,
|
||||
struct zfcp_unit *,
|
||||
struct scsi_cmnd *, int, int);
|
||||
extern int zfcp_fsf_req_complete(struct zfcp_fsf_req *);
|
||||
extern void zfcp_fsf_incoming_els(struct zfcp_fsf_req *);
|
||||
extern void zfcp_fsf_req_free(struct zfcp_fsf_req *);
|
||||
extern struct zfcp_fsf_req *zfcp_fsf_send_fcp_command_task_management(
|
||||
struct zfcp_adapter *, struct zfcp_unit *, u8, int);
|
||||
extern struct zfcp_fsf_req *zfcp_fsf_abort_fcp_command(
|
||||
unsigned long, struct zfcp_adapter *, struct zfcp_unit *, int);
|
||||
|
||||
/******************************* FC/FCP **************************************/
|
||||
extern int zfcp_nameserver_enqueue(struct zfcp_adapter *);
|
||||
extern int zfcp_ns_gid_pn_request(struct zfcp_erp_action *);
|
||||
extern int zfcp_check_ct_response(struct ct_hdr *);
|
||||
extern int zfcp_handle_els_rjt(u32, struct zfcp_ls_rjt_par *);
|
||||
extern void zfcp_plogi_evaluate(struct zfcp_port *, struct fsf_plogi *);
|
||||
|
||||
/******************************* SCSI ****************************************/
|
||||
extern int zfcp_adapter_scsi_register(struct zfcp_adapter *);
|
||||
extern void zfcp_adapter_scsi_unregister(struct zfcp_adapter *);
|
||||
extern void zfcp_set_fcp_dl(struct fcp_cmnd_iu *, fcp_dl_t);
|
||||
extern char *zfcp_get_fcp_rsp_info_ptr(struct fcp_rsp_iu *);
|
||||
extern void set_host_byte(int *, char);
|
||||
extern void set_driver_byte(int *, char);
|
||||
extern char *zfcp_get_fcp_sns_info_ptr(struct fcp_rsp_iu *);
|
||||
extern fcp_dl_t zfcp_get_fcp_dl(struct fcp_cmnd_iu *);
|
||||
|
||||
extern int zfcp_scsi_command_async(struct zfcp_adapter *,struct zfcp_unit *,
|
||||
struct scsi_cmnd *, int);
|
||||
extern int zfcp_scsi_command_sync(struct zfcp_unit *, struct scsi_cmnd *, int);
|
||||
extern struct fc_function_template zfcp_transport_functions;
|
||||
|
||||
/******************************** ERP ****************************************/
|
||||
extern void zfcp_erp_modify_adapter_status(struct zfcp_adapter *, u8, void *,
|
||||
u32, int);
|
||||
extern int zfcp_erp_adapter_reopen(struct zfcp_adapter *, int, u8, void *);
|
||||
extern int zfcp_erp_adapter_shutdown(struct zfcp_adapter *, int, u8, void *);
|
||||
extern void zfcp_erp_adapter_failed(struct zfcp_adapter *, u8, void *);
|
||||
|
||||
extern void zfcp_erp_modify_port_status(struct zfcp_port *, u8, void *, u32,
|
||||
int);
|
||||
extern int zfcp_erp_port_reopen(struct zfcp_port *, int, u8, void *);
|
||||
extern int zfcp_erp_port_shutdown(struct zfcp_port *, int, u8, void *);
|
||||
extern int zfcp_erp_port_forced_reopen(struct zfcp_port *, int, u8, void *);
|
||||
extern void zfcp_erp_port_failed(struct zfcp_port *, u8, void *);
|
||||
extern int zfcp_erp_port_reopen_all(struct zfcp_adapter *, int, u8, void *);
|
||||
|
||||
extern void zfcp_erp_modify_unit_status(struct zfcp_unit *, u8, void *, u32,
|
||||
int);
|
||||
extern int zfcp_erp_unit_reopen(struct zfcp_unit *, int, u8, void *);
|
||||
extern int zfcp_erp_unit_shutdown(struct zfcp_unit *, int, u8, void *);
|
||||
extern void zfcp_erp_unit_failed(struct zfcp_unit *, u8, void *);
|
||||
|
||||
extern int zfcp_erp_thread_setup(struct zfcp_adapter *);
|
||||
extern int zfcp_erp_thread_kill(struct zfcp_adapter *);
|
||||
extern int zfcp_erp_wait(struct zfcp_adapter *);
|
||||
extern void zfcp_erp_async_handler(struct zfcp_erp_action *, unsigned long);
|
||||
|
||||
extern int zfcp_test_link(struct zfcp_port *);
|
||||
|
||||
extern void zfcp_erp_port_boxed(struct zfcp_port *, u8 id, void *ref);
|
||||
extern void zfcp_erp_unit_boxed(struct zfcp_unit *, u8 id, void *ref);
|
||||
extern void zfcp_erp_port_access_denied(struct zfcp_port *, u8 id, void *ref);
|
||||
extern void zfcp_erp_unit_access_denied(struct zfcp_unit *, u8 id, void *ref);
|
||||
extern void zfcp_erp_adapter_access_changed(struct zfcp_adapter *, u8, void *);
|
||||
extern void zfcp_erp_port_access_changed(struct zfcp_port *, u8, void *);
|
||||
extern void zfcp_erp_unit_access_changed(struct zfcp_unit *, u8, void *);
|
||||
|
||||
/******************************** AUX ****************************************/
|
||||
extern void zfcp_rec_dbf_event_thread(u8 id, struct zfcp_adapter *adapter,
|
||||
int lock);
|
||||
extern void zfcp_rec_dbf_event_adapter(u8 id, void *ref, struct zfcp_adapter *);
|
||||
extern void zfcp_rec_dbf_event_port(u8 id, void *ref, struct zfcp_port *port);
|
||||
extern void zfcp_rec_dbf_event_unit(u8 id, void *ref, struct zfcp_unit *unit);
|
||||
extern void zfcp_rec_dbf_event_trigger(u8 id, void *ref, u8 want, u8 need,
|
||||
void *action, struct zfcp_adapter *,
|
||||
/* zfcp_dbf.c */
|
||||
extern int zfcp_adapter_debug_register(struct zfcp_adapter *);
|
||||
extern void zfcp_adapter_debug_unregister(struct zfcp_adapter *);
|
||||
extern void zfcp_rec_dbf_event_thread(u8, struct zfcp_adapter *);
|
||||
extern void zfcp_rec_dbf_event_thread_lock(u8, struct zfcp_adapter *);
|
||||
extern void zfcp_rec_dbf_event_adapter(u8, void *, struct zfcp_adapter *);
|
||||
extern void zfcp_rec_dbf_event_port(u8, void *, struct zfcp_port *);
|
||||
extern void zfcp_rec_dbf_event_unit(u8, void *, struct zfcp_unit *);
|
||||
extern void zfcp_rec_dbf_event_trigger(u8, void *, u8, u8, void *,
|
||||
struct zfcp_adapter *,
|
||||
struct zfcp_port *, struct zfcp_unit *);
|
||||
extern void zfcp_rec_dbf_event_action(u8 id, struct zfcp_erp_action *);
|
||||
|
||||
extern void zfcp_rec_dbf_event_action(u8, struct zfcp_erp_action *);
|
||||
extern void zfcp_hba_dbf_event_fsf_response(struct zfcp_fsf_req *);
|
||||
extern void zfcp_hba_dbf_event_fsf_unsol(const char *, struct zfcp_adapter *,
|
||||
struct fsf_status_read_buffer *);
|
||||
extern void zfcp_hba_dbf_event_qdio(struct zfcp_adapter *,
|
||||
unsigned int, unsigned int, unsigned int,
|
||||
int, int);
|
||||
|
||||
extern void zfcp_san_dbf_event_ct_request(struct zfcp_fsf_req *);
|
||||
extern void zfcp_san_dbf_event_ct_response(struct zfcp_fsf_req *);
|
||||
extern void zfcp_san_dbf_event_els_request(struct zfcp_fsf_req *);
|
||||
extern void zfcp_san_dbf_event_els_response(struct zfcp_fsf_req *);
|
||||
extern void zfcp_san_dbf_event_incoming_els(struct zfcp_fsf_req *);
|
||||
|
||||
extern void zfcp_scsi_dbf_event_result(const char *, int, struct zfcp_adapter *,
|
||||
struct scsi_cmnd *,
|
||||
struct zfcp_fsf_req *);
|
||||
|
@ -198,6 +64,101 @@ extern void zfcp_scsi_dbf_event_abort(const char *, struct zfcp_adapter *,
|
|||
unsigned long);
|
||||
extern void zfcp_scsi_dbf_event_devreset(const char *, u8, struct zfcp_unit *,
|
||||
struct scsi_cmnd *);
|
||||
extern int zfcp_reqlist_isempty(struct zfcp_adapter *);
|
||||
|
||||
/* zfcp_erp.c */
|
||||
extern void zfcp_erp_modify_adapter_status(struct zfcp_adapter *, u8, void *,
|
||||
u32, int);
|
||||
extern void zfcp_erp_adapter_reopen(struct zfcp_adapter *, int, u8, void *);
|
||||
extern void zfcp_erp_adapter_shutdown(struct zfcp_adapter *, int, u8, void *);
|
||||
extern void zfcp_erp_adapter_failed(struct zfcp_adapter *, u8, void *);
|
||||
extern void zfcp_erp_modify_port_status(struct zfcp_port *, u8, void *, u32,
|
||||
int);
|
||||
extern int zfcp_erp_port_reopen(struct zfcp_port *, int, u8, void *);
|
||||
extern void zfcp_erp_port_shutdown(struct zfcp_port *, int, u8, void *);
|
||||
extern void zfcp_erp_port_forced_reopen(struct zfcp_port *, int, u8, void *);
|
||||
extern void zfcp_erp_port_failed(struct zfcp_port *, u8, void *);
|
||||
extern void zfcp_erp_modify_unit_status(struct zfcp_unit *, u8, void *, u32,
|
||||
int);
|
||||
extern void zfcp_erp_unit_reopen(struct zfcp_unit *, int, u8, void *);
|
||||
extern void zfcp_erp_unit_shutdown(struct zfcp_unit *, int, u8, void *);
|
||||
extern void zfcp_erp_unit_failed(struct zfcp_unit *, u8, void *);
|
||||
extern int zfcp_erp_thread_setup(struct zfcp_adapter *);
|
||||
extern void zfcp_erp_thread_kill(struct zfcp_adapter *);
|
||||
extern void zfcp_erp_wait(struct zfcp_adapter *);
|
||||
extern void zfcp_erp_notify(struct zfcp_erp_action *, unsigned long);
|
||||
extern void zfcp_erp_port_boxed(struct zfcp_port *, u8, void *);
|
||||
extern void zfcp_erp_unit_boxed(struct zfcp_unit *, u8, void *);
|
||||
extern void zfcp_erp_port_access_denied(struct zfcp_port *, u8, void *);
|
||||
extern void zfcp_erp_unit_access_denied(struct zfcp_unit *, u8, void *);
|
||||
extern void zfcp_erp_adapter_access_changed(struct zfcp_adapter *, u8, void *);
|
||||
extern void zfcp_erp_timeout_handler(unsigned long);
|
||||
|
||||
/* zfcp_fc.c */
|
||||
extern int zfcp_scan_ports(struct zfcp_adapter *);
|
||||
extern void _zfcp_scan_ports_later(struct work_struct *);
|
||||
extern void zfcp_fc_incoming_els(struct zfcp_fsf_req *);
|
||||
extern int zfcp_fc_ns_gid_pn_request(struct zfcp_erp_action *);
|
||||
extern void zfcp_fc_plogi_evaluate(struct zfcp_port *, struct fsf_plogi *);
|
||||
extern void zfcp_test_link(struct zfcp_port *);
|
||||
|
||||
/* zfcp_fsf.c */
|
||||
extern int zfcp_fsf_open_port(struct zfcp_erp_action *);
|
||||
extern int zfcp_fsf_close_port(struct zfcp_erp_action *);
|
||||
extern int zfcp_fsf_close_physical_port(struct zfcp_erp_action *);
|
||||
extern int zfcp_fsf_open_unit(struct zfcp_erp_action *);
|
||||
extern int zfcp_fsf_close_unit(struct zfcp_erp_action *);
|
||||
extern int zfcp_fsf_exchange_config_data(struct zfcp_erp_action *);
|
||||
extern int zfcp_fsf_exchange_config_data_sync(struct zfcp_adapter *,
|
||||
struct fsf_qtcb_bottom_config *);
|
||||
extern int zfcp_fsf_exchange_port_data(struct zfcp_erp_action *);
|
||||
extern int zfcp_fsf_exchange_port_data_sync(struct zfcp_adapter *,
|
||||
struct fsf_qtcb_bottom_port *);
|
||||
extern struct zfcp_fsf_req *zfcp_fsf_control_file(struct zfcp_adapter *,
|
||||
struct zfcp_fsf_cfdc *);
|
||||
extern void zfcp_fsf_req_dismiss_all(struct zfcp_adapter *);
|
||||
extern int zfcp_fsf_status_read(struct zfcp_adapter *);
|
||||
extern int zfcp_status_read_refill(struct zfcp_adapter *adapter);
|
||||
extern int zfcp_fsf_send_ct(struct zfcp_send_ct *, mempool_t *,
|
||||
struct zfcp_erp_action *);
|
||||
extern int zfcp_fsf_send_els(struct zfcp_send_els *);
|
||||
extern int zfcp_fsf_send_fcp_command_task(struct zfcp_adapter *,
|
||||
struct zfcp_unit *,
|
||||
struct scsi_cmnd *, int, int);
|
||||
extern void zfcp_fsf_req_complete(struct zfcp_fsf_req *);
|
||||
extern void zfcp_fsf_req_free(struct zfcp_fsf_req *);
|
||||
extern struct zfcp_fsf_req *zfcp_fsf_send_fcp_ctm(struct zfcp_adapter *,
|
||||
struct zfcp_unit *, u8, int);
|
||||
extern struct zfcp_fsf_req *zfcp_fsf_abort_fcp_command(unsigned long,
|
||||
struct zfcp_adapter *,
|
||||
struct zfcp_unit *, int);
|
||||
|
||||
/* zfcp_qdio.c */
|
||||
extern int zfcp_qdio_allocate(struct zfcp_adapter *);
|
||||
extern void zfcp_qdio_free(struct zfcp_adapter *);
|
||||
extern int zfcp_qdio_send(struct zfcp_fsf_req *);
|
||||
extern volatile struct qdio_buffer_element *zfcp_qdio_sbale_req(
|
||||
struct zfcp_fsf_req *);
|
||||
extern volatile struct qdio_buffer_element *zfcp_qdio_sbale_curr(
|
||||
struct zfcp_fsf_req *);
|
||||
extern int zfcp_qdio_sbals_from_sg(struct zfcp_fsf_req *, unsigned long,
|
||||
struct scatterlist *, int);
|
||||
extern int zfcp_qdio_open(struct zfcp_adapter *);
|
||||
extern void zfcp_qdio_close(struct zfcp_adapter *);
|
||||
|
||||
/* zfcp_scsi.c */
|
||||
extern struct zfcp_data zfcp_data;
|
||||
extern int zfcp_adapter_scsi_register(struct zfcp_adapter *);
|
||||
extern void zfcp_adapter_scsi_unregister(struct zfcp_adapter *);
|
||||
extern void zfcp_set_fcp_dl(struct fcp_cmnd_iu *, fcp_dl_t);
|
||||
extern char *zfcp_get_fcp_sns_info_ptr(struct fcp_rsp_iu *);
|
||||
extern struct fc_function_template zfcp_transport_functions;
|
||||
|
||||
/* zfcp_sysfs.c */
|
||||
extern struct attribute_group zfcp_sysfs_unit_attrs;
|
||||
extern struct attribute_group zfcp_sysfs_adapter_attrs;
|
||||
extern struct attribute_group zfcp_sysfs_ns_port_attrs;
|
||||
extern struct attribute_group zfcp_sysfs_port_attrs;
|
||||
extern struct device_attribute *zfcp_sysfs_sdev_attrs[];
|
||||
extern struct device_attribute *zfcp_sysfs_shost_attrs[];
|
||||
|
||||
#endif /* ZFCP_EXT_H */
|
||||
|
|
567
drivers/s390/scsi/zfcp_fc.c
Normal file
567
drivers/s390/scsi/zfcp_fc.c
Normal file
|
@ -0,0 +1,567 @@
|
|||
/*
|
||||
* zfcp device driver
|
||||
*
|
||||
* Fibre Channel related functions for the zfcp device driver.
|
||||
*
|
||||
* Copyright IBM Corporation 2008
|
||||
*/
|
||||
|
||||
#include "zfcp_ext.h"
|
||||
|
||||
struct ct_iu_gpn_ft_req {
|
||||
struct ct_hdr header;
|
||||
u8 flags;
|
||||
u8 domain_id_scope;
|
||||
u8 area_id_scope;
|
||||
u8 fc4_type;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct gpn_ft_resp_acc {
|
||||
u8 control;
|
||||
u8 port_id[3];
|
||||
u8 reserved[4];
|
||||
u64 wwpn;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
#define ZFCP_GPN_FT_ENTRIES ((PAGE_SIZE - sizeof(struct ct_hdr)) \
|
||||
/ sizeof(struct gpn_ft_resp_acc))
|
||||
#define ZFCP_GPN_FT_BUFFERS 4
|
||||
#define ZFCP_GPN_FT_MAX_ENTRIES ZFCP_GPN_FT_BUFFERS * (ZFCP_GPN_FT_ENTRIES + 1)
|
||||
|
||||
struct ct_iu_gpn_ft_resp {
|
||||
struct ct_hdr header;
|
||||
struct gpn_ft_resp_acc accept[ZFCP_GPN_FT_ENTRIES];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct zfcp_gpn_ft {
|
||||
struct zfcp_send_ct ct;
|
||||
struct scatterlist sg_req;
|
||||
struct scatterlist sg_resp[ZFCP_GPN_FT_BUFFERS];
|
||||
};
|
||||
|
||||
static struct zfcp_port *zfcp_get_port_by_did(struct zfcp_adapter *adapter,
|
||||
u32 d_id)
|
||||
{
|
||||
struct zfcp_port *port;
|
||||
|
||||
list_for_each_entry(port, &adapter->port_list_head, list)
|
||||
if ((port->d_id == d_id) &&
|
||||
!atomic_test_mask(ZFCP_STATUS_COMMON_REMOVE, &port->status))
|
||||
return port;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void _zfcp_fc_incoming_rscn(struct zfcp_fsf_req *fsf_req, u32 range,
|
||||
struct fcp_rscn_element *elem)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct zfcp_port *port;
|
||||
|
||||
read_lock_irqsave(&zfcp_data.config_lock, flags);
|
||||
list_for_each_entry(port, &fsf_req->adapter->port_list_head, list) {
|
||||
if (atomic_test_mask(ZFCP_STATUS_PORT_WKA, &port->status))
|
||||
continue;
|
||||
/* FIXME: ZFCP_STATUS_PORT_DID_DID check is racy */
|
||||
if (!atomic_test_mask(ZFCP_STATUS_PORT_DID_DID, &port->status))
|
||||
/* Try to connect to unused ports anyway. */
|
||||
zfcp_erp_port_reopen(port,
|
||||
ZFCP_STATUS_COMMON_ERP_FAILED,
|
||||
82, fsf_req);
|
||||
else if ((port->d_id & range) == (elem->nport_did & range))
|
||||
/* Check connection status for connected ports */
|
||||
zfcp_test_link(port);
|
||||
}
|
||||
read_unlock_irqrestore(&zfcp_data.config_lock, flags);
|
||||
}
|
||||
|
||||
static void zfcp_fc_incoming_rscn(struct zfcp_fsf_req *fsf_req)
|
||||
{
|
||||
struct fsf_status_read_buffer *status_buffer = (void *)fsf_req->data;
|
||||
struct fcp_rscn_head *fcp_rscn_head;
|
||||
struct fcp_rscn_element *fcp_rscn_element;
|
||||
u16 i;
|
||||
u16 no_entries;
|
||||
u32 range_mask;
|
||||
|
||||
fcp_rscn_head = (struct fcp_rscn_head *) status_buffer->payload.data;
|
||||
fcp_rscn_element = (struct fcp_rscn_element *) fcp_rscn_head;
|
||||
|
||||
/* see FC-FS */
|
||||
no_entries = fcp_rscn_head->payload_len /
|
||||
sizeof(struct fcp_rscn_element);
|
||||
|
||||
for (i = 1; i < no_entries; i++) {
|
||||
/* skip head and start with 1st element */
|
||||
fcp_rscn_element++;
|
||||
switch (fcp_rscn_element->addr_format) {
|
||||
case ZFCP_PORT_ADDRESS:
|
||||
range_mask = ZFCP_PORTS_RANGE_PORT;
|
||||
break;
|
||||
case ZFCP_AREA_ADDRESS:
|
||||
range_mask = ZFCP_PORTS_RANGE_AREA;
|
||||
break;
|
||||
case ZFCP_DOMAIN_ADDRESS:
|
||||
range_mask = ZFCP_PORTS_RANGE_DOMAIN;
|
||||
break;
|
||||
case ZFCP_FABRIC_ADDRESS:
|
||||
range_mask = ZFCP_PORTS_RANGE_FABRIC;
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
_zfcp_fc_incoming_rscn(fsf_req, range_mask, fcp_rscn_element);
|
||||
}
|
||||
schedule_work(&fsf_req->adapter->scan_work);
|
||||
}
|
||||
|
||||
static void zfcp_fc_incoming_wwpn(struct zfcp_fsf_req *req, wwn_t wwpn)
|
||||
{
|
||||
struct zfcp_adapter *adapter = req->adapter;
|
||||
struct zfcp_port *port;
|
||||
unsigned long flags;
|
||||
|
||||
read_lock_irqsave(&zfcp_data.config_lock, flags);
|
||||
list_for_each_entry(port, &adapter->port_list_head, list)
|
||||
if (port->wwpn == wwpn)
|
||||
break;
|
||||
read_unlock_irqrestore(&zfcp_data.config_lock, flags);
|
||||
|
||||
if (port && (port->wwpn == wwpn))
|
||||
zfcp_erp_port_forced_reopen(port, 0, 83, req);
|
||||
}
|
||||
|
||||
static void zfcp_fc_incoming_plogi(struct zfcp_fsf_req *req)
|
||||
{
|
||||
struct fsf_status_read_buffer *status_buffer =
|
||||
(struct fsf_status_read_buffer *)req->data;
|
||||
struct fsf_plogi *els_plogi =
|
||||
(struct fsf_plogi *) status_buffer->payload.data;
|
||||
|
||||
zfcp_fc_incoming_wwpn(req, els_plogi->serv_param.wwpn);
|
||||
}
|
||||
|
||||
static void zfcp_fc_incoming_logo(struct zfcp_fsf_req *req)
|
||||
{
|
||||
struct fsf_status_read_buffer *status_buffer =
|
||||
(struct fsf_status_read_buffer *)req->data;
|
||||
struct fcp_logo *els_logo =
|
||||
(struct fcp_logo *) status_buffer->payload.data;
|
||||
|
||||
zfcp_fc_incoming_wwpn(req, els_logo->nport_wwpn);
|
||||
}
|
||||
|
||||
/**
|
||||
* zfcp_fc_incoming_els - handle incoming ELS
|
||||
* @fsf_req - request which contains incoming ELS
|
||||
*/
|
||||
void zfcp_fc_incoming_els(struct zfcp_fsf_req *fsf_req)
|
||||
{
|
||||
struct fsf_status_read_buffer *status_buffer =
|
||||
(struct fsf_status_read_buffer *) fsf_req->data;
|
||||
unsigned int els_type = status_buffer->payload.data[0];
|
||||
|
||||
zfcp_san_dbf_event_incoming_els(fsf_req);
|
||||
if (els_type == LS_PLOGI)
|
||||
zfcp_fc_incoming_plogi(fsf_req);
|
||||
else if (els_type == LS_LOGO)
|
||||
zfcp_fc_incoming_logo(fsf_req);
|
||||
else if (els_type == LS_RSCN)
|
||||
zfcp_fc_incoming_rscn(fsf_req);
|
||||
}
|
||||
|
||||
static void zfcp_ns_gid_pn_handler(unsigned long data)
|
||||
{
|
||||
struct zfcp_gid_pn_data *gid_pn = (struct zfcp_gid_pn_data *) data;
|
||||
struct zfcp_send_ct *ct = &gid_pn->ct;
|
||||
struct ct_iu_gid_pn_req *ct_iu_req = sg_virt(ct->req);
|
||||
struct ct_iu_gid_pn_resp *ct_iu_resp = sg_virt(ct->resp);
|
||||
struct zfcp_port *port = gid_pn->port;
|
||||
|
||||
if (ct->status)
|
||||
goto out;
|
||||
if (ct_iu_resp->header.cmd_rsp_code != ZFCP_CT_ACCEPT) {
|
||||
atomic_set_mask(ZFCP_STATUS_PORT_INVALID_WWPN, &port->status);
|
||||
goto out;
|
||||
}
|
||||
/* paranoia */
|
||||
if (ct_iu_req->wwpn != port->wwpn)
|
||||
goto out;
|
||||
/* looks like a valid d_id */
|
||||
port->d_id = ct_iu_resp->d_id & ZFCP_DID_MASK;
|
||||
atomic_set_mask(ZFCP_STATUS_PORT_DID_DID, &port->status);
|
||||
out:
|
||||
mempool_free(gid_pn, port->adapter->pool.data_gid_pn);
|
||||
}
|
||||
|
||||
/**
|
||||
* zfcp_fc_ns_gid_pn_request - initiate GID_PN nameserver request
|
||||
* @erp_action: pointer to zfcp_erp_action where GID_PN request is needed
|
||||
* return: -ENOMEM on error, 0 otherwise
|
||||
*/
|
||||
int zfcp_fc_ns_gid_pn_request(struct zfcp_erp_action *erp_action)
|
||||
{
|
||||
int ret;
|
||||
struct zfcp_gid_pn_data *gid_pn;
|
||||
struct zfcp_adapter *adapter = erp_action->adapter;
|
||||
|
||||
gid_pn = mempool_alloc(adapter->pool.data_gid_pn, GFP_ATOMIC);
|
||||
if (!gid_pn)
|
||||
return -ENOMEM;
|
||||
|
||||
memset(gid_pn, 0, sizeof(*gid_pn));
|
||||
|
||||
/* setup parameters for send generic command */
|
||||
gid_pn->port = erp_action->port;
|
||||
gid_pn->ct.port = adapter->nameserver_port;
|
||||
gid_pn->ct.handler = zfcp_ns_gid_pn_handler;
|
||||
gid_pn->ct.handler_data = (unsigned long) gid_pn;
|
||||
gid_pn->ct.timeout = ZFCP_NS_GID_PN_TIMEOUT;
|
||||
gid_pn->ct.req = &gid_pn->req;
|
||||
gid_pn->ct.resp = &gid_pn->resp;
|
||||
gid_pn->ct.req_count = 1;
|
||||
gid_pn->ct.resp_count = 1;
|
||||
sg_init_one(&gid_pn->req, &gid_pn->ct_iu_req,
|
||||
sizeof(struct ct_iu_gid_pn_req));
|
||||
sg_init_one(&gid_pn->resp, &gid_pn->ct_iu_resp,
|
||||
sizeof(struct ct_iu_gid_pn_resp));
|
||||
|
||||
/* setup nameserver request */
|
||||
gid_pn->ct_iu_req.header.revision = ZFCP_CT_REVISION;
|
||||
gid_pn->ct_iu_req.header.gs_type = ZFCP_CT_DIRECTORY_SERVICE;
|
||||
gid_pn->ct_iu_req.header.gs_subtype = ZFCP_CT_NAME_SERVER;
|
||||
gid_pn->ct_iu_req.header.options = ZFCP_CT_SYNCHRONOUS;
|
||||
gid_pn->ct_iu_req.header.cmd_rsp_code = ZFCP_CT_GID_PN;
|
||||
gid_pn->ct_iu_req.header.max_res_size = ZFCP_CT_MAX_SIZE;
|
||||
gid_pn->ct_iu_req.wwpn = erp_action->port->wwpn;
|
||||
|
||||
ret = zfcp_fsf_send_ct(&gid_pn->ct, adapter->pool.fsf_req_erp,
|
||||
erp_action);
|
||||
if (ret)
|
||||
mempool_free(gid_pn, adapter->pool.data_gid_pn);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* zfcp_fc_plogi_evaluate - evaluate PLOGI playload
|
||||
* @port: zfcp_port structure
|
||||
* @plogi: plogi payload
|
||||
*
|
||||
* Evaluate PLOGI playload and copy important fields into zfcp_port structure
|
||||
*/
|
||||
void zfcp_fc_plogi_evaluate(struct zfcp_port *port, struct fsf_plogi *plogi)
|
||||
{
|
||||
port->maxframe_size = plogi->serv_param.common_serv_param[7] |
|
||||
((plogi->serv_param.common_serv_param[6] & 0x0F) << 8);
|
||||
if (plogi->serv_param.class1_serv_param[0] & 0x80)
|
||||
port->supported_classes |= FC_COS_CLASS1;
|
||||
if (plogi->serv_param.class2_serv_param[0] & 0x80)
|
||||
port->supported_classes |= FC_COS_CLASS2;
|
||||
if (plogi->serv_param.class3_serv_param[0] & 0x80)
|
||||
port->supported_classes |= FC_COS_CLASS3;
|
||||
if (plogi->serv_param.class4_serv_param[0] & 0x80)
|
||||
port->supported_classes |= FC_COS_CLASS4;
|
||||
}
|
||||
|
||||
struct zfcp_els_adisc {
|
||||
struct zfcp_send_els els;
|
||||
struct scatterlist req;
|
||||
struct scatterlist resp;
|
||||
struct zfcp_ls_adisc ls_adisc;
|
||||
struct zfcp_ls_adisc_acc ls_adisc_acc;
|
||||
};
|
||||
|
||||
static void zfcp_fc_adisc_handler(unsigned long data)
|
||||
{
|
||||
struct zfcp_els_adisc *adisc = (struct zfcp_els_adisc *) data;
|
||||
struct zfcp_port *port = adisc->els.port;
|
||||
struct zfcp_ls_adisc_acc *ls_adisc = &adisc->ls_adisc_acc;
|
||||
|
||||
if (adisc->els.status) {
|
||||
/* request rejected or timed out */
|
||||
zfcp_erp_port_forced_reopen(port, 0, 63, NULL);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!port->wwnn)
|
||||
port->wwnn = ls_adisc->wwnn;
|
||||
|
||||
if (port->wwpn != ls_adisc->wwpn)
|
||||
zfcp_erp_port_reopen(port, 0, 64, NULL);
|
||||
|
||||
out:
|
||||
zfcp_port_put(port);
|
||||
kfree(adisc);
|
||||
}
|
||||
|
||||
static int zfcp_fc_adisc(struct zfcp_port *port)
|
||||
{
|
||||
struct zfcp_els_adisc *adisc;
|
||||
struct zfcp_adapter *adapter = port->adapter;
|
||||
|
||||
adisc = kzalloc(sizeof(struct zfcp_els_adisc), GFP_ATOMIC);
|
||||
if (!adisc)
|
||||
return -ENOMEM;
|
||||
|
||||
adisc->els.req = &adisc->req;
|
||||
adisc->els.resp = &adisc->resp;
|
||||
sg_init_one(adisc->els.req, &adisc->ls_adisc,
|
||||
sizeof(struct zfcp_ls_adisc));
|
||||
sg_init_one(adisc->els.resp, &adisc->ls_adisc_acc,
|
||||
sizeof(struct zfcp_ls_adisc_acc));
|
||||
|
||||
adisc->els.req_count = 1;
|
||||
adisc->els.resp_count = 1;
|
||||
adisc->els.adapter = adapter;
|
||||
adisc->els.port = port;
|
||||
adisc->els.d_id = port->d_id;
|
||||
adisc->els.handler = zfcp_fc_adisc_handler;
|
||||
adisc->els.handler_data = (unsigned long) adisc;
|
||||
adisc->els.ls_code = adisc->ls_adisc.code = ZFCP_LS_ADISC;
|
||||
|
||||
/* acc. to FC-FS, hard_nport_id in ADISC should not be set for ports
|
||||
without FC-AL-2 capability, so we don't set it */
|
||||
adisc->ls_adisc.wwpn = fc_host_port_name(adapter->scsi_host);
|
||||
adisc->ls_adisc.wwnn = fc_host_node_name(adapter->scsi_host);
|
||||
adisc->ls_adisc.nport_id = fc_host_port_id(adapter->scsi_host);
|
||||
|
||||
return zfcp_fsf_send_els(&adisc->els);
|
||||
}
|
||||
|
||||
/**
|
||||
* zfcp_test_link - lightweight link test procedure
|
||||
* @port: port to be tested
|
||||
*
|
||||
* Test status of a link to a remote port using the ELS command ADISC.
|
||||
* If there is a problem with the remote port, error recovery steps
|
||||
* will be triggered.
|
||||
*/
|
||||
void zfcp_test_link(struct zfcp_port *port)
|
||||
{
|
||||
int retval;
|
||||
|
||||
zfcp_port_get(port);
|
||||
retval = zfcp_fc_adisc(port);
|
||||
if (retval == 0 || retval == -EBUSY)
|
||||
return;
|
||||
|
||||
/* send of ADISC was not possible */
|
||||
zfcp_port_put(port);
|
||||
zfcp_erp_port_forced_reopen(port, 0, 65, NULL);
|
||||
}
|
||||
|
||||
static int zfcp_scan_get_nameserver(struct zfcp_adapter *adapter)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!adapter->nameserver_port)
|
||||
return -EINTR;
|
||||
|
||||
if (!atomic_test_mask(ZFCP_STATUS_COMMON_UNBLOCKED,
|
||||
&adapter->nameserver_port->status)) {
|
||||
ret = zfcp_erp_port_reopen(adapter->nameserver_port, 0, 148,
|
||||
NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
zfcp_erp_wait(adapter);
|
||||
zfcp_port_put(adapter->nameserver_port);
|
||||
}
|
||||
return !atomic_test_mask(ZFCP_STATUS_COMMON_UNBLOCKED,
|
||||
&adapter->nameserver_port->status);
|
||||
}
|
||||
|
||||
static void zfcp_gpn_ft_handler(unsigned long _done)
|
||||
{
|
||||
complete((struct completion *)_done);
|
||||
}
|
||||
|
||||
static void zfcp_free_sg_env(struct zfcp_gpn_ft *gpn_ft)
|
||||
{
|
||||
struct scatterlist *sg = &gpn_ft->sg_req;
|
||||
|
||||
kfree(sg_virt(sg)); /* free request buffer */
|
||||
zfcp_sg_free_table(gpn_ft->sg_resp, ZFCP_GPN_FT_BUFFERS);
|
||||
|
||||
kfree(gpn_ft);
|
||||
}
|
||||
|
||||
static struct zfcp_gpn_ft *zfcp_alloc_sg_env(void)
|
||||
{
|
||||
struct zfcp_gpn_ft *gpn_ft;
|
||||
struct ct_iu_gpn_ft_req *req;
|
||||
|
||||
gpn_ft = kzalloc(sizeof(*gpn_ft), GFP_KERNEL);
|
||||
if (!gpn_ft)
|
||||
return NULL;
|
||||
|
||||
req = kzalloc(sizeof(struct ct_iu_gpn_ft_req), GFP_KERNEL);
|
||||
if (!req) {
|
||||
kfree(gpn_ft);
|
||||
gpn_ft = NULL;
|
||||
goto out;
|
||||
}
|
||||
sg_init_one(&gpn_ft->sg_req, req, sizeof(*req));
|
||||
|
||||
if (zfcp_sg_setup_table(gpn_ft->sg_resp, ZFCP_GPN_FT_BUFFERS)) {
|
||||
zfcp_free_sg_env(gpn_ft);
|
||||
gpn_ft = NULL;
|
||||
}
|
||||
out:
|
||||
return gpn_ft;
|
||||
}
|
||||
|
||||
|
||||
static int zfcp_scan_issue_gpn_ft(struct zfcp_gpn_ft *gpn_ft,
|
||||
struct zfcp_adapter *adapter)
|
||||
{
|
||||
struct zfcp_send_ct *ct = &gpn_ft->ct;
|
||||
struct ct_iu_gpn_ft_req *req = sg_virt(&gpn_ft->sg_req);
|
||||
struct completion done;
|
||||
int ret;
|
||||
|
||||
/* prepare CT IU for GPN_FT */
|
||||
req->header.revision = ZFCP_CT_REVISION;
|
||||
req->header.gs_type = ZFCP_CT_DIRECTORY_SERVICE;
|
||||
req->header.gs_subtype = ZFCP_CT_NAME_SERVER;
|
||||
req->header.options = ZFCP_CT_SYNCHRONOUS;
|
||||
req->header.cmd_rsp_code = ZFCP_CT_GPN_FT;
|
||||
req->header.max_res_size = (sizeof(struct gpn_ft_resp_acc) *
|
||||
(ZFCP_GPN_FT_MAX_ENTRIES - 1)) >> 2;
|
||||
req->flags = 0;
|
||||
req->domain_id_scope = 0;
|
||||
req->area_id_scope = 0;
|
||||
req->fc4_type = ZFCP_CT_SCSI_FCP;
|
||||
|
||||
/* prepare zfcp_send_ct */
|
||||
ct->port = adapter->nameserver_port;
|
||||
ct->handler = zfcp_gpn_ft_handler;
|
||||
ct->handler_data = (unsigned long)&done;
|
||||
ct->timeout = 10;
|
||||
ct->req = &gpn_ft->sg_req;
|
||||
ct->resp = gpn_ft->sg_resp;
|
||||
ct->req_count = 1;
|
||||
ct->resp_count = ZFCP_GPN_FT_BUFFERS;
|
||||
|
||||
init_completion(&done);
|
||||
ret = zfcp_fsf_send_ct(ct, NULL, NULL);
|
||||
if (!ret)
|
||||
wait_for_completion(&done);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void zfcp_validate_port(struct zfcp_port *port)
|
||||
{
|
||||
struct zfcp_adapter *adapter = port->adapter;
|
||||
|
||||
atomic_clear_mask(ZFCP_STATUS_COMMON_NOESC, &port->status);
|
||||
|
||||
if (port == adapter->nameserver_port)
|
||||
return;
|
||||
if ((port->supported_classes != 0) || (port->units != 0)) {
|
||||
zfcp_port_put(port);
|
||||
return;
|
||||
}
|
||||
zfcp_erp_port_shutdown(port, 0, 151, NULL);
|
||||
zfcp_erp_wait(adapter);
|
||||
zfcp_port_put(port);
|
||||
zfcp_port_dequeue(port);
|
||||
}
|
||||
|
||||
static int zfcp_scan_eval_gpn_ft(struct zfcp_gpn_ft *gpn_ft)
|
||||
{
|
||||
struct zfcp_send_ct *ct = &gpn_ft->ct;
|
||||
struct scatterlist *sg = gpn_ft->sg_resp;
|
||||
struct ct_hdr *hdr = sg_virt(sg);
|
||||
struct gpn_ft_resp_acc *acc = sg_virt(sg);
|
||||
struct zfcp_adapter *adapter = ct->port->adapter;
|
||||
struct zfcp_port *port, *tmp;
|
||||
u32 d_id;
|
||||
int ret = 0, x;
|
||||
|
||||
if (ct->status)
|
||||
return -EIO;
|
||||
|
||||
if (hdr->cmd_rsp_code != ZFCP_CT_ACCEPT) {
|
||||
if (hdr->reason_code == ZFCP_CT_UNABLE_TO_PERFORM_CMD)
|
||||
return -EAGAIN; /* might be a temporary condition */
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (hdr->max_res_size)
|
||||
return -E2BIG;
|
||||
|
||||
down(&zfcp_data.config_sema);
|
||||
|
||||
/* first entry is the header */
|
||||
for (x = 1; x < ZFCP_GPN_FT_MAX_ENTRIES; x++) {
|
||||
if (x % (ZFCP_GPN_FT_ENTRIES + 1))
|
||||
acc++;
|
||||
else
|
||||
acc = sg_virt(++sg);
|
||||
|
||||
d_id = acc->port_id[0] << 16 | acc->port_id[1] << 8 |
|
||||
acc->port_id[2];
|
||||
|
||||
/* skip the adapter's port and known remote ports */
|
||||
if (acc->wwpn == fc_host_port_name(adapter->scsi_host) ||
|
||||
zfcp_get_port_by_did(adapter, d_id))
|
||||
continue;
|
||||
|
||||
port = zfcp_port_enqueue(adapter, acc->wwpn,
|
||||
ZFCP_STATUS_PORT_DID_DID |
|
||||
ZFCP_STATUS_COMMON_NOESC, d_id);
|
||||
if (IS_ERR(port))
|
||||
ret = PTR_ERR(port);
|
||||
else
|
||||
zfcp_erp_port_reopen(port, 0, 149, NULL);
|
||||
if (acc->control & 0x80) /* last entry */
|
||||
break;
|
||||
}
|
||||
|
||||
zfcp_erp_wait(adapter);
|
||||
list_for_each_entry_safe(port, tmp, &adapter->port_list_head, list)
|
||||
zfcp_validate_port(port);
|
||||
up(&zfcp_data.config_sema);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* zfcp_scan_ports - scan remote ports and attach new ports
|
||||
* @adapter: pointer to struct zfcp_adapter
|
||||
*/
|
||||
int zfcp_scan_ports(struct zfcp_adapter *adapter)
|
||||
{
|
||||
int ret, i;
|
||||
struct zfcp_gpn_ft *gpn_ft;
|
||||
|
||||
zfcp_erp_wait(adapter); /* wait until adapter is finished with ERP */
|
||||
if (fc_host_port_type(adapter->scsi_host) != FC_PORTTYPE_NPORT)
|
||||
return 0;
|
||||
|
||||
ret = zfcp_scan_get_nameserver(adapter);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
gpn_ft = zfcp_alloc_sg_env();
|
||||
if (!gpn_ft)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
ret = zfcp_scan_issue_gpn_ft(gpn_ft, adapter);
|
||||
if (!ret) {
|
||||
ret = zfcp_scan_eval_gpn_ft(gpn_ft);
|
||||
if (ret == -EAGAIN)
|
||||
ssleep(1);
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
zfcp_free_sg_env(gpn_ft);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
void _zfcp_scan_ports_later(struct work_struct *work)
|
||||
{
|
||||
zfcp_scan_ports(container_of(work, struct zfcp_adapter, scan_work));
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -1,27 +1,16 @@
|
|||
/*
|
||||
* This file is part of the zfcp device driver for
|
||||
* FCP adapters for IBM System z9 and zSeries.
|
||||
* zfcp device driver
|
||||
*
|
||||
* (C) Copyright IBM Corp. 2002, 2006
|
||||
* Interface to the FSF support functions.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
* Copyright IBM Corporation 2002, 2008
|
||||
*/
|
||||
|
||||
#ifndef FSF_H
|
||||
#define FSF_H
|
||||
|
||||
#include <linux/pfn.h>
|
||||
|
||||
#define FSF_QTCB_CURRENT_VERSION 0x00000001
|
||||
|
||||
/* FSF commands */
|
||||
|
@ -258,6 +247,16 @@
|
|||
#define FSF_UNIT_ACCESS_EXCLUSIVE 0x02000000
|
||||
#define FSF_UNIT_ACCESS_OUTBOUND_TRANSFER 0x10000000
|
||||
|
||||
/* FSF interface for CFDC */
|
||||
#define ZFCP_CFDC_MAX_SIZE 127 * 1024
|
||||
#define ZFCP_CFDC_PAGES PFN_UP(ZFCP_CFDC_MAX_SIZE)
|
||||
|
||||
struct zfcp_fsf_cfdc {
|
||||
struct scatterlist sg[ZFCP_CFDC_PAGES];
|
||||
u32 command;
|
||||
u32 option;
|
||||
};
|
||||
|
||||
struct fsf_queue_designator {
|
||||
u8 cssid;
|
||||
u8 chpid;
|
||||
|
@ -288,29 +287,6 @@ struct fsf_bit_error_payload {
|
|||
u32 current_transmit_b2b_credit;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct fsf_status_read_buffer {
|
||||
u32 status_type;
|
||||
u32 status_subtype;
|
||||
u32 length;
|
||||
u32 res1;
|
||||
struct fsf_queue_designator queue_designator;
|
||||
u32 d_id;
|
||||
u32 class;
|
||||
u64 fcp_lun;
|
||||
u8 res3[24];
|
||||
u8 payload[FSF_STATUS_READ_PAYLOAD_SIZE];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct fsf_qual_version_error {
|
||||
u32 fsf_version;
|
||||
u32 res1[3];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct fsf_qual_sequence_error {
|
||||
u32 exp_req_seq_no;
|
||||
u32 res1[3];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct fsf_link_down_info {
|
||||
u32 error_code;
|
||||
u32 res1;
|
||||
|
@ -323,11 +299,47 @@ struct fsf_link_down_info {
|
|||
u8 vendor_specific_code;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct fsf_status_read_buffer {
|
||||
u32 status_type;
|
||||
u32 status_subtype;
|
||||
u32 length;
|
||||
u32 res1;
|
||||
struct fsf_queue_designator queue_designator;
|
||||
u32 d_id;
|
||||
u32 class;
|
||||
u64 fcp_lun;
|
||||
u8 res3[24];
|
||||
union {
|
||||
u8 data[FSF_STATUS_READ_PAYLOAD_SIZE];
|
||||
u32 word[FSF_STATUS_READ_PAYLOAD_SIZE/sizeof(u32)];
|
||||
struct fsf_link_down_info link_down_info;
|
||||
struct fsf_bit_error_payload bit_error;
|
||||
} payload;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct fsf_qual_version_error {
|
||||
u32 fsf_version;
|
||||
u32 res1[3];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct fsf_qual_sequence_error {
|
||||
u32 exp_req_seq_no;
|
||||
u32 res1[3];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct fsf_qual_latency_info {
|
||||
u32 channel_lat;
|
||||
u32 fabric_lat;
|
||||
u8 res1[8];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
union fsf_prot_status_qual {
|
||||
u32 word[FSF_PROT_STATUS_QUAL_SIZE / sizeof(u32)];
|
||||
u64 doubleword[FSF_PROT_STATUS_QUAL_SIZE / sizeof(u64)];
|
||||
struct fsf_qual_version_error version_error;
|
||||
struct fsf_qual_sequence_error sequence_error;
|
||||
struct fsf_link_down_info link_down_info;
|
||||
struct fsf_qual_latency_info latency_info;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct fsf_qtcb_prefix {
|
||||
|
@ -437,7 +449,9 @@ struct fsf_qtcb_bottom_config {
|
|||
u32 fc_link_speed;
|
||||
u32 adapter_type;
|
||||
u32 peer_d_id;
|
||||
u8 res2[12];
|
||||
u8 res1[2];
|
||||
u16 timer_interval;
|
||||
u8 res2[8];
|
||||
u32 s_id;
|
||||
struct fsf_nport_serv_param nport_serv_param;
|
||||
u8 reserved_nport_serv_param[16];
|
||||
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
496
drivers/s390/scsi/zfcp_sysfs.c
Normal file
496
drivers/s390/scsi/zfcp_sysfs.c
Normal file
|
@ -0,0 +1,496 @@
|
|||
/*
|
||||
* zfcp device driver
|
||||
*
|
||||
* sysfs attributes.
|
||||
*
|
||||
* Copyright IBM Corporation 2008
|
||||
*/
|
||||
|
||||
#include "zfcp_ext.h"
|
||||
|
||||
#define ZFCP_DEV_ATTR(_feat, _name, _mode, _show, _store) \
|
||||
struct device_attribute dev_attr_##_feat##_##_name = __ATTR(_name, _mode,\
|
||||
_show, _store)
|
||||
#define ZFCP_DEFINE_ATTR(_feat_def, _feat, _name, _format, _value) \
|
||||
static ssize_t zfcp_sysfs_##_feat##_##_name##_show(struct device *dev, \
|
||||
struct device_attribute *at,\
|
||||
char *buf) \
|
||||
{ \
|
||||
struct _feat_def *_feat = dev_get_drvdata(dev); \
|
||||
\
|
||||
return sprintf(buf, _format, _value); \
|
||||
} \
|
||||
static ZFCP_DEV_ATTR(_feat, _name, S_IRUGO, \
|
||||
zfcp_sysfs_##_feat##_##_name##_show, NULL);
|
||||
|
||||
ZFCP_DEFINE_ATTR(zfcp_adapter, adapter, status, "0x%08x\n",
|
||||
atomic_read(&adapter->status));
|
||||
ZFCP_DEFINE_ATTR(zfcp_adapter, adapter, peer_wwnn, "0x%016llx\n",
|
||||
adapter->peer_wwnn);
|
||||
ZFCP_DEFINE_ATTR(zfcp_adapter, adapter, peer_wwpn, "0x%016llx\n",
|
||||
adapter->peer_wwpn);
|
||||
ZFCP_DEFINE_ATTR(zfcp_adapter, adapter, peer_d_id, "0x%06x\n",
|
||||
adapter->peer_d_id);
|
||||
ZFCP_DEFINE_ATTR(zfcp_adapter, adapter, card_version, "0x%04x\n",
|
||||
adapter->hydra_version);
|
||||
ZFCP_DEFINE_ATTR(zfcp_adapter, adapter, lic_version, "0x%08x\n",
|
||||
adapter->fsf_lic_version);
|
||||
ZFCP_DEFINE_ATTR(zfcp_adapter, adapter, hardware_version, "0x%08x\n",
|
||||
adapter->hardware_version);
|
||||
ZFCP_DEFINE_ATTR(zfcp_adapter, adapter, in_recovery, "%d\n",
|
||||
(atomic_read(&adapter->status) &
|
||||
ZFCP_STATUS_COMMON_ERP_INUSE) != 0);
|
||||
|
||||
ZFCP_DEFINE_ATTR(zfcp_port, port, status, "0x%08x\n",
|
||||
atomic_read(&port->status));
|
||||
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(zfcp_unit, unit, status, "0x%08x\n",
|
||||
atomic_read(&unit->status));
|
||||
ZFCP_DEFINE_ATTR(zfcp_unit, unit, in_recovery, "%d\n",
|
||||
(atomic_read(&unit->status) &
|
||||
ZFCP_STATUS_COMMON_ERP_INUSE) != 0);
|
||||
ZFCP_DEFINE_ATTR(zfcp_unit, unit, access_denied, "%d\n",
|
||||
(atomic_read(&unit->status) &
|
||||
ZFCP_STATUS_COMMON_ACCESS_DENIED) != 0);
|
||||
ZFCP_DEFINE_ATTR(zfcp_unit, unit, access_shared, "%d\n",
|
||||
(atomic_read(&unit->status) &
|
||||
ZFCP_STATUS_UNIT_SHARED) != 0);
|
||||
ZFCP_DEFINE_ATTR(zfcp_unit, unit, access_readonly, "%d\n",
|
||||
(atomic_read(&unit->status) &
|
||||
ZFCP_STATUS_UNIT_READONLY) != 0);
|
||||
|
||||
#define ZFCP_SYSFS_FAILED(_feat_def, _feat, _adapter, _mod_id, _reopen_id) \
|
||||
static ssize_t zfcp_sysfs_##_feat##_failed_show(struct device *dev, \
|
||||
struct device_attribute *attr, \
|
||||
char *buf) \
|
||||
{ \
|
||||
struct _feat_def *_feat = dev_get_drvdata(dev); \
|
||||
\
|
||||
if (atomic_read(&_feat->status) & ZFCP_STATUS_COMMON_ERP_FAILED) \
|
||||
return sprintf(buf, "1\n"); \
|
||||
else \
|
||||
return sprintf(buf, "0\n"); \
|
||||
} \
|
||||
static ssize_t zfcp_sysfs_##_feat##_failed_store(struct device *dev, \
|
||||
struct device_attribute *attr,\
|
||||
const char *buf, size_t count)\
|
||||
{ \
|
||||
struct _feat_def *_feat = dev_get_drvdata(dev); \
|
||||
unsigned long val; \
|
||||
int retval = 0; \
|
||||
\
|
||||
down(&zfcp_data.config_sema); \
|
||||
if (atomic_read(&_feat->status) & ZFCP_STATUS_COMMON_REMOVE) { \
|
||||
retval = -EBUSY; \
|
||||
goto out; \
|
||||
} \
|
||||
\
|
||||
if (strict_strtoul(buf, 0, &val) || val != 0) { \
|
||||
retval = -EINVAL; \
|
||||
goto out; \
|
||||
} \
|
||||
\
|
||||
zfcp_erp_modify_##_feat##_status(_feat, _mod_id, NULL, \
|
||||
ZFCP_STATUS_COMMON_RUNNING, ZFCP_SET);\
|
||||
zfcp_erp_##_feat##_reopen(_feat, ZFCP_STATUS_COMMON_ERP_FAILED, \
|
||||
_reopen_id, NULL); \
|
||||
zfcp_erp_wait(_adapter); \
|
||||
out: \
|
||||
up(&zfcp_data.config_sema); \
|
||||
return retval ? retval : (ssize_t) count; \
|
||||
} \
|
||||
static ZFCP_DEV_ATTR(_feat, failed, S_IWUSR | S_IRUGO, \
|
||||
zfcp_sysfs_##_feat##_failed_show, \
|
||||
zfcp_sysfs_##_feat##_failed_store);
|
||||
|
||||
ZFCP_SYSFS_FAILED(zfcp_adapter, adapter, adapter, 44, 93);
|
||||
ZFCP_SYSFS_FAILED(zfcp_port, port, port->adapter, 45, 96);
|
||||
ZFCP_SYSFS_FAILED(zfcp_unit, unit, unit->port->adapter, 46, 97);
|
||||
|
||||
static ssize_t zfcp_sysfs_port_rescan_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct zfcp_adapter *adapter = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
if (atomic_read(&adapter->status) & ZFCP_STATUS_COMMON_REMOVE)
|
||||
return -EBUSY;
|
||||
|
||||
ret = zfcp_scan_ports(adapter);
|
||||
return ret ? ret : (ssize_t) count;
|
||||
}
|
||||
static ZFCP_DEV_ATTR(adapter, port_rescan, S_IWUSR, NULL,
|
||||
zfcp_sysfs_port_rescan_store);
|
||||
|
||||
static ssize_t zfcp_sysfs_port_remove_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct zfcp_adapter *adapter = dev_get_drvdata(dev);
|
||||
struct zfcp_port *port;
|
||||
wwn_t wwpn;
|
||||
int retval = 0;
|
||||
|
||||
down(&zfcp_data.config_sema);
|
||||
if (atomic_read(&adapter->status) & ZFCP_STATUS_COMMON_REMOVE) {
|
||||
retval = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (strict_strtoull(buf, 0, &wwpn)) {
|
||||
retval = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
write_lock_irq(&zfcp_data.config_lock);
|
||||
port = zfcp_get_port_by_wwpn(adapter, wwpn);
|
||||
if (port && (atomic_read(&port->refcount) == 0)) {
|
||||
zfcp_port_get(port);
|
||||
atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, &port->status);
|
||||
list_move(&port->list, &adapter->port_remove_lh);
|
||||
} else
|
||||
port = NULL;
|
||||
write_unlock_irq(&zfcp_data.config_lock);
|
||||
|
||||
if (!port) {
|
||||
retval = -ENXIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
zfcp_erp_port_shutdown(port, 0, 92, NULL);
|
||||
zfcp_erp_wait(adapter);
|
||||
zfcp_port_put(port);
|
||||
zfcp_port_dequeue(port);
|
||||
out:
|
||||
up(&zfcp_data.config_sema);
|
||||
return retval ? retval : (ssize_t) count;
|
||||
}
|
||||
static ZFCP_DEV_ATTR(adapter, port_remove, S_IWUSR, NULL,
|
||||
zfcp_sysfs_port_remove_store);
|
||||
|
||||
static struct attribute *zfcp_adapter_attrs[] = {
|
||||
&dev_attr_adapter_failed.attr,
|
||||
&dev_attr_adapter_in_recovery.attr,
|
||||
&dev_attr_adapter_port_remove.attr,
|
||||
&dev_attr_adapter_port_rescan.attr,
|
||||
&dev_attr_adapter_peer_wwnn.attr,
|
||||
&dev_attr_adapter_peer_wwpn.attr,
|
||||
&dev_attr_adapter_peer_d_id.attr,
|
||||
&dev_attr_adapter_card_version.attr,
|
||||
&dev_attr_adapter_lic_version.attr,
|
||||
&dev_attr_adapter_status.attr,
|
||||
&dev_attr_adapter_hardware_version.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
struct attribute_group zfcp_sysfs_adapter_attrs = {
|
||||
.attrs = zfcp_adapter_attrs,
|
||||
};
|
||||
|
||||
static ssize_t zfcp_sysfs_unit_add_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct zfcp_port *port = dev_get_drvdata(dev);
|
||||
struct zfcp_unit *unit;
|
||||
fcp_lun_t fcp_lun;
|
||||
int retval = -EINVAL;
|
||||
|
||||
down(&zfcp_data.config_sema);
|
||||
if (atomic_read(&port->status) & ZFCP_STATUS_COMMON_REMOVE) {
|
||||
retval = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (strict_strtoull(buf, 0, &fcp_lun))
|
||||
goto out;
|
||||
|
||||
unit = zfcp_unit_enqueue(port, fcp_lun);
|
||||
if (IS_ERR(unit))
|
||||
goto out;
|
||||
|
||||
retval = 0;
|
||||
|
||||
zfcp_erp_unit_reopen(unit, 0, 94, NULL);
|
||||
zfcp_erp_wait(unit->port->adapter);
|
||||
zfcp_unit_put(unit);
|
||||
out:
|
||||
up(&zfcp_data.config_sema);
|
||||
return retval ? retval : (ssize_t) count;
|
||||
}
|
||||
static DEVICE_ATTR(unit_add, S_IWUSR, NULL, zfcp_sysfs_unit_add_store);
|
||||
|
||||
static ssize_t zfcp_sysfs_unit_remove_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct zfcp_port *port = dev_get_drvdata(dev);
|
||||
struct zfcp_unit *unit;
|
||||
fcp_lun_t fcp_lun;
|
||||
int retval = 0;
|
||||
|
||||
down(&zfcp_data.config_sema);
|
||||
if (atomic_read(&port->status) & ZFCP_STATUS_COMMON_REMOVE) {
|
||||
retval = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (strict_strtoull(buf, 0, &fcp_lun)) {
|
||||
retval = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
write_lock_irq(&zfcp_data.config_lock);
|
||||
unit = zfcp_get_unit_by_lun(port, fcp_lun);
|
||||
if (unit && (atomic_read(&unit->refcount) == 0)) {
|
||||
zfcp_unit_get(unit);
|
||||
atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, &unit->status);
|
||||
list_move(&unit->list, &port->unit_remove_lh);
|
||||
} else
|
||||
unit = NULL;
|
||||
|
||||
write_unlock_irq(&zfcp_data.config_lock);
|
||||
|
||||
if (!unit) {
|
||||
retval = -ENXIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
zfcp_erp_unit_shutdown(unit, 0, 95, NULL);
|
||||
zfcp_erp_wait(unit->port->adapter);
|
||||
zfcp_unit_put(unit);
|
||||
zfcp_unit_dequeue(unit);
|
||||
out:
|
||||
up(&zfcp_data.config_sema);
|
||||
return retval ? retval : (ssize_t) count;
|
||||
}
|
||||
static DEVICE_ATTR(unit_remove, S_IWUSR, NULL, zfcp_sysfs_unit_remove_store);
|
||||
|
||||
static struct attribute *zfcp_port_ns_attrs[] = {
|
||||
&dev_attr_port_failed.attr,
|
||||
&dev_attr_port_in_recovery.attr,
|
||||
&dev_attr_port_status.attr,
|
||||
&dev_attr_port_access_denied.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
/**
|
||||
* zfcp_sysfs_ns_port_attrs - sysfs attributes for nameserver
|
||||
*/
|
||||
struct attribute_group zfcp_sysfs_ns_port_attrs = {
|
||||
.attrs = zfcp_port_ns_attrs,
|
||||
};
|
||||
|
||||
static struct attribute *zfcp_port_no_ns_attrs[] = {
|
||||
&dev_attr_unit_add.attr,
|
||||
&dev_attr_unit_remove.attr,
|
||||
&dev_attr_port_failed.attr,
|
||||
&dev_attr_port_in_recovery.attr,
|
||||
&dev_attr_port_status.attr,
|
||||
&dev_attr_port_access_denied.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
/**
|
||||
* zfcp_sysfs_port_attrs - sysfs attributes for all other ports
|
||||
*/
|
||||
struct attribute_group zfcp_sysfs_port_attrs = {
|
||||
.attrs = zfcp_port_no_ns_attrs,
|
||||
};
|
||||
|
||||
static struct attribute *zfcp_unit_attrs[] = {
|
||||
&dev_attr_unit_failed.attr,
|
||||
&dev_attr_unit_in_recovery.attr,
|
||||
&dev_attr_unit_status.attr,
|
||||
&dev_attr_unit_access_denied.attr,
|
||||
&dev_attr_unit_access_shared.attr,
|
||||
&dev_attr_unit_access_readonly.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
struct attribute_group zfcp_sysfs_unit_attrs = {
|
||||
.attrs = zfcp_unit_attrs,
|
||||
};
|
||||
|
||||
#define ZFCP_DEFINE_LATENCY_ATTR(_name) \
|
||||
static ssize_t \
|
||||
zfcp_sysfs_unit_##_name##_latency_show(struct device *dev, \
|
||||
struct device_attribute *attr, \
|
||||
char *buf) { \
|
||||
struct scsi_device *sdev = to_scsi_device(dev); \
|
||||
struct zfcp_unit *unit = sdev->hostdata; \
|
||||
struct zfcp_latencies *lat = &unit->latencies; \
|
||||
struct zfcp_adapter *adapter = unit->port->adapter; \
|
||||
unsigned long flags; \
|
||||
unsigned long long fsum, fmin, fmax, csum, cmin, cmax, cc; \
|
||||
\
|
||||
spin_lock_irqsave(&lat->lock, flags); \
|
||||
fsum = lat->_name.fabric.sum * adapter->timer_ticks; \
|
||||
fmin = lat->_name.fabric.min * adapter->timer_ticks; \
|
||||
fmax = lat->_name.fabric.max * adapter->timer_ticks; \
|
||||
csum = lat->_name.channel.sum * adapter->timer_ticks; \
|
||||
cmin = lat->_name.channel.min * adapter->timer_ticks; \
|
||||
cmax = lat->_name.channel.max * adapter->timer_ticks; \
|
||||
cc = lat->_name.counter; \
|
||||
spin_unlock_irqrestore(&lat->lock, flags); \
|
||||
\
|
||||
do_div(fsum, 1000); \
|
||||
do_div(fmin, 1000); \
|
||||
do_div(fmax, 1000); \
|
||||
do_div(csum, 1000); \
|
||||
do_div(cmin, 1000); \
|
||||
do_div(cmax, 1000); \
|
||||
\
|
||||
return sprintf(buf, "%llu %llu %llu %llu %llu %llu %llu\n", \
|
||||
fmin, fmax, fsum, cmin, cmax, csum, cc); \
|
||||
} \
|
||||
static ssize_t \
|
||||
zfcp_sysfs_unit_##_name##_latency_store(struct device *dev, \
|
||||
struct device_attribute *attr, \
|
||||
const char *buf, size_t count) \
|
||||
{ \
|
||||
struct scsi_device *sdev = to_scsi_device(dev); \
|
||||
struct zfcp_unit *unit = sdev->hostdata; \
|
||||
struct zfcp_latencies *lat = &unit->latencies; \
|
||||
unsigned long flags; \
|
||||
\
|
||||
spin_lock_irqsave(&lat->lock, flags); \
|
||||
lat->_name.fabric.sum = 0; \
|
||||
lat->_name.fabric.min = 0xFFFFFFFF; \
|
||||
lat->_name.fabric.max = 0; \
|
||||
lat->_name.channel.sum = 0; \
|
||||
lat->_name.channel.min = 0xFFFFFFFF; \
|
||||
lat->_name.channel.max = 0; \
|
||||
lat->_name.counter = 0; \
|
||||
spin_unlock_irqrestore(&lat->lock, flags); \
|
||||
\
|
||||
return (ssize_t) count; \
|
||||
} \
|
||||
static DEVICE_ATTR(_name##_latency, S_IWUSR | S_IRUGO, \
|
||||
zfcp_sysfs_unit_##_name##_latency_show, \
|
||||
zfcp_sysfs_unit_##_name##_latency_store);
|
||||
|
||||
ZFCP_DEFINE_LATENCY_ATTR(read);
|
||||
ZFCP_DEFINE_LATENCY_ATTR(write);
|
||||
ZFCP_DEFINE_LATENCY_ATTR(cmd);
|
||||
|
||||
#define ZFCP_DEFINE_SCSI_ATTR(_name, _format, _value) \
|
||||
static ssize_t zfcp_sysfs_scsi_##_name##_show(struct device *dev, \
|
||||
struct device_attribute *attr,\
|
||||
char *buf) \
|
||||
{ \
|
||||
struct scsi_device *sdev = to_scsi_device(dev); \
|
||||
struct zfcp_unit *unit = sdev->hostdata; \
|
||||
\
|
||||
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",
|
||||
unit->port->adapter->ccw_device->dev.bus_id);
|
||||
ZFCP_DEFINE_SCSI_ATTR(wwpn, "0x%016llx\n", unit->port->wwpn);
|
||||
ZFCP_DEFINE_SCSI_ATTR(fcp_lun, "0x%016llx\n", unit->fcp_lun);
|
||||
|
||||
struct device_attribute *zfcp_sysfs_sdev_attrs[] = {
|
||||
&dev_attr_fcp_lun,
|
||||
&dev_attr_wwpn,
|
||||
&dev_attr_hba_id,
|
||||
&dev_attr_read_latency,
|
||||
&dev_attr_write_latency,
|
||||
&dev_attr_cmd_latency,
|
||||
NULL
|
||||
};
|
||||
|
||||
static ssize_t zfcp_sysfs_adapter_util_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct Scsi_Host *scsi_host = dev_to_shost(dev);
|
||||
struct fsf_qtcb_bottom_port *qtcb_port;
|
||||
struct zfcp_adapter *adapter;
|
||||
int retval;
|
||||
|
||||
adapter = (struct zfcp_adapter *) scsi_host->hostdata[0];
|
||||
if (!(adapter->adapter_features & FSF_FEATURE_MEASUREMENT_DATA))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
qtcb_port = kzalloc(sizeof(struct fsf_qtcb_bottom_port), GFP_KERNEL);
|
||||
if (!qtcb_port)
|
||||
return -ENOMEM;
|
||||
|
||||
retval = zfcp_fsf_exchange_port_data_sync(adapter, qtcb_port);
|
||||
if (!retval)
|
||||
retval = sprintf(buf, "%u %u %u\n", qtcb_port->cp_util,
|
||||
qtcb_port->cb_util, qtcb_port->a_util);
|
||||
kfree(qtcb_port);
|
||||
return retval;
|
||||
}
|
||||
static DEVICE_ATTR(utilization, S_IRUGO, zfcp_sysfs_adapter_util_show, NULL);
|
||||
|
||||
static int zfcp_sysfs_adapter_ex_config(struct device *dev,
|
||||
struct fsf_statistics_info *stat_inf)
|
||||
{
|
||||
struct Scsi_Host *scsi_host = dev_to_shost(dev);
|
||||
struct fsf_qtcb_bottom_config *qtcb_config;
|
||||
struct zfcp_adapter *adapter;
|
||||
int retval;
|
||||
|
||||
adapter = (struct zfcp_adapter *) scsi_host->hostdata[0];
|
||||
if (!(adapter->adapter_features & FSF_FEATURE_MEASUREMENT_DATA))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
qtcb_config = kzalloc(sizeof(struct fsf_qtcb_bottom_config),
|
||||
GFP_KERNEL);
|
||||
if (!qtcb_config)
|
||||
return -ENOMEM;
|
||||
|
||||
retval = zfcp_fsf_exchange_config_data_sync(adapter, qtcb_config);
|
||||
if (!retval)
|
||||
*stat_inf = qtcb_config->stat_info;
|
||||
|
||||
kfree(qtcb_config);
|
||||
return retval;
|
||||
}
|
||||
|
||||
#define ZFCP_SHOST_ATTR(_name, _format, _arg...) \
|
||||
static ssize_t zfcp_sysfs_adapter_##_name##_show(struct device *dev, \
|
||||
struct device_attribute *attr,\
|
||||
char *buf) \
|
||||
{ \
|
||||
struct fsf_statistics_info stat_info; \
|
||||
int retval; \
|
||||
\
|
||||
retval = zfcp_sysfs_adapter_ex_config(dev, &stat_info); \
|
||||
if (retval) \
|
||||
return retval; \
|
||||
\
|
||||
return sprintf(buf, _format, ## _arg); \
|
||||
} \
|
||||
static DEVICE_ATTR(_name, S_IRUGO, zfcp_sysfs_adapter_##_name##_show, NULL);
|
||||
|
||||
ZFCP_SHOST_ATTR(requests, "%llu %llu %llu\n",
|
||||
(unsigned long long) stat_info.input_req,
|
||||
(unsigned long long) stat_info.output_req,
|
||||
(unsigned long long) stat_info.control_req);
|
||||
|
||||
ZFCP_SHOST_ATTR(megabytes, "%llu %llu\n",
|
||||
(unsigned long long) stat_info.input_mb,
|
||||
(unsigned long long) stat_info.output_mb);
|
||||
|
||||
ZFCP_SHOST_ATTR(seconds_active, "%llu\n",
|
||||
(unsigned long long) stat_info.seconds_act);
|
||||
|
||||
struct device_attribute *zfcp_sysfs_shost_attrs[] = {
|
||||
&dev_attr_utilization,
|
||||
&dev_attr_requests,
|
||||
&dev_attr_megabytes,
|
||||
&dev_attr_seconds_active,
|
||||
NULL
|
||||
};
|
|
@ -1,270 +0,0 @@
|
|||
/*
|
||||
* This file is part of the zfcp device driver for
|
||||
* FCP adapters for IBM System z9 and zSeries.
|
||||
*
|
||||
* (C) Copyright IBM Corp. 2002, 2006
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "zfcp_ext.h"
|
||||
|
||||
#define ZFCP_LOG_AREA ZFCP_LOG_AREA_CONFIG
|
||||
|
||||
/**
|
||||
* ZFCP_DEFINE_ADAPTER_ATTR
|
||||
* @_name: name of show attribute
|
||||
* @_format: format string
|
||||
* @_value: value to print
|
||||
*
|
||||
* Generates attributes for an adapter.
|
||||
*/
|
||||
#define ZFCP_DEFINE_ADAPTER_ATTR(_name, _format, _value) \
|
||||
static ssize_t zfcp_sysfs_adapter_##_name##_show(struct device *dev, struct device_attribute *attr, \
|
||||
char *buf) \
|
||||
{ \
|
||||
struct zfcp_adapter *adapter; \
|
||||
\
|
||||
adapter = dev_get_drvdata(dev); \
|
||||
return sprintf(buf, _format, _value); \
|
||||
} \
|
||||
\
|
||||
static DEVICE_ATTR(_name, S_IRUGO, zfcp_sysfs_adapter_##_name##_show, NULL);
|
||||
|
||||
ZFCP_DEFINE_ADAPTER_ATTR(status, "0x%08x\n", atomic_read(&adapter->status));
|
||||
ZFCP_DEFINE_ADAPTER_ATTR(peer_wwnn, "0x%016llx\n", adapter->peer_wwnn);
|
||||
ZFCP_DEFINE_ADAPTER_ATTR(peer_wwpn, "0x%016llx\n", adapter->peer_wwpn);
|
||||
ZFCP_DEFINE_ADAPTER_ATTR(peer_d_id, "0x%06x\n", adapter->peer_d_id);
|
||||
ZFCP_DEFINE_ADAPTER_ATTR(card_version, "0x%04x\n", adapter->hydra_version);
|
||||
ZFCP_DEFINE_ADAPTER_ATTR(lic_version, "0x%08x\n", adapter->fsf_lic_version);
|
||||
ZFCP_DEFINE_ADAPTER_ATTR(hardware_version, "0x%08x\n",
|
||||
adapter->hardware_version);
|
||||
ZFCP_DEFINE_ADAPTER_ATTR(in_recovery, "%d\n", atomic_test_mask
|
||||
(ZFCP_STATUS_COMMON_ERP_INUSE, &adapter->status));
|
||||
|
||||
/**
|
||||
* zfcp_sysfs_port_add_store - add a port to sysfs tree
|
||||
* @dev: pointer to belonging device
|
||||
* @buf: pointer to input buffer
|
||||
* @count: number of bytes in buffer
|
||||
*
|
||||
* Store function of the "port_add" attribute of an adapter.
|
||||
*/
|
||||
static ssize_t
|
||||
zfcp_sysfs_port_add_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
wwn_t wwpn;
|
||||
char *endp;
|
||||
struct zfcp_adapter *adapter;
|
||||
struct zfcp_port *port;
|
||||
int retval = -EINVAL;
|
||||
|
||||
down(&zfcp_data.config_sema);
|
||||
|
||||
adapter = dev_get_drvdata(dev);
|
||||
if (atomic_test_mask(ZFCP_STATUS_COMMON_REMOVE, &adapter->status)) {
|
||||
retval = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
wwpn = simple_strtoull(buf, &endp, 0);
|
||||
if ((endp + 1) < (buf + count))
|
||||
goto out;
|
||||
|
||||
port = zfcp_port_enqueue(adapter, wwpn, 0, 0);
|
||||
if (!port)
|
||||
goto out;
|
||||
|
||||
retval = 0;
|
||||
|
||||
zfcp_erp_port_reopen(port, 0, 91, NULL);
|
||||
zfcp_erp_wait(port->adapter);
|
||||
zfcp_port_put(port);
|
||||
out:
|
||||
up(&zfcp_data.config_sema);
|
||||
return retval ? retval : (ssize_t) count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(port_add, S_IWUSR, NULL, zfcp_sysfs_port_add_store);
|
||||
|
||||
/**
|
||||
* zfcp_sysfs_port_remove_store - remove a port from sysfs tree
|
||||
* @dev: pointer to belonging device
|
||||
* @buf: pointer to input buffer
|
||||
* @count: number of bytes in buffer
|
||||
*
|
||||
* Store function of the "port_remove" attribute of an adapter.
|
||||
*/
|
||||
static ssize_t
|
||||
zfcp_sysfs_port_remove_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
struct zfcp_adapter *adapter;
|
||||
struct zfcp_port *port;
|
||||
wwn_t wwpn;
|
||||
char *endp;
|
||||
int retval = 0;
|
||||
|
||||
down(&zfcp_data.config_sema);
|
||||
|
||||
adapter = dev_get_drvdata(dev);
|
||||
if (atomic_test_mask(ZFCP_STATUS_COMMON_REMOVE, &adapter->status)) {
|
||||
retval = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
wwpn = simple_strtoull(buf, &endp, 0);
|
||||
if ((endp + 1) < (buf + count)) {
|
||||
retval = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
write_lock_irq(&zfcp_data.config_lock);
|
||||
port = zfcp_get_port_by_wwpn(adapter, wwpn);
|
||||
if (port && (atomic_read(&port->refcount) == 0)) {
|
||||
zfcp_port_get(port);
|
||||
atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, &port->status);
|
||||
list_move(&port->list, &adapter->port_remove_lh);
|
||||
}
|
||||
else {
|
||||
port = NULL;
|
||||
}
|
||||
write_unlock_irq(&zfcp_data.config_lock);
|
||||
|
||||
if (!port) {
|
||||
retval = -ENXIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
zfcp_erp_port_shutdown(port, 0, 92, NULL);
|
||||
zfcp_erp_wait(adapter);
|
||||
zfcp_port_put(port);
|
||||
zfcp_port_dequeue(port);
|
||||
out:
|
||||
up(&zfcp_data.config_sema);
|
||||
return retval ? retval : (ssize_t) count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(port_remove, S_IWUSR, NULL, zfcp_sysfs_port_remove_store);
|
||||
|
||||
/**
|
||||
* zfcp_sysfs_adapter_failed_store - failed state of adapter
|
||||
* @dev: pointer to belonging device
|
||||
* @buf: pointer to input buffer
|
||||
* @count: number of bytes in buffer
|
||||
*
|
||||
* Store function of the "failed" attribute of an adapter.
|
||||
* If a "0" gets written to "failed", error recovery will be
|
||||
* started for the belonging adapter.
|
||||
*/
|
||||
static ssize_t
|
||||
zfcp_sysfs_adapter_failed_store(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct zfcp_adapter *adapter;
|
||||
unsigned int val;
|
||||
char *endp;
|
||||
int retval = 0;
|
||||
|
||||
down(&zfcp_data.config_sema);
|
||||
|
||||
adapter = dev_get_drvdata(dev);
|
||||
if (atomic_test_mask(ZFCP_STATUS_COMMON_REMOVE, &adapter->status)) {
|
||||
retval = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
val = simple_strtoul(buf, &endp, 0);
|
||||
if (((endp + 1) < (buf + count)) || (val != 0)) {
|
||||
retval = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
zfcp_erp_modify_adapter_status(adapter, 44, NULL,
|
||||
ZFCP_STATUS_COMMON_RUNNING, ZFCP_SET);
|
||||
zfcp_erp_adapter_reopen(adapter, ZFCP_STATUS_COMMON_ERP_FAILED, 93,
|
||||
NULL);
|
||||
zfcp_erp_wait(adapter);
|
||||
out:
|
||||
up(&zfcp_data.config_sema);
|
||||
return retval ? retval : (ssize_t) count;
|
||||
}
|
||||
|
||||
/**
|
||||
* zfcp_sysfs_adapter_failed_show - failed state of adapter
|
||||
* @dev: pointer to belonging device
|
||||
* @buf: pointer to input buffer
|
||||
*
|
||||
* Show function of "failed" attribute of adapter. Will be
|
||||
* "0" if adapter is working, otherwise "1".
|
||||
*/
|
||||
static ssize_t
|
||||
zfcp_sysfs_adapter_failed_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct zfcp_adapter *adapter;
|
||||
|
||||
adapter = dev_get_drvdata(dev);
|
||||
if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_FAILED, &adapter->status))
|
||||
return sprintf(buf, "1\n");
|
||||
else
|
||||
return sprintf(buf, "0\n");
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(failed, S_IWUSR | S_IRUGO, zfcp_sysfs_adapter_failed_show,
|
||||
zfcp_sysfs_adapter_failed_store);
|
||||
|
||||
static struct attribute *zfcp_adapter_attrs[] = {
|
||||
&dev_attr_failed.attr,
|
||||
&dev_attr_in_recovery.attr,
|
||||
&dev_attr_port_remove.attr,
|
||||
&dev_attr_port_add.attr,
|
||||
&dev_attr_peer_wwnn.attr,
|
||||
&dev_attr_peer_wwpn.attr,
|
||||
&dev_attr_peer_d_id.attr,
|
||||
&dev_attr_card_version.attr,
|
||||
&dev_attr_lic_version.attr,
|
||||
&dev_attr_status.attr,
|
||||
&dev_attr_hardware_version.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static struct attribute_group zfcp_adapter_attr_group = {
|
||||
.attrs = zfcp_adapter_attrs,
|
||||
};
|
||||
|
||||
/**
|
||||
* zfcp_sysfs_create_adapter_files - create sysfs adapter files
|
||||
* @dev: pointer to belonging device
|
||||
*
|
||||
* Create all attributes of the sysfs representation of an adapter.
|
||||
*/
|
||||
int
|
||||
zfcp_sysfs_adapter_create_files(struct device *dev)
|
||||
{
|
||||
return sysfs_create_group(&dev->kobj, &zfcp_adapter_attr_group);
|
||||
}
|
||||
|
||||
/**
|
||||
* zfcp_sysfs_remove_adapter_files - remove sysfs adapter files
|
||||
* @dev: pointer to belonging device
|
||||
*
|
||||
* Remove all attributes of the sysfs representation of an adapter.
|
||||
*/
|
||||
void
|
||||
zfcp_sysfs_adapter_remove_files(struct device *dev)
|
||||
{
|
||||
sysfs_remove_group(&dev->kobj, &zfcp_adapter_attr_group);
|
||||
}
|
||||
|
||||
#undef ZFCP_LOG_AREA
|
|
@ -1,106 +0,0 @@
|
|||
/*
|
||||
* This file is part of the zfcp device driver for
|
||||
* FCP adapters for IBM System z9 and zSeries.
|
||||
*
|
||||
* (C) Copyright IBM Corp. 2002, 2006
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "zfcp_ext.h"
|
||||
|
||||
#define ZFCP_LOG_AREA ZFCP_LOG_AREA_CONFIG
|
||||
|
||||
/**
|
||||
* ZFCP_DEFINE_DRIVER_ATTR - define for all log |