Commit 89a93f2f authored by Linus Torvalds's avatar Linus Torvalds
Browse files

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
  ...
parents 260eddf4 fe9233fb
...@@ -56,19 +56,33 @@ Supported Cards/Chipsets ...@@ -56,19 +56,33 @@ Supported Cards/Chipsets
9005:0285:9005:02d1 Adaptec 5405 (Voodoo40) 9005:0285:9005:02d1 Adaptec 5405 (Voodoo40)
9005:0285:15d9:02d2 SMC AOC-USAS-S8i-LP 9005:0285:15d9:02d2 SMC AOC-USAS-S8i-LP
9005:0285:15d9:02d3 SMC AOC-USAS-S8iR-LP 9005:0285:15d9:02d3 SMC AOC-USAS-S8iR-LP
9005:0285:9005:02d4 Adaptec 2045 (Voodoo04 Lite) 9005:0285:9005:02d4 Adaptec ASR-2045 (Voodoo04 Lite)
9005:0285:9005:02d5 Adaptec 2405 (Voodoo40 Lite) 9005:0285:9005:02d5 Adaptec ASR-2405 (Voodoo40 Lite)
9005:0285:9005:02d6 Adaptec 2445 (Voodoo44 Lite) 9005:0285:9005:02d6 Adaptec ASR-2445 (Voodoo44 Lite)
9005:0285:9005:02d7 Adaptec 2805 (Voodoo80 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:0364 Adaptec 5400S (Mustang)
1011:0046:9005:0365 Adaptec 5400S (Mustang)
9005:0287:9005:0800 Adaptec Themisto (Jupiter) 9005:0287:9005:0800 Adaptec Themisto (Jupiter)
9005:0200:9005:0200 Adaptec Themisto (Jupiter) 9005:0200:9005:0200 Adaptec Themisto (Jupiter)
9005:0286:9005:0800 Adaptec Callisto (Jupiter) 9005:0286:9005:0800 Adaptec Callisto (Jupiter)
1011:0046:9005:1364 Dell PERC 2/QC (Quad Channel, Mustang) 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:0001:1028:0001 Dell PERC 2/Si (Iguana)
1028:0003:1028:0003 Dell PERC 3/Si (SlimFast) 1028:0003:1028:0003 Dell PERC 3/Si (SlimFast)
1028:0002:1028:0002 Dell PERC 3/Di (Opal) 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:00d1 Dell PERC 3/DiV (Viper)
1028:0002:1028:00d9 Dell PERC 3/DiL (Lexus) 1028:0002:1028:00d9 Dell PERC 3/DiL (Lexus)
1028:000a:1028:0106 Dell PERC 3/DiJ (Jaguar) 1028:000a:1028:0106 Dell PERC 3/DiJ (Jaguar)
......
...@@ -740,8 +740,13 @@ static int bsg_put_device(struct bsg_device *bd) ...@@ -740,8 +740,13 @@ static int bsg_put_device(struct bsg_device *bd)
mutex_lock(&bsg_mutex); mutex_lock(&bsg_mutex);
do_free = atomic_dec_and_test(&bd->ref_count); do_free = atomic_dec_and_test(&bd->ref_count);
if (!do_free) if (!do_free) {
mutex_unlock(&bsg_mutex);
goto out; goto out;
}
hlist_del(&bd->dev_list);
mutex_unlock(&bsg_mutex);
dprintk("%s: tearing down\n", bd->name); dprintk("%s: tearing down\n", bd->name);
...@@ -757,10 +762,8 @@ static int bsg_put_device(struct bsg_device *bd) ...@@ -757,10 +762,8 @@ static int bsg_put_device(struct bsg_device *bd)
*/ */
ret = bsg_complete_all_commands(bd); ret = bsg_complete_all_commands(bd);
hlist_del(&bd->dev_list);
kfree(bd); kfree(bd);
out: out:
mutex_unlock(&bsg_mutex);
kref_put(&q->bsg_dev.ref, bsg_kref_release_function); kref_put(&q->bsg_dev.ref, bsg_kref_release_function);
if (do_free) if (do_free)
blk_put_queue(q); blk_put_queue(q);
......
...@@ -71,6 +71,10 @@ ...@@ -71,6 +71,10 @@
#include "iscsi_iser.h" #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; static unsigned int iscsi_max_lun = 512;
module_param_named(max_lun, iscsi_max_lun, uint, S_IRUGO); module_param_named(max_lun, iscsi_max_lun, uint, S_IRUGO);
...@@ -91,7 +95,6 @@ iscsi_iser_recv(struct iscsi_conn *conn, ...@@ -91,7 +95,6 @@ iscsi_iser_recv(struct iscsi_conn *conn,
struct iscsi_hdr *hdr, char *rx_data, int rx_data_len) struct iscsi_hdr *hdr, char *rx_data, int rx_data_len)
{ {
int rc = 0; int rc = 0;
uint32_t ret_itt;
int datalen; int datalen;
int ahslen; int ahslen;
...@@ -107,12 +110,7 @@ iscsi_iser_recv(struct iscsi_conn *conn, ...@@ -107,12 +110,7 @@ iscsi_iser_recv(struct iscsi_conn *conn,
/* read AHS */ /* read AHS */
ahslen = hdr->hlength * 4; ahslen = hdr->hlength * 4;
/* verify itt (itt encoding: age+cid+itt) */ rc = iscsi_complete_pdu(conn, hdr, rx_data, rx_data_len);
rc = iscsi_verify_itt(conn, hdr, &ret_itt);
if (!rc)
rc = iscsi_complete_pdu(conn, hdr, rx_data, rx_data_len);
if (rc && rc != ISCSI_ERR_NO_SCSI_CMD) if (rc && rc != ISCSI_ERR_NO_SCSI_CMD)
goto error; goto error;
...@@ -123,25 +121,33 @@ iscsi_iser_recv(struct iscsi_conn *conn, ...@@ -123,25 +121,33 @@ iscsi_iser_recv(struct iscsi_conn *conn,
/** /**
* 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 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_conn *iser_conn = task->conn->dd_data;
struct iscsi_iser_cmd_task *iser_ctask = ctask->dd_data; struct iscsi_iser_task *iser_task = task->dd_data;
/* mgmt task */
if (!task->sc) {
iser_task->desc.data = task->data;
return 0;
}
iser_ctask->command_sent = 0; iser_task->command_sent = 0;
iser_ctask->iser_conn = iser_conn; iser_task->iser_conn = iser_conn;
iser_ctask_rdma_init(iser_ctask); iser_task_rdma_init(iser_task);
return 0; return 0;
} }
/** /**
* iscsi_mtask_xmit - xmit management(immediate) task * iscsi_iser_mtask_xmit - xmit management(immediate) task
* @conn: iscsi connection * @conn: iscsi connection
* @mtask: task management task * @task: task management task
* *
* Notes: * Notes:
* The function can return -EAGAIN in which case caller must * The function can return -EAGAIN in which case caller must
...@@ -150,20 +156,19 @@ iscsi_iser_cmd_init(struct iscsi_cmd_task *ctask) ...@@ -150,20 +156,19 @@ iscsi_iser_cmd_init(struct iscsi_cmd_task *ctask)
* *
**/ **/
static int static int
iscsi_iser_mtask_xmit(struct iscsi_conn *conn, iscsi_iser_mtask_xmit(struct iscsi_conn *conn, struct iscsi_task *task)
struct iscsi_mgmt_task *mtask)
{ {
int error = 0; 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. * right after sending them.
* The recycling scheme is based on whether a response is expected * The recycling scheme is based on whether a response is expected
* - if yes, the mtask is recycled at iscsi_complete_pdu * - if yes, the task is recycled at iscsi_complete_pdu
* - if no, the mtask is recycled at iser_snd_completion * - if no, the task is recycled at iser_snd_completion
*/ */
if (error && error != -ENOBUFS) if (error && error != -ENOBUFS)
iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED); iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
...@@ -172,97 +177,86 @@ iscsi_iser_mtask_xmit(struct iscsi_conn *conn, ...@@ -172,97 +177,86 @@ iscsi_iser_mtask_xmit(struct iscsi_conn *conn,
} }
static int static int
iscsi_iser_ctask_xmit_unsol_data(struct iscsi_conn *conn, iscsi_iser_task_xmit_unsol_data(struct iscsi_conn *conn,
struct iscsi_cmd_task *ctask) struct iscsi_task *task)
{ {
struct iscsi_data hdr; struct iscsi_data hdr;
int error = 0; int error = 0;
/* Send data-out PDUs while there's still unsolicited data to send */ /* Send data-out PDUs while there's still unsolicited data to send */
while (ctask->unsol_count > 0) { while (task->unsol_count > 0) {
iscsi_prep_unsolicit_data_pdu(ctask, &hdr); iscsi_prep_unsolicit_data_pdu(task, &hdr);
debug_scsi("Sending data-out: itt 0x%x, data count %d\n", 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 */ /* the buffer description has been passed with the command */
/* Send the command */ /* Send the command */
error = iser_send_data_out(conn, ctask, &hdr); error = iser_send_data_out(conn, task, &hdr);
if (error) { if (error) {
ctask->unsol_datasn--; task->unsol_datasn--;
goto iscsi_iser_ctask_xmit_unsol_data_exit; 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", 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; return error;
} }
static int static int
iscsi_iser_ctask_xmit(struct iscsi_conn *conn, iscsi_iser_task_xmit(struct iscsi_task *task)
struct iscsi_cmd_task *ctask)
{ {
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; int error = 0;
if (ctask->sc->sc_data_direction == DMA_TO_DEVICE) { if (!task->sc)
BUG_ON(scsi_bufflen(ctask->sc) == 0); 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", debug_scsi("cmd [itt %x total %d imm %d unsol_data %d\n",
ctask->itt, scsi_bufflen(ctask->sc), task->itt, scsi_bufflen(task->sc),
ctask->imm_count, ctask->unsol_count); task->imm_count, task->unsol_count);
} }
debug_scsi("ctask deq [cid %d itt 0x%x]\n", debug_scsi("task deq [cid %d itt 0x%x]\n",
conn->id, ctask->itt); conn->id, task->itt);
/* Send the cmd PDU */ /* Send the cmd PDU */
if (!iser_ctask->command_sent) { if (!iser_task->command_sent) {
error = iser_send_command(conn, ctask); error = iser_send_command(conn, task);
if (error) if (error)
goto iscsi_iser_ctask_xmit_exit; goto iscsi_iser_task_xmit_exit;
iser_ctask->command_sent = 1; iser_task->command_sent = 1;
} }
/* Send unsolicited data-out PDU(s) if necessary */ /* Send unsolicited data-out PDU(s) if necessary */
if (ctask->unsol_count) if (task->unsol_count)
error = iscsi_iser_ctask_xmit_unsol_data(conn, ctask); error = iscsi_iser_task_xmit_unsol_data(conn, task);
iscsi_iser_ctask_xmit_exit: iscsi_iser_task_xmit_exit:
if (error && error != -ENOBUFS) if (error && error != -ENOBUFS)
iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED); iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
return error; return error;
} }
static void 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) { /* mgmt tasks do not need special cleanup */
iser_ctask->status = ISER_TASK_STATUS_COMPLETED; if (!task->sc)
iser_ctask_rdma_finalize(iser_ctask); return;
}
}
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); if (iser_task->status == ISER_TASK_STATUS_STARTED) {
list_for_each_entry(ib_conn, &ig.connlist, conn_list) { iser_task->status = ISER_TASK_STATUS_COMPLETED;
if (ib_conn == uib_conn) { iser_task_rdma_finalize(iser_task);
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 * static struct iscsi_cls_conn *
...@@ -272,7 +266,7 @@ 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_cls_conn *cls_conn;
struct iscsi_iser_conn *iser_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) if (!cls_conn)
return NULL; return NULL;
conn = cls_conn->dd_data; conn = cls_conn->dd_data;
...@@ -283,21 +277,11 @@ iscsi_iser_conn_create(struct iscsi_cls_session *cls_session, uint32_t conn_idx) ...@@ -283,21 +277,11 @@ iscsi_iser_conn_create(struct iscsi_cls_session *cls_session, uint32_t conn_idx)
*/ */
conn->max_recv_dlength = 128; conn->max_recv_dlength = 128;
iser_conn = kzalloc(sizeof(*iser_conn), GFP_KERNEL); iser_conn = conn->dd_data;
if (!iser_conn)
goto conn_alloc_fail;
/* currently this is the only field which need to be initiated */
rwlock_init(&iser_conn->lock);
conn->dd_data = iser_conn; conn->dd_data = iser_conn;
iser_conn->iscsi_conn = conn; iser_conn->iscsi_conn = conn;
return cls_conn; return cls_conn;
conn_alloc_fail:
iscsi_conn_teardown(cls_conn);
return NULL;
} }
static void static void
...@@ -305,11 +289,18 @@ iscsi_iser_conn_destroy(struct iscsi_cls_conn *cls_conn) ...@@ -305,11 +289,18 @@ iscsi_iser_conn_destroy(struct iscsi_cls_conn *cls_conn)
{ {
struct iscsi_conn *conn = cls_conn->dd_data; struct iscsi_conn *conn = cls_conn->dd_data;
struct iscsi_iser_conn *iser_conn = 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); iscsi_conn_teardown(cls_conn);
if (iser_conn->ib_conn) /*
iser_conn->ib_conn->iser_conn = NULL; * Userspace will normally call the stop callback and
kfree(iser_conn); * 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 static int
...@@ -320,6 +311,7 @@ iscsi_iser_conn_bind(struct iscsi_cls_session *cls_session, ...@@ -320,6 +311,7 @@ iscsi_iser_conn_bind(struct iscsi_cls_session *cls_session,
struct iscsi_conn *conn = cls_conn->dd_data; struct iscsi_conn *conn = cls_conn->dd_data;
struct iscsi_iser_conn *iser_conn; struct iscsi_iser_conn *iser_conn;
struct iser_conn *ib_conn; struct iser_conn *ib_conn;
struct iscsi_endpoint *ep;
int error; int error;
error = iscsi_conn_bind(cls_session, cls_conn, is_leading); error = iscsi_conn_bind(cls_session, cls_conn, is_leading);
...@@ -328,12 +320,14 @@ iscsi_iser_conn_bind(struct iscsi_cls_session *cls_session, ...@@ -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 /* the transport ep handle comes from user space so it must be
* verified against the global ib connections list */ * verified against the global ib connections list */
ib_conn = iscsi_iser_ib_conn_lookup(transport_eph); ep = iscsi_lookup_endpoint(transport_eph);
if (!ib_conn) { if (!ep) {
iser_err("can't bind eph %llx\n", iser_err("can't bind eph %llx\n",
(unsigned long long)transport_eph); (unsigned long long)transport_eph);
return -EINVAL; return -EINVAL;
} }
ib_conn = ep->dd_data;
/* binds the iSER connection retrieved from the previously /* binds the iSER connection retrieved from the previously
* connected ep_handle to the iSCSI layer connection. exchanges * connected ep_handle to the iSCSI layer connection. exchanges
* connection pointers */ * connection pointers */
...@@ -341,10 +335,30 @@ iscsi_iser_conn_bind(struct iscsi_cls_session *cls_session, ...@@ -341,10 +335,30 @@ iscsi_iser_conn_bind(struct iscsi_cls_session *cls_session,
iser_conn = conn->dd_data; iser_conn = conn->dd_data;
ib_conn->iser_conn = iser_conn; ib_conn->iser_conn = iser_conn;
iser_conn->ib_conn = ib_conn; iser_conn->ib_conn = ib_conn;
iser_conn_get(ib_conn);
return 0;
}
conn->recv_lock = &iser_conn->lock; 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;
return 0; /*
* 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 static int
...@@ -360,55 +374,75 @@ 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); 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 * static struct iscsi_cls_session *
iscsi_iser_session_create(struct iscsi_transport *iscsit, iscsi_iser_session_create(struct iscsi_endpoint *ep,
struct scsi_transport_template *scsit, uint16_t cmds_max, uint16_t qdepth,
uint16_t cmds_max, uint16_t qdepth, uint32_t initial_cmdsn, uint32_t *hostno)
uint32_t initial_cmdsn, uint32_t *hostno)
{ {
struct iscsi_cls_session *cls_session; struct iscsi_cls_session *cls_session;
struct iscsi_session *session; struct iscsi_session *session;
struct Scsi_Host *shost;
int i; int i;
uint32_t hn; struct iscsi_task *task;
struct iscsi_cmd_task *ctask; struct iscsi_iser_task *iser_task;
struct iscsi_mgmt_task *mtask; struct iser_conn *ib_conn;
struct iscsi_iser_cmd_task *iser_ctask;
struct iser_desc *desc; 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 * we do not support setting can_queue cmd_per_lun from userspace yet
* because we preallocate so many resources * 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_DEF_XMIT_CMDS_MAX,
ISCSI_MAX_CMD_PER_LUN, sizeof(struct iscsi_iser_task),
sizeof(struct iscsi_iser_cmd_task), initial_cmdsn, 0);
sizeof(struct iser_desc),
initial_cmdsn, &hn);
if (!cls_session) if (!cls_session)
return NULL; goto remove_host;
session = cls_session->dd_data;
*hostno = hn;
session = class_to_transport_session(cls_session);
shost->can_queue = session->scsi_cmds_max;
/* libiscsi setup itts, data and pool so just set desc fields */ /* libiscsi setup itts, data and pool so just set desc fields */
for (i = 0; i < session->cmds_max; i++) { for (i = 0; i < session->cmds_max; i++) {
ctask = session->cmds[i]; task = session->cmds[i];
iser_ctask = ctask->dd_data; iser_task = task->dd_data;
ctask->hdr = (struct iscsi_cmd *)&iser_ctask->desc.iscsi_header; task->hdr = (struct iscsi_cmd *)&iser_task->desc.iscsi_header;
ctask->hdr_max = sizeof(iser_ctask->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; return cls_session;
remove_host:
iscsi_host_remove(shost);
free_host:
iscsi_host_free(shost);
return NULL;
} }
static int static int
...@@ -481,34 +515,37 @@ iscsi_iser_conn_get_stats(struct iscsi_cls_conn *cls_conn, struct iscsi_stats *s ...@@ -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; stats->custom[3].value = conn->fmr_unalign_cnt;
} }
static int static struct iscsi_endpoint *
iscsi_iser_ep_connect(struct sockaddr *dst_addr, int non_blocking, iscsi_iser_ep_connect(struct sockaddr *dst_addr, int non_blocking)
__u64 *ep_handle)
{ {
int err; int err;
struct iser_conn *ib_conn; struct iser_conn *ib_conn;
struct iscsi_endpoint *ep;
err = iser_conn_init(&ib_conn); ep = iscsi_create_endpoint(sizeof(*ib_conn));
if (err) if (!ep)
goto out; return ERR_PTR(-ENOMEM);
err = iser_connect(ib_conn, NULL, (struct sockaddr_in *)dst_addr, non_blocking); ib_conn = ep->dd_data;
if (!err) ib_conn->ep = ep;
*ep_handle = (__u64)(unsigned long)ib_conn; iser_conn_init(ib_conn);
out: err = iser_connect(ib_conn, NULL, (struct sockaddr_in *)dst_addr,
return err; non_blocking);
if (err) {
iscsi_destroy_endpoint(ep);
return ERR_PTR(err);
}
return ep;
} }
static int 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; int rc;
if (!ib_conn) ib_conn = ep->dd_data;
return -EINVAL;
rc = wait_event_interruptible_timeout(ib_conn->wait, rc = wait_event_interruptible_timeout(ib_conn->wait,
ib_conn->state == ISER_CONN_UP, ib_conn->state == ISER_CONN_UP,
msecs_to_jiffies(timeout_ms)); msecs_to_jiffies(timeout_ms));
...@@ -530,13 +567,21 @@ iscsi_iser_ep_poll(__u64 ep_handle, int timeout_ms) ...@@ -530,13 +567,21 @@ iscsi_iser_ep_poll(__u64 ep_handle, int timeout_ms)
} }
static void static void
iscsi_iser_ep_disconnect(__u64 ep_handle) iscsi_iser_ep_disconnect(struct iscsi_endpoint *ep)
{ {
struct iser_conn *ib_conn; struct iser_conn *ib_conn;
ib_conn = iscsi_iser_ib_conn_lookup(ep_handle); ib_conn = ep->dd_data;
if (!ib_conn) if (ib_conn->iser_conn)
return; /*
* 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_err("ib conn %p state %d\n",ib_conn, ib_conn->state);
iser_conn_terminate(ib_conn); iser_conn_terminate(ib_conn);
...@@ -547,7 +592,6 @@ static struct scsi_host_template iscsi_iser_sht = { ...@@ -547,7 +592,6 @@ static struct scsi_host_template iscsi_iser_sht = {
.name = "iSCSI Initiator over iSER, v." DRV_VER, .name = "iSCSI Initiator over iSER, v." DRV_VER,
.queuecommand = iscsi_queuecommand, .queuecommand = iscsi_queuecommand,
.change_queue_depth = iscsi_change_queue_depth, .change_queue_depth = iscsi_change_queue_depth,
.can_queue = ISCSI_DEF_XMIT_CMDS_MAX - 1,
.sg_tablesize = ISCSI_ISER_SG_TABLESIZE, .sg_tablesize = ISCSI_ISER_SG_TABLESIZE,
.max_sectors = 1024, .max_sectors = 1024,
.cmd_per_lun = ISCSI_MAX_CMD_PER_LUN, .cmd_per_lun = ISCSI_MAX_CMD_PER_LUN,
...@@ -581,17 +625,14 @@ static struct iscsi_transport iscsi_iser_transport = { ...@@ -581,17 +625,14 @@ static struct iscsi_transport iscsi_iser_transport = {
ISCSI_USERNAME | ISCSI_PASSWORD | ISCSI_USERNAME | ISCSI_PASSWORD |
ISCSI_USERNAME_IN | ISCSI_PASSWORD_IN | ISCSI_USERNAME_IN | ISCSI_PASSWORD_IN |
ISCSI_FAST_ABORT | ISCSI_ABORT_TMO | 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 | .host_param_mask = ISCSI_HOST_HWADDRESS |
ISCSI_HOST_NETDEV_NAME | ISCSI_HOST_NETDEV_NAME |
ISCSI_HOST_INITIATOR_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 */ /* session management */
.create_session = iscsi_iser_session_create, .create_session = iscsi_iser_session_create,
.destroy_session = iscsi_session_teardown, .destroy_session = iscsi_iser_session_destroy,
/* connection management */ /* connection management */
.create_conn = iscsi_iser_conn_create, .create_conn = iscsi_iser_conn_create,
.bind_conn = iscsi_iser_conn_bind, .bind_conn = iscsi_iser_conn_bind,
...@@ -600,17 +641,16 @@ static struct iscsi_transport iscsi_iser_transport = { ...@@ -600,17 +641,16 @@ static struct iscsi_transport iscsi_iser_transport = {
.get_conn_param = iscsi_conn_get_param, .get_conn_param = iscsi_conn_get_param,
.get_session_param = iscsi_session_get_param, .get_session_param = iscsi_session_get_param,
.start_conn = iscsi_iser_conn_start, .start_conn = iscsi_iser_conn_start,
.stop_conn = iscsi_conn_stop, .stop_conn = iscsi_iser_conn_stop,
/* iscsi host params */ /* iscsi host params */
.get_host_param = iscsi_host_get_param, .get_host_param = iscsi_host_get_param,
.set_host_param = iscsi_host_set_param, .set_host_param = iscsi_host_set_param,
/* IO */ /* IO */
.send_pdu = iscsi_conn_send_pdu, .send_pdu = iscsi_conn_send_pdu,
.get_stats = iscsi_iser_conn_get_stats, .get_stats = iscsi_iser_conn_get_stats,
.init_cmd_task = iscsi_iser_cmd_init, .init_task = iscsi_iser_task_init,
.xmit_cmd_task = iscsi_iser_ctask_xmit, .xmit_task = iscsi_iser_task_xmit,
.xmit_mgmt_task = iscsi_iser_mtask_xmit, .cleanup_task = iscsi_iser_cleanup_task,
.cleanup_cmd_task = iscsi_iser_cleanup_ctask,
/* recovery */ /* recovery */
.session_recovery_timedout = iscsi_session_recovery_timedout, .session_recovery_timedout = iscsi_session_recovery_timedout,
...@@ -630,8 +670,6 @@ static int __init iser_init(void) ...@@ -630,8 +670,6 @@ static int __init iser_init(void)
return -EINVAL; return -EINVAL;
} }
iscsi_iser_transport.max_lun = iscsi_max_lun;
memset(&ig, 0, sizeof(struct iser_global)); memset(&ig, 0, sizeof(struct iser_global));
ig.desc_cache = kmem_cache_create("iser_descriptors", ig.desc_cache = kmem_cache_create("iser_descriptors",
...@@ -647,7 +685,9 @@ static int __init iser_init(void) ...@@ -647,7 +685,9 @@ static int __init iser_init(void)
mutex_init(&ig.connlist_mutex); mutex_init(&ig.connlist_mutex);
INIT_LIST_HEAD(&ig.connlist); 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"); iser_err("iscsi_register_transport failed\n");
err = -EINVAL; err = -EINVAL;
goto register_transport_failure; goto register_transport_failure;
......
...@@ -94,7 +94,6 @@ ...@@ -94,7 +94,6 @@
/* support upto 512KB in one RDMA */ /* support upto 512KB in one RDMA */
#define ISCSI_ISER_SG_TABLESIZE (0x80000 >> SHIFT_4K) #define ISCSI_ISER_SG_TABLESIZE (0x80000 >> SHIFT_4K)
#define ISCSI_ISER_MAX_LUN 256 #define ISCSI_ISER_MAX_LUN 256
#define ISCSI_ISER_MAX_CMD_LEN 16
/* QP settings */ /* QP settings */
/* Maximal bounds on received asynchronous PDUs */ /* Maximal bounds on received asynchronous PDUs */
...@@ -172,7 +171,8 @@ struct iser_data_buf { ...@@ -172,7 +171,8 @@ struct iser_data_buf {
/* fwd declarations */ /* fwd declarations */
struct iser_device; struct iser_device;
struct iscsi_iser_conn; struct iscsi_iser_conn;
struct iscsi_iser_cmd_task; struct iscsi_iser_task;
struct iscsi_endpoint;
struct iser_mem_reg { struct iser_mem_reg {
u32 lkey; u32 lkey;
...@@ -196,7 +196,7 @@ struct iser_regd_buf { ...@@ -196,7 +196,7 @@ struct iser_regd_buf {
#define MAX_REGD_BUF_VECTOR_LEN 2 #define MAX_REGD_BUF_VECTOR_LEN 2
struct iser_dto { struct iser_dto {
struct iscsi_iser_cmd_task *ctask; struct iscsi_iser_task *task;
struct iser_conn *ib_conn; struct iser_conn *ib_conn;
int notify_enable; int notify_enable;
...@@ -240,7 +240,9 @@ struct iser_device { ...@@ -240,7 +240,9 @@ struct iser_device {
struct iser_conn { struct iser_conn {
struct iscsi_iser_conn *iser_conn; /* iser conn for upcalls */ struct iscsi_iser_conn *iser_conn; /* iser conn for upcalls */
struct iscsi_endpoint *ep;
enum iser_ib_conn_state state; /* rdma connection state */ enum iser_ib_conn_state state; /* rdma connection state */
atomic_t refcount;
spinlock_t lock; /* used for state changes */ spinlock_t lock; /* used for state changes */
struct iser_device *device; /* device context */ struct iser_device *device; /* device context */
struct rdma_cm_id *cma_id; /* CMA ID */ struct rdma_cm_id *cma_id; /* CMA ID */
...@@ -259,11 +261,9 @@ struct iser_conn { ...@@ -259,11 +261,9 @@ struct iser_conn {
struct iscsi_iser_conn { struct iscsi_iser_conn {
struct iscsi_conn *iscsi_conn;/* ptr to iscsi conn */ struct iscsi_conn *iscsi_conn;/* ptr to iscsi conn */
struct iser_conn *ib_conn; /* iSER IB 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 iser_desc desc;
struct iscsi_iser_conn *iser_conn; struct iscsi_iser_conn *iser_conn;
enum iser_task_status status; enum iser_task_status status;
...@@ -296,22 +296,26 @@ extern int iser_debug_level; ...@@ -296,22 +296,26 @@ extern int iser_debug_level;
/* allocate connection resources needed for rdma functionality */ /* allocate connection resources needed for rdma functionality */
int iser_conn_set_full_featured_mode(struct iscsi_conn *conn); int iser_conn_set_full_featured_mode(struct iscsi_conn *conn);
int iser_send_control(struct iscsi_conn *conn, int iser_send_control(struct iscsi_conn *conn,
struct iscsi_mgmt_task *mtask); struct iscsi_task *task);
int iser_send_command(struct iscsi_conn *conn, int iser_send_command(struct iscsi_conn *conn,
struct iscsi_cmd_task *ctask); struct iscsi_task *task);
int iser_send_data_out(struct iscsi_conn *conn, int iser_send_data_out(struct iscsi_conn *conn,
struct iscsi_cmd_task *ctask, struct iscsi_task *task,
struct iscsi_data *hdr); struct iscsi_data *hdr);
void iscsi_iser_recv(struct iscsi_conn *conn, void iscsi_iser_recv(struct iscsi_conn *conn,
struct iscsi_hdr *hdr, struct iscsi_hdr *hdr,
char *rx_data, char *rx_data,
int rx_data_len); 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); void iser_conn_terminate(struct iser_conn *ib_conn);
...@@ -320,9 +324,9 @@ void iser_rcv_completion(struct iser_desc *desc, ...@@ -320,9 +324,9 @@ void iser_rcv_completion(struct iser_desc *desc,
void iser_snd_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); void iser_dto_buffs_release(struct iser_dto *dto);
...@@ -332,10 +336,10 @@ void iser_reg_single(struct iser_device *device, ...@@ -332,10 +336,10 @@ void iser_reg_single(struct iser_device *device,
struct iser_regd_buf *regd_buf, struct iser_regd_buf *regd_buf,
enum dma_data_direction direction); 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); 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); enum iser_data_dir cmd_dir);
int iser_connect(struct iser_conn *ib_conn, int iser_connect(struct iser_conn *ib_conn,
...@@ -355,10 +359,10 @@ int iser_post_send(struct iser_desc *tx_desc); ...@@ -355,10 +359,10 @@ int iser_post_send(struct iser_desc *tx_desc);
int iser_conn_state_comp(struct iser_conn *ib_conn, int iser_conn_state_comp(struct iser_conn *ib_conn,
enum iser_ib_conn_state comp); 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, struct iser_data_buf *data,
enum iser_data_dir iser_dir, enum iser_data_dir iser_dir,
enum dma_data_direction dma_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 #endif
...@@ -64,46 +64,46 @@ static void iser_dto_add_regd_buff(struct iser_dto *dto, ...@@ -64,46 +64,46 @@ static void iser_dto_add_regd_buff(struct iser_dto *dto,
/* Register user buffer memory and initialize passive rdma /* Register user buffer memory and initialize passive rdma
* dto descriptor. Total data size is stored in * 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) 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; struct iser_regd_buf *regd_buf;
int err; int err;
struct iser_hdr *hdr = &iser_ctask->desc.iser_header; struct iser_hdr *hdr = &iser_task->desc.iser_header;
struct iser_data_buf *buf_in = &iser_ctask->data[ISER_DIR_IN]; 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, buf_in,
ISER_DIR_IN, ISER_DIR_IN,
DMA_FROM_DEVICE); DMA_FROM_DEVICE);
if (err) if (err)
return 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: " iser_err("Total data length: %ld, less than EDTL: "
"%d, in READ cmd BHS itt: %d, conn: 0x%p\n", "%d, in READ cmd BHS itt: %d, conn: 0x%p\n",
iser_ctask->data[ISER_DIR_IN].data_len, edtl, iser_task->data[ISER_DIR_IN].data_len, edtl,
ctask->itt, iser_ctask->iser_conn); task->itt, iser_task->iser_conn);
return -EINVAL; return -EINVAL;
} }
err = iser_reg_rdma_mem(iser_ctask,ISER_DIR_IN); err = iser_reg_rdma_mem(iser_task,ISER_DIR_IN);
if (err) { if (err) {
iser_err("Failed to set up Data-IN RDMA\n"); iser_err("Failed to set up Data-IN RDMA\n");
return err; 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->flags |= ISER_RSV;
hdr->read_stag = cpu_to_be32(regd_buf->reg.rkey); hdr->read_stag = cpu_to_be32(regd_buf->reg.rkey);
hdr->read_va = cpu_to_be64(regd_buf->reg.va); hdr->read_va = cpu_to_be64(regd_buf->reg.va);
iser_dbg("Cmd itt:%d READ tags RKEY:%#.4X VA:%#llX\n", 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); (unsigned long long)regd_buf->reg.va);
return 0; return 0;
...@@ -111,43 +111,43 @@ static int iser_prepare_read_cmd(struct iscsi_cmd_task *ctask, ...@@ -111,43 +111,43 @@ static int iser_prepare_read_cmd(struct iscsi_cmd_task *ctask,
/* Register user buffer memory and initialize passive rdma /* Register user buffer memory and initialize passive rdma
* dto descriptor. Total data size is stored in * dto descriptor. Total data size is stored in
* ctask->data[ISER_DIR_OUT].data_len * task->data[ISER_DIR_OUT].data_len
*/ */
static int 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 imm_sz,
unsigned int unsol_sz, unsigned int unsol_sz,
unsigned int edtl) 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; struct iser_regd_buf *regd_buf;
int err; int err;
struct iser_dto *send_dto = &iser_ctask->desc.dto; struct iser_dto *send_dto = &iser_task->desc.dto;
struct iser_hdr *hdr = &iser_ctask->desc.iser_header; struct iser_hdr *hdr = &iser_task->desc.iser_header;
struct iser_data_buf *buf_out = &iser_ctask->data[ISER_DIR_OUT]; 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, buf_out,
ISER_DIR_OUT, ISER_DIR_OUT,
DMA_TO_DEVICE); DMA_TO_DEVICE);
if (err) if (err)
return 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, " iser_err("Total data length: %ld, less than EDTL: %d, "
"in WRITE cmd BHS itt: %d, conn: 0x%p\n", "in WRITE cmd BHS itt: %d, conn: 0x%p\n",
iser_ctask->data[ISER_DIR_OUT].data_len, iser_task->data[ISER_DIR_OUT].data_len,
edtl, ctask->itt, ctask->conn); edtl, task->itt, task->conn);
return -EINVAL; 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) { if (err != 0) {
iser_err("Failed to register write cmd RDMA mem\n"); iser_err("Failed to register write cmd RDMA mem\n");
return err; return err;
} }
regd_buf = &iser_ctask->rdma_regd[ISER_DIR_OUT]; regd_buf = &iser_task->rdma_regd[ISER_DIR_OUT];
if (unsol_sz < edtl) { if (unsol_sz < edtl) {
hdr->flags |= ISER_WSV; hdr->flags |= ISER_WSV;
...@@ -156,13 +156,13 @@ iser_prepare_write_cmd(struct iscsi_cmd_task *ctask, ...@@ -156,13 +156,13 @@ iser_prepare_write_cmd(struct iscsi_cmd_task *ctask,
iser_dbg("Cmd itt:%d, WRITE tags, RKEY:%#.4X " iser_dbg("Cmd itt:%d, WRITE tags, RKEY:%#.4X "
"VA:%#llX + unsol:%d\n", "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); (unsigned long long)regd_buf->reg.va, unsol_sz);
} }
if (imm_sz > 0) { if (imm_sz > 0) {
iser_dbg("Cmd itt:%d, WRITE, adding imm.data sz: %d\n", 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, iser_dto_add_regd_buff(send_dto,
regd_buf, regd_buf,
0, 0,
...@@ -314,38 +314,38 @@ iser_check_xmit(struct iscsi_conn *conn, void *task) ...@@ -314,38 +314,38 @@ iser_check_xmit(struct iscsi_conn *conn, void *task)
/** /**
* iser_send_command - send command PDU * iser_send_command - send command PDU
*/ */
int iser_send_command(struct iscsi_conn *conn, int iser_send_command(struct iscsi_conn *conn,
struct iscsi_cmd_task *ctask) struct iscsi_task *task)
{ {
struct iscsi_iser_conn *iser_conn = conn->dd_data; 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; struct iser_dto *send_dto = NULL;
unsigned long edtl; unsigned long edtl;
int err = 0; int err = 0;
struct iser_data_buf *data_buf; struct iser_data_buf *data_buf;
struct iscsi_cmd *hdr = ctask->hdr; struct iscsi_cmd *hdr = task->hdr;
struct scsi_cmnd *sc = ctask->sc; struct scsi_cmnd *sc = task->sc;
if (!iser_conn_state_comp(iser_conn->ib_conn, ISER_CONN_UP)) { 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); iser_err("Failed to send, conn: 0x%p is not up\n", iser_conn->ib_conn);
return -EPERM; return -EPERM;
} }
if (iser_check_xmit(conn, ctask)) if (iser_check_xmit(conn, task))
return -ENOBUFS; return -ENOBUFS;
edtl = ntohl(hdr->data_length); edtl = ntohl(hdr->data_length);
/* build the tx desc regd header and add it to the tx desc dto */ /* build the tx desc regd header and add it to the tx desc dto */
iser_ctask->desc.type = ISCSI_TX_SCSI_COMMAND; iser_task->desc.type = ISCSI_TX_SCSI_COMMAND;
send_dto = &iser_ctask->desc.dto; send_dto = &iser_task->desc.dto;
send_dto->ctask = iser_ctask; send_dto->task = iser_task;
iser_create_send_desc(iser_conn, &iser_ctask->desc); iser_create_send_desc(iser_conn, &iser_task->desc);
if (hdr->flags & ISCSI_FLAG_CMD_READ) if (hdr->flags & ISCSI_FLAG_CMD_READ)
data_buf = &iser_ctask->data[ISER_DIR_IN]; data_buf = &iser_task->data[ISER_DIR_IN];
else 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 */ if (scsi_sg_count(sc)) { /* using a scatter list */
data_buf->buf = scsi_sglist(sc); data_buf->buf = scsi_sglist(sc);
...@@ -355,15 +355,15 @@ int iser_send_command(struct iscsi_conn *conn, ...@@ -355,15 +355,15 @@ int iser_send_command(struct iscsi_conn *conn,
data_buf->data_len = scsi_bufflen(sc); data_buf->data_len = scsi_bufflen(sc);
if (hdr->flags & ISCSI_FLAG_CMD_READ) { if (hdr->flags & ISCSI_FLAG_CMD_READ) {
err = iser_prepare_read_cmd(ctask, edtl); err = iser_prepare_read_cmd(task, edtl);
if (err) if (err)
goto send_command_error; goto send_command_error;
} }
if (hdr->flags & ISCSI_FLAG_CMD_WRITE) { if (hdr->flags & ISCSI_FLAG_CMD_WRITE) {
err = iser_prepare_write_cmd(ctask, err = iser_prepare_write_cmd(task,
ctask->imm_count, task->imm_count,
ctask->imm_count + task->imm_count +
ctask->unsol_count, task->unsol_count,
edtl); edtl);
if (err) if (err)
goto send_command_error; goto send_command_error;
...@@ -378,27 +378,27 @@ int iser_send_command(struct iscsi_conn *conn, ...@@ -378,27 +378,27 @@ int iser_send_command(struct iscsi_conn *conn,
goto send_command_error; 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) if (!err)
return 0; return 0;
send_command_error: send_command_error:
iser_dto_buffs_release(send_dto); 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; return err;
} }
/** /**
* iser_send_data_out - send data out PDU * iser_send_data_out - send data out PDU
*/ */
int iser_send_data_out(struct iscsi_conn *conn, int iser_send_data_out(struct iscsi_conn *conn,
struct iscsi_cmd_task *ctask, struct iscsi_task *task,
struct iscsi_data *hdr) struct iscsi_data *hdr)
{ {
struct iscsi_iser_conn *iser_conn = conn->dd_data; 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_desc *tx_desc = NULL;
struct iser_dto *send_dto = NULL; struct iser_dto *send_dto = NULL;
unsigned long buf_offset; unsigned long buf_offset;
...@@ -411,7 +411,7 @@ int iser_send_data_out(struct iscsi_conn *conn, ...@@ -411,7 +411,7 @@ int iser_send_data_out(struct iscsi_conn *conn,
return -EPERM; return -EPERM;
} }
if (iser_check_xmit(conn, ctask)) if (iser_check_xmit(conn, task))
return -ENOBUFS; return -ENOBUFS;
itt = (__force uint32_t)hdr->itt; itt = (__force uint32_t)hdr->itt;
...@@ -432,7 +432,7 @@ int iser_send_data_out(struct iscsi_conn *conn, ...@@ -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 */ /* build the tx desc regd header and add it to the tx desc dto */
send_dto = &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_create_send_desc(iser_conn, tx_desc);
iser_reg_single(iser_conn->ib_conn->device, iser_reg_single(iser_conn->ib_conn->device,
...@@ -440,15 +440,15 @@ int iser_send_data_out(struct iscsi_conn *conn, ...@@ -440,15 +440,15 @@ int iser_send_data_out(struct iscsi_conn *conn,
/* all data was registered for RDMA, we can use the lkey */ /* all data was registered for RDMA, we can use the lkey */
iser_dto_add_regd_buff(send_dto, iser_dto_add_regd_buff(send_dto,
&iser_ctask->rdma_regd[ISER_DIR_OUT], &iser_task->rdma_regd[ISER_DIR_OUT],
buf_offset, buf_offset,
data_seg_len); 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 " iser_err("Offset:%ld & DSL:%ld in Data-Out "
"inconsistent with total len:%ld, itt:%d\n", "inconsistent with total len:%ld, itt:%d\n",
buf_offset, data_seg_len, 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; err = -EINVAL;
goto send_data_out_error; goto send_data_out_error;
} }
...@@ -468,10 +468,11 @@ int iser_send_data_out(struct iscsi_conn *conn, ...@@ -468,10 +468,11 @@ int iser_send_data_out(struct iscsi_conn *conn,
} }
int iser_send_control(struct iscsi_conn *conn, 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 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; struct iser_dto *send_dto = NULL;
unsigned long data_seg_len; unsigned long data_seg_len;
int err = 0; int err = 0;
...@@ -483,27 +484,27 @@ int iser_send_control(struct iscsi_conn *conn, ...@@ -483,27 +484,27 @@ int iser_send_control(struct iscsi_conn *conn,
return -EPERM; return -EPERM;
} }
if (iser_check_xmit(conn,mtask)) if (iser_check_xmit(conn, task))
return -ENOBUFS; return -ENOBUFS;
/* build the tx desc regd header and add it to the tx desc dto */ /* build the tx desc regd header and add it to the tx desc dto */
mdesc->type = ISCSI_TX_CONTROL; mdesc->type = ISCSI_TX_CONTROL;
send_dto = &mdesc->dto; send_dto = &mdesc->dto;
send_dto->ctask = NULL; send_dto->task = NULL;
iser_create_send_desc(iser_conn, mdesc); iser_create_send_desc(iser_conn, mdesc);
device = iser_conn->ib_conn->device; device = iser_conn->ib_conn->device;
iser_reg_single(device, send_dto->regd[0], DMA_TO_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) { if (data_seg_len > 0) {
regd_buf = &mdesc->data_regd_buf; regd_buf = &mdesc->data_regd_buf;
memset(regd_buf, 0, sizeof(struct iser_regd_buf)); memset(regd_buf, 0, sizeof(struct iser_regd_buf));
regd_buf->device = device; regd_buf->device = device;
regd_buf->virt_addr = mtask->data; regd_buf->virt_addr = task->data;
regd_buf->data_size = mtask->data_count; regd_buf->data_size = task->data_count;
iser_reg_single(device, regd_buf, iser_reg_single(device, regd_buf,
DMA_TO_DEVICE); DMA_TO_DEVICE);
iser_dto_add_regd_buff(send_dto, regd_buf, iser_dto_add_regd_buff(send_dto, regd_buf,
...@@ -533,15 +534,13 @@ int iser_send_control(struct iscsi_conn *conn, ...@@ -533,15 +534,13 @@ int iser_send_control(struct iscsi_conn *conn,
void iser_rcv_completion(struct iser_desc *rx_desc, void iser_rcv_completion(struct iser_desc *rx_desc,
unsigned long dto_xfer_len) 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_iser_conn *conn = dto->ib_conn->iser_conn;
struct iscsi_session *session = conn->iscsi_conn->session; struct iscsi_task *task;
struct iscsi_cmd_task *ctask; struct iscsi_iser_task *iser_task;
struct iscsi_iser_cmd_task *iser_ctask;
struct iscsi_hdr *hdr; struct iscsi_hdr *hdr;
char *rx_data = NULL; char *rx_data = NULL;
int rx_data_len = 0; int rx_data_len = 0;
unsigned int itt;
unsigned char opcode; unsigned char opcode;
hdr = &rx_desc->iscsi_header; hdr = &rx_desc->iscsi_header;
...@@ -557,19 +556,24 @@ void iser_rcv_completion(struct iser_desc *rx_desc, ...@@ -557,19 +556,24 @@ void iser_rcv_completion(struct iser_desc *rx_desc,
opcode = hdr->opcode & ISCSI_OPCODE_MASK; opcode = hdr->opcode & ISCSI_OPCODE_MASK;
if (opcode == ISCSI_OP_SCSI_CMD_RSP) { if (opcode == ISCSI_OP_SCSI_CMD_RSP) {
itt = get_itt(hdr->itt); /* mask out cid and age bits */ spin_lock(&conn->iscsi_conn->session->lock);
if (!(itt < session->cmds_max)) 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!!! " iser_err("itt can't be matched to task!!! "
"conn %p opcode %d cmds_max %d itt %d\n", "conn %p opcode %d itt %d\n",
conn->iscsi_conn,opcode,session->cmds_max,itt); conn->iscsi_conn, opcode, hdr->itt);
/* use the mapping given with the cmds array indexed by itt */ else {
ctask = (struct iscsi_cmd_task *)session->cmds[itt]; iser_task = task->dd_data;
iser_ctask = ctask->dd_data; iser_dbg("itt %d task %p\n",hdr->itt, task);
iser_dbg("itt %d ctask %p\n",itt,ctask); iser_task->status = ISER_TASK_STATUS_COMPLETED;
iser_ctask->status = ISER_TASK_STATUS_COMPLETED; iser_task_rdma_finalize(iser_task);
iser_ctask_rdma_finalize(iser_ctask); iscsi_put_task(task);
}
} }
iser_dto_buffs_release(dto); iser_dto_buffs_release(dto);
iscsi_iser_recv(conn->iscsi_conn, hdr, rx_data, rx_data_len); 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) ...@@ -590,7 +594,7 @@ void iser_snd_completion(struct iser_desc *tx_desc)
struct iser_conn *ib_conn = dto->ib_conn; struct iser_conn *ib_conn = dto->ib_conn;
struct iscsi_iser_conn *iser_conn = ib_conn->iser_conn; struct iscsi_iser_conn *iser_conn = ib_conn->iser_conn;
struct iscsi_conn *conn = iser_conn->iscsi_conn; struct iscsi_conn *conn = iser_conn->iscsi_conn;
struct iscsi_mgmt_task *mtask; struct iscsi_task *task;
int resume_tx = 0; int resume_tx = 0;
iser_dbg("Initiator, Data sent dto=0x%p\n", dto); iser_dbg("Initiator, Data sent dto=0x%p\n", dto);
...@@ -613,36 +617,31 @@ void iser_snd_completion(struct iser_desc *tx_desc) ...@@ -613,36 +617,31 @@ void iser_snd_completion(struct iser_desc *tx_desc)
if (tx_desc->type == ISCSI_TX_CONTROL) { if (tx_desc->type == ISCSI_TX_CONTROL) {
/* this arithmetic is legal by libiscsi dd_data allocation */ /* this arithmetic is legal by libiscsi dd_data allocation */
mtask = (void *) ((long)(void *)tx_desc - task = (void *) ((long)(void *)tx_desc -
sizeof(struct iscsi_mgmt_task)); sizeof(struct iscsi_task));
if (mtask->hdr->itt == RESERVED_ITT) { if (task->hdr->itt == RESERVED_ITT)
struct iscsi_session *session = conn->session; iscsi_put_task(task);
spin_lock(&conn->session->lock);
iscsi_free_mgmt_task(conn, mtask);
spin_unlock(&session->lock);
}
} }
} }
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_task->dir[ISER_DIR_IN] = 0;
iser_ctask->dir[ISER_DIR_OUT] = 0; iser_task->dir[ISER_DIR_OUT] = 0;
iser_ctask->data[ISER_DIR_IN].data_len = 0; iser_task->data[ISER_DIR_IN].data_len = 0;
iser_ctask->data[ISER_DIR_OUT].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)); 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)); 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 deferred;
int is_rdma_aligned = 1; int is_rdma_aligned = 1;
...@@ -651,17 +650,17 @@ void iser_ctask_rdma_finalize(struct iscsi_iser_cmd_task *iser_ctask) ...@@ -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, /* if we were reading, copy back to unaligned sglist,
* anyway dma_unmap and free the copy * 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; 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; 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]) { if (iser_task->dir[ISER_DIR_IN]) {
regd = &iser_ctask->rdma_regd[ISER_DIR_IN]; regd = &iser_task->rdma_regd[ISER_DIR_IN];
deferred = iser_regd_buff_release(regd); deferred = iser_regd_buff_release(regd);
if (deferred) { if (deferred) {
iser_err("%d references remain for BUF-IN rdma reg\n", 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) ...@@ -669,8 +668,8 @@ void iser_ctask_rdma_finalize(struct iscsi_iser_cmd_task *iser_ctask)
} }
} }
if (iser_ctask->dir[ISER_DIR_OUT]) { if (iser_task->dir[ISER_DIR_OUT]) {
regd = &iser_ctask->rdma_regd[ISER_DIR_OUT]; regd = &iser_task->rdma_regd[ISER_DIR_OUT];
deferred = iser_regd_buff_release(regd); deferred = iser_regd_buff_release(regd);
if (deferred) { if (deferred) {
iser_err("%d references remain for BUF-OUT rdma reg\n", 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) ...@@ -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 the data was unaligned, it was already unmapped and then copied */
if (is_rdma_aligned) 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) void iser_dto_buffs_release(struct iser_dto *dto)
......
...@@ -99,13 +99,13 @@ void iser_reg_single(struct iser_device *device, ...@@ -99,13 +99,13 @@ void iser_reg_single(struct iser_device *device,
/** /**
* iser_start_rdma_unaligned_sg * 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) enum iser_data_dir cmd_dir)
{ {
int dma_nents; int dma_nents;
struct ib_device *dev; struct ib_device *dev;
char *mem = NULL; 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; unsigned long cmd_data_len = data->data_len;
if (cmd_data_len > ISER_KMALLOC_THRESHOLD) 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, ...@@ -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); sg_init_one(&iser_task->data_copy[cmd_dir].sg_single, mem, cmd_data_len);
iser_ctask->data_copy[cmd_dir].buf = iser_task->data_copy[cmd_dir].buf =
&iser_ctask->data_copy[cmd_dir].sg_single; &iser_task->data_copy[cmd_dir].sg_single;
iser_ctask->data_copy[cmd_dir].size = 1; 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, dma_nents = ib_dma_map_sg(dev,
&iser_ctask->data_copy[cmd_dir].sg_single, &iser_task->data_copy[cmd_dir].sg_single,
1, 1,
(cmd_dir == ISER_DIR_OUT) ? (cmd_dir == ISER_DIR_OUT) ?
DMA_TO_DEVICE : DMA_FROM_DEVICE); DMA_TO_DEVICE : DMA_FROM_DEVICE);
BUG_ON(dma_nents == 0); 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; return 0;
} }
/** /**
* iser_finalize_rdma_unaligned_sg * 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) enum iser_data_dir cmd_dir)
{ {
struct ib_device *dev; struct ib_device *dev;
struct iser_data_buf *mem_copy; struct iser_data_buf *mem_copy;
unsigned long cmd_data_len; unsigned long cmd_data_len;
dev = iser_ctask->iser_conn->ib_conn->device->ib_device; dev = iser_task->iser_conn->ib_conn->device->ib_device;
mem_copy = &iser_ctask->data_copy[cmd_dir]; mem_copy = &iser_task->data_copy[cmd_dir];
ib_dma_unmap_sg(dev, &mem_copy->sg_single, 1, ib_dma_unmap_sg(dev, &mem_copy->sg_single, 1,
(cmd_dir == ISER_DIR_OUT) ? (cmd_dir == ISER_DIR_OUT) ?
...@@ -184,8 +184,8 @@ void iser_finalize_rdma_unaligned_sg(struct iscsi_iser_cmd_task *iser_ctask, ...@@ -184,8 +184,8 @@ void iser_finalize_rdma_unaligned_sg(struct iscsi_iser_cmd_task *iser_ctask,
/* copy back read RDMA to unaligned sg */ /* copy back read RDMA to unaligned sg */
mem = mem_copy->copy_buf; mem = mem_copy->copy_buf;
sgl = (struct scatterlist *)iser_ctask->data[ISER_DIR_IN].buf; sgl = (struct scatterlist *)iser_task->data[ISER_DIR_IN].buf;
sg_size = iser_ctask->data[ISER_DIR_IN].size; sg_size = iser_task->data[ISER_DIR_IN].size;
p = mem; p = mem;
for_each_sg(sgl, sg, sg_size, i) { 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, ...@@ -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) if (cmd_data_len > ISER_KMALLOC_THRESHOLD)
free_pages((unsigned long)mem_copy->copy_buf, free_pages((unsigned long)mem_copy->copy_buf,
...@@ -376,15 +376,15 @@ static void iser_page_vec_build(struct iser_data_buf *data, ...@@ -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, int iser_dma_map_task_data(struct iscsi_iser_task *iser_task,
struct iser_data_buf *data, struct iser_data_buf *data,
enum iser_data_dir iser_dir, enum iser_data_dir iser_dir,
enum dma_data_direction dma_dir) enum dma_data_direction dma_dir)
{ {
struct ib_device *dev; struct ib_device *dev;
iser_ctask->dir[iser_dir] = 1; iser_task->dir[iser_dir] = 1;
dev = iser_ctask->iser_conn->ib_conn->device->ib_device; dev = iser_task->iser_conn->ib_conn->device->ib_device;
data->dma_nents = ib_dma_map_sg(dev, data->buf, data->size, dma_dir); data->dma_nents = ib_dma_map_sg(dev, data->buf, data->size, dma_dir);
if (data->dma_nents == 0) { if (data->dma_nents == 0) {
...@@ -394,20 +394,20 @@ int iser_dma_map_task_data(struct iscsi_iser_cmd_task *iser_ctask, ...@@ -394,20 +394,20 @@ int iser_dma_map_task_data(struct iscsi_iser_cmd_task *iser_ctask,
return 0; 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 ib_device *dev;
struct iser_data_buf *data; 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]) { if (iser_task->dir[ISER_DIR_IN]) {
data = &iser_ctask->data[ISER_DIR_IN]; data = &iser_task->data[ISER_DIR_IN];
ib_dma_unmap_sg(dev, data->buf, data->size, DMA_FROM_DEVICE); ib_dma_unmap_sg(dev, data->buf, data->size, DMA_FROM_DEVICE);
} }
if (iser_ctask->dir[ISER_DIR_OUT]) { if (iser_task->dir[ISER_DIR_OUT]) {
data = &iser_ctask->data[ISER_DIR_OUT]; data = &iser_task->data[ISER_DIR_OUT];
ib_dma_unmap_sg(dev, data->buf, data->size, DMA_TO_DEVICE); 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) ...@@ -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 * 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) enum iser_data_dir cmd_dir)
{ {
struct iscsi_conn *iscsi_conn = iser_ctask->iser_conn->iscsi_conn; struct iscsi_conn *iscsi_conn = iser_task->iser_conn->iscsi_conn;
struct iser_conn *ib_conn = iser_ctask->iser_conn->ib_conn; struct iser_conn *ib_conn = iser_task->iser_conn->ib_conn;
struct iser_device *device = ib_conn->device; struct iser_device *device = ib_conn->device;
struct ib_device *ibdev = device->ib_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; struct iser_regd_buf *regd_buf;
int aligned_len; int aligned_len;
int err; int err;
int i; int i;
struct scatterlist *sg; 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); aligned_len = iser_data_buf_aligned_len(mem, ibdev);
if (aligned_len != mem->dma_nents) { if (aligned_len != mem->dma_nents) {
...@@ -442,13 +442,13 @@ int iser_reg_rdma_mem(struct iscsi_iser_cmd_task *iser_ctask, ...@@ -442,13 +442,13 @@ int iser_reg_rdma_mem(struct iscsi_iser_cmd_task *iser_ctask,
iser_data_buf_dump(mem, ibdev); iser_data_buf_dump(mem, ibdev);
/* unmap the command data before accessing it */ /* 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 */ /* allocate copy buf, if we are writing, copy the */
/* unaligned scatterlist, dma map the copy */ /* 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; 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 */ /* 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, ...@@ -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, &regd_buf->reg); err = iser_reg_page_vec(ib_conn, ib_conn->page_vec, &regd_buf->reg);
if (err) { if (err) {
iser_data_buf_dump(mem, ibdev); iser_data_buf_dump(mem, ibdev);
iser_err("mem->dma_nents = %d (dlength = 0x%x)\n", mem->dma_nents, iser_err("mem->dma_nents = %d (dlength = 0x%x)\n",
ntoh24(iser_ctask->desc.iscsi_header.dlength)); mem->dma_nents,
ntoh24(iser_task->desc.iscsi_header.dlength));
iser_err("page_vec: data_size = 0x%x, length = %d, offset = 0x%x\n", 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->data_size, ib_conn->page_vec->length,
ib_conn->page_vec->offset); ib_conn->page_vec->offset);
......
...@@ -323,7 +323,18 @@ static void iser_conn_release(struct iser_conn *ib_conn) ...@@ -323,7 +323,18 @@ static void iser_conn_release(struct iser_conn *ib_conn)
iser_device_try_release(device); iser_device_try_release(device);
if (ib_conn->iser_conn) if (ib_conn->iser_conn)
ib_conn->iser_conn->ib_conn = NULL; 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) ...@@ -347,7 +358,7 @@ void iser_conn_terminate(struct iser_conn *ib_conn)
wait_event_interruptible(ib_conn->wait, wait_event_interruptible(ib_conn->wait,
ib_conn->state == ISER_CONN_DOWN); 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) 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 ...@@ -481,24 +492,15 @@ static int iser_cma_handler(struct rdma_cm_id *cma_id, struct rdma_cm_event *eve
return ret; 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; ib_conn->state = ISER_CONN_INIT;
init_waitqueue_head(&ib_conn->wait); init_waitqueue_head(&ib_conn->wait);
atomic_set(&ib_conn->post_recv_buf_count, 0); atomic_set(&ib_conn->post_recv_buf_count, 0);
atomic_set(&ib_conn->post_send_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); INIT_LIST_HEAD(&ib_conn->conn_list);
spin_lock_init(&ib_conn->lock); spin_lock_init(&ib_conn->lock);
*ibconn = ib_conn;
return 0;
} }
/** /**
......
...@@ -252,27 +252,14 @@ config DM_ZERO ...@@ -252,27 +252,14 @@ config DM_ZERO
config DM_MULTIPATH config DM_MULTIPATH
tristate "Multipath target" tristate "Multipath target"
depends on BLK_DEV_DM 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--- ---help---
Allow volume managers to support multipath hardware. 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 config DM_DELAY
tristate "I/O delaying target (EXPERIMENTAL)" tristate "I/O delaying target (EXPERIMENTAL)"
depends on BLK_DEV_DM && EXPERIMENTAL depends on BLK_DEV_DM && EXPERIMENTAL
......
...@@ -4,11 +4,9 @@ ...@@ -4,11 +4,9 @@
dm-mod-objs := dm.o dm-table.o dm-target.o dm-linear.o dm-stripe.o \ 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-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-snapshot-objs := dm-snap.o dm-exception-store.o
dm-mirror-objs := dm-raid1.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 md-mod-objs := md.o bitmap.o
raid456-objs := raid5.o raid6algos.o raid6recov.o raid6tables.o \ raid456-objs := raid5.o raid6algos.o raid6recov.o raid6tables.o \
raid6int1.o raid6int2.o raid6int4.o \ raid6int1.o raid6int2.o raid6int4.o \
...@@ -35,9 +33,6 @@ obj-$(CONFIG_BLK_DEV_DM) += dm-mod.o ...@@ -35,9 +33,6 @@ obj-$(CONFIG_BLK_DEV_DM) += dm-mod.o
obj-$(CONFIG_DM_CRYPT) += dm-crypt.o obj-$(CONFIG_DM_CRYPT) += dm-crypt.o
obj-$(CONFIG_DM_DELAY) += dm-delay.o obj-$(CONFIG_DM_DELAY) += dm-delay.o
obj-$(CONFIG_DM_MULTIPATH) += dm-multipath.o dm-round-robin.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_SNAPSHOT) += dm-snapshot.o
obj-$(CONFIG_DM_MIRROR) += dm-mirror.o dm-log.o obj-$(CONFIG_DM_MIRROR) += dm-mirror.o dm-log.o
obj-$(CONFIG_DM_ZERO) += dm-zero.o obj-$(CONFIG_DM_ZERO) += dm-zero.o
......
/*
* 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");
/*
* 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);
/*
* 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
/*
* 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);
...@@ -7,7 +7,6 @@ ...@@ -7,7 +7,6 @@
#include "dm.h" #include "dm.h"
#include "dm-path-selector.h" #include "dm-path-selector.h"
#include "dm-hw-handler.h"
#include "dm-bio-list.h" #include "dm-bio-list.h"
#include "dm-bio-record.h" #include "dm-bio-record.h"
#include "dm-uevent.h" #include "dm-uevent.h"
...@@ -20,6 +19,7 @@ ...@@ -20,6 +19,7 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/time.h> #include <linux/time.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <scsi/scsi_dh.h>
#include <asm/atomic.h> #include <asm/atomic.h>
#define DM_MSG_PREFIX "multipath" #define DM_MSG_PREFIX "multipath"
...@@ -61,7 +61,8 @@ struct multipath { ...@@ -61,7 +61,8 @@ struct multipath {
spinlock_t lock; spinlock_t lock;
struct hw_handler hw_handler; const char *hw_handler_name;
struct work_struct activate_path;
unsigned nr_priority_groups; unsigned nr_priority_groups;
struct list_head priority_groups; struct list_head priority_groups;
unsigned pg_init_required; /* pg_init needs calling? */ unsigned pg_init_required; /* pg_init needs calling? */
...@@ -106,9 +107,10 @@ typedef int (*action_fn) (struct pgpath *pgpath); ...@@ -106,9 +107,10 @@ typedef int (*action_fn) (struct pgpath *pgpath);
static struct kmem_cache *_mpio_cache; 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 process_queued_ios(struct work_struct *work);
static void trigger_event(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) ...@@ -178,6 +180,7 @@ static struct multipath *alloc_multipath(struct dm_target *ti)
m->queue_io = 1; m->queue_io = 1;
INIT_WORK(&m->process_queued_ios, process_queued_ios); INIT_WORK(&m->process_queued_ios, process_queued_ios);
INIT_WORK(&m->trigger_event, trigger_event); 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); m->mpio_pool = mempool_create_slab_pool(MIN_IOS, _mpio_cache);
if (!m->mpio_pool) { if (!m->mpio_pool) {
kfree(m); kfree(m);
...@@ -193,18 +196,13 @@ static struct multipath *alloc_multipath(struct dm_target *ti) ...@@ -193,18 +196,13 @@ static struct multipath *alloc_multipath(struct dm_target *ti)
static void free_multipath(struct multipath *m) static void free_multipath(struct multipath *m)
{ {
struct priority_group *pg, *tmp; struct priority_group *pg, *tmp;
struct hw_handler *hwh = &m->hw_handler;
list_for_each_entry_safe(pg, tmp, &m->priority_groups, list) { list_for_each_entry_safe(pg, tmp, &m->priority_groups, list) {
list_del(&pg->list); list_del(&pg->list);
free_priority_group(pg, m->ti); free_priority_group(pg, m->ti);
} }
if (hwh->type) { kfree(m->hw_handler_name);
hwh->type->destroy(hwh);
dm_put_hw_handler(hwh->type);
}
mempool_destroy(m->mpio_pool); mempool_destroy(m->mpio_pool);
kfree(m); kfree(m);
} }
...@@ -216,12 +214,10 @@ static void free_multipath(struct multipath *m) ...@@ -216,12 +214,10 @@ static void free_multipath(struct multipath *m)
static void __switch_pg(struct multipath *m, struct pgpath *pgpath) static void __switch_pg(struct multipath *m, struct pgpath *pgpath)
{ {
struct hw_handler *hwh = &m->hw_handler;
m->current_pg = pgpath->pg; m->current_pg = pgpath->pg;
/* Must we initialise the PG first, and queue I/O till it's ready? */ /* 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->pg_init_required = 1;
m->queue_io = 1; m->queue_io = 1;
} else { } else {
...@@ -409,7 +405,6 @@ static void process_queued_ios(struct work_struct *work) ...@@ -409,7 +405,6 @@ static void process_queued_ios(struct work_struct *work)
{ {
struct multipath *m = struct multipath *m =
container_of(work, struct multipath, process_queued_ios); container_of(work, struct multipath, process_queued_ios);
struct hw_handler *hwh = &m->hw_handler;
struct pgpath *pgpath = NULL; struct pgpath *pgpath = NULL;
unsigned init_required = 0, must_queue = 1; unsigned init_required = 0, must_queue = 1;
unsigned long flags; unsigned long flags;
...@@ -439,7 +434,7 @@ static void process_queued_ios(struct work_struct *work) ...@@ -439,7 +434,7 @@ static void process_queued_ios(struct work_struct *work)
spin_unlock_irqrestore(&m->lock, flags); spin_unlock_irqrestore(&m->lock, flags);
if (init_required) if (init_required)
hwh->type->pg_init(hwh, pgpath->pg->bypassed, &pgpath->path); queue_work(kmpath_handlerd, &m->activate_path);
if (!must_queue) if (!must_queue)
dispatch_queued_ios(m); dispatch_queued_ios(m);
...@@ -652,8 +647,6 @@ static struct priority_group *parse_priority_group(struct arg_set *as, ...@@ -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) static int parse_hw_handler(struct arg_set *as, struct multipath *m)
{ {
int r;
struct hw_handler_type *hwht;
unsigned hw_argc; unsigned hw_argc;
struct dm_target *ti = m->ti; struct dm_target *ti = m->ti;
...@@ -661,30 +654,20 @@ static int parse_hw_handler(struct arg_set *as, struct multipath *m) ...@@ -661,30 +654,20 @@ static int parse_hw_handler(struct arg_set *as, struct multipath *m)
{0, 1024, "invalid number of hardware handler args"}, {0, 1024, "invalid number of hardware handler args"},
}; };
r = read_param(_params, shift(as), &hw_argc, &ti->error); if (read_param(_params, shift(as), &hw_argc, &ti->error))
if (r)
return -EINVAL; return -EINVAL;
if (!hw_argc) if (!hw_argc)
return 0; return 0;
hwht = dm_get_hw_handler(shift(as)); m->hw_handler_name = kstrdup(shift(as), GFP_KERNEL);
if (!hwht) { 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"; ti->error = "unknown hardware handler type";
kfree(m->hw_handler_name);
m->hw_handler_name = NULL;
return -EINVAL; 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); consume(as, hw_argc - 1);
return 0; return 0;
...@@ -808,6 +791,7 @@ static void multipath_dtr(struct dm_target *ti) ...@@ -808,6 +791,7 @@ static void multipath_dtr(struct dm_target *ti)
{ {
struct multipath *m = (struct multipath *) ti->private; struct multipath *m = (struct multipath *) ti->private;
flush_workqueue(kmpath_handlerd);
flush_workqueue(kmultipathd); flush_workqueue(kmultipathd);
free_multipath(m); free_multipath(m);
} }
...@@ -1025,52 +1009,85 @@ static int pg_init_limit_reached(struct multipath *m, struct pgpath *pgpath) ...@@ -1025,52 +1009,85 @@ static int pg_init_limit_reached(struct multipath *m, struct pgpath *pgpath)
return limit_reached; return limit_reached;
} }
/* static void pg_init_done(struct dm_path *path, int errors)
* pg_init must call this when it has completed its initialisation
*/
void dm_pg_init_complete(struct dm_path *path, unsigned err_flags)
{ {
struct pgpath *pgpath = path_to_pgpath(path); struct pgpath *pgpath = path_to_pgpath(path);
struct priority_group *pg = pgpath->pg; struct priority_group *pg = pgpath->pg;
struct multipath *m = pg->m; struct multipath *m = pg->m;
unsigned long flags; unsigned long flags;
/* /* device or driver problems */
* If requested, retry pg_init until maximum number of retries exceeded. switch (errors) {
* If retry not requested and PG already bypassed, always fail the path. case SCSI_DH_OK:
*/ break;
if (err_flags & MP_RETRY) { case SCSI_DH_NOSYS:
if (pg_init_limit_reached(m, pgpath)) if (!m->hw_handler_name) {
err_flags |= MP_FAIL_PATH; errors = 0;
} else if (err_flags && pg->bypassed) break;
err_flags |= MP_FAIL_PATH; }
DMERR("Cannot failover device because scsi_dh_%s was not "
if (err_flags & MP_FAIL_PATH) "loaded.", m->hw_handler_name);
/*
* Fail path for now, so we do not ping pong
*/
fail_path(pgpath); fail_path(pgpath);
break;
if (err_flags & MP_BYPASS_PG) 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); 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); 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_pgpath = NULL;
m->current_pg = NULL; m->current_pg = NULL;
} else if (!m->pg_init_required) } else if (!m->pg_init_required) {
m->queue_io = 0; m->queue_io = 0;
pg->bypassed = 0;
}
m->pg_init_in_progress = 0; m->pg_init_in_progress = 0;
queue_work(kmultipathd, &m->process_queued_ios); queue_work(kmultipathd, &m->process_queued_ios);
spin_unlock_irqrestore(&m->lock, flags); 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 * end_io handling
*/ */
static int do_end_io(struct multipath *m, struct bio *bio, static int do_end_io(struct multipath *m, struct bio *bio,
int error, struct dm_mpath_io *mpio) 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; unsigned long flags;
if (!error) if (!error)
...@@ -1097,19 +1114,8 @@ static int do_end_io(struct multipath *m, struct bio *bio, ...@@ -1097,19 +1114,8 @@ static int do_end_io(struct multipath *m, struct bio *bio,
} }
spin_unlock_irqrestore(&m->lock, flags); spin_unlock_irqrestore(&m->lock, flags);
if (hwh->type && hwh->type->error) if (mpio->pgpath)
err_flags = hwh->type->error(hwh, bio); fail_path(mpio->pgpath);
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;
requeue: requeue:
dm_bio_restore(&mpio->details, bio); dm_bio_restore(&mpio->details, bio);
...@@ -1194,7 +1200,6 @@ static int multipath_status(struct dm_target *ti, status_type_t type, ...@@ -1194,7 +1200,6 @@ static int multipath_status(struct dm_target *ti, status_type_t type,
int sz = 0; int sz = 0;
unsigned long flags; unsigned long flags;
struct multipath *m = (struct multipath *) ti->private; struct multipath *m = (struct multipath *) ti->private;
struct hw_handler *hwh = &m->hw_handler;
struct priority_group *pg; struct priority_group *pg;
struct pgpath *p; struct pgpath *p;
unsigned pg_num; unsigned pg_num;
...@@ -1214,12 +1219,10 @@ static int multipath_status(struct dm_target *ti, status_type_t type, ...@@ -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); DMEMIT("pg_init_retries %u ", m->pg_init_retries);
} }
if (hwh->type && hwh->type->status) if (!m->hw_handler_name || type == STATUSTYPE_INFO)
sz += hwh->type->status(hwh, type, result + sz, maxlen - sz);
else if (!hwh->type || type == STATUSTYPE_INFO)
DMEMIT("0 "); DMEMIT("0 ");
else else
DMEMIT("1 %s ", hwh->type->name); DMEMIT("1 %s ", m->hw_handler_name);
DMEMIT("%u ", m->nr_priority_groups); DMEMIT("%u ", m->nr_priority_groups);
...@@ -1422,6 +1425,21 @@ static int __init dm_multipath_init(void) ...@@ -1422,6 +1425,21 @@ static int __init dm_multipath_init(void)
return -ENOMEM; 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", DMINFO("version %u.%u.%u loaded",
multipath_target.version[0], multipath_target.version[1], multipath_target.version[0], multipath_target.version[1],
multipath_target.version[2]); multipath_target.version[2]);
...@@ -1433,6 +1451,7 @@ static void __exit dm_multipath_exit(void) ...@@ -1433,6 +1451,7 @@ static void __exit dm_multipath_exit(void)
{ {
int r; int r;
destroy_workqueue(kmpath_handlerd);
destroy_workqueue(kmultipathd); destroy_workqueue(kmultipathd);
r = dm_unregister_target(&multipath_target); r = dm_unregister_target(&multipath_target);
...@@ -1441,8 +1460,6 @@ static void __exit dm_multipath_exit(void) ...@@ -1441,8 +1460,6 @@ static void __exit dm_multipath_exit(void)
kmem_cache_destroy(_mpio_cache); kmem_cache_destroy(_mpio_cache);
} }
EXPORT_SYMBOL_GPL(dm_pg_init_complete);
module_init(dm_multipath_init); module_init(dm_multipath_init);
module_exit(dm_multipath_exit); module_exit(dm_multipath_exit);
......
...@@ -16,7 +16,6 @@ struct dm_path { ...@@ -16,7 +16,6 @@ struct dm_path {
unsigned is_active; /* Read-only */ unsigned is_active; /* Read-only */
void *pscontext; /* For path-selector use */ void *pscontext; /* For path-selector use */
void *hwhcontext; /* For hw-handler use */
}; };
/* Callback for hwh_pg_init_fn to use when complete */ /* Callback for hwh_pg_init_fn to use when complete */
......
/* /*
* Copyright (c) 2000-2007 LSI Corporation. * Copyright (c) 2000-2008 LSI Corporation.
* *
* *
* Name: mpi.h * Name: mpi.h
......
/* /*
* Copyright (c) 2000-2007 LSI Corporation. * Copyright (c) 2000-2008 LSI Corporation.
* *
* *
* Name: mpi_cnfg.h * Name: mpi_cnfg.h
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
* For use with LSI PCI chip/adapter(s) * For use with LSI PCI chip/adapter(s)
* running LSI Fusion MPT (Message Passing Technology) firmware. * 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) * (mailto:DL-MPTFusionLinux@lsi.com)
* *
*/ */
...@@ -103,7 +103,7 @@ static int mfcounter = 0; ...@@ -103,7 +103,7 @@ static int mfcounter = 0;
* Public data... * Public data...
*/ */
struct proc_dir_entry *mpt_proc_root_dir; static struct proc_dir_entry *mpt_proc_root_dir;
#define WHOINIT_UNKNOWN 0xAA #define WHOINIT_UNKNOWN 0xAA
...@@ -253,6 +253,55 @@ mpt_get_cb_idx(MPT_DRIVER_CLASS dclass) ...@@ -253,6 +253,55 @@ mpt_get_cb_idx(MPT_DRIVER_CLASS dclass)
return 0; 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... * Process turbo (context) reply...
*/ */
...@@ -1616,6 +1665,22 @@ mpt_attach(struct pci_dev *pdev, const struct pci_device_id *id) ...@@ -1616,6 +1665,22 @@ mpt_attach(struct pci_dev *pdev, const struct pci_device_id *id)
/* Find lookup slot. */ /* Find lookup slot. */
INIT_LIST_HEAD(&ioc->list); 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", dinitprintk(ioc, printk(MYIOC_s_INFO_FMT "facts @ %p, pfacts[0] @ %p\n",
ioc->name, &ioc->facts, &ioc->pfacts[0])); ioc->name, &ioc->facts, &ioc->pfacts[0]));
...@@ -1727,6 +1792,10 @@ mpt_attach(struct pci_dev *pdev, const struct pci_device_id *id) ...@@ -1727,6 +1792,10 @@ mpt_attach(struct pci_dev *pdev, const struct pci_device_id *id)
iounmap(ioc->memmap); iounmap(ioc->memmap);
if (r != -5) if (r != -5)
pci_release_selected_regions(pdev, ioc->bars); pci_release_selected_regions(pdev, ioc->bars);
destroy_workqueue(ioc->reset_work_q);
ioc->reset_work_q = NULL;
kfree(ioc); kfree(ioc);
pci_set_drvdata(pdev, NULL); pci_set_drvdata(pdev, NULL);
return r; return r;
...@@ -1759,6 +1828,10 @@ mpt_attach(struct pci_dev *pdev, const struct pci_device_id *id) ...@@ -1759,6 +1828,10 @@ mpt_attach(struct pci_dev *pdev, const struct pci_device_id *id)
} }
#endif #endif
if (!ioc->alt_ioc)
queue_delayed_work(ioc->reset_work_q, &ioc->fault_reset_work,
msecs_to_jiffies(MPT_POLLING_INTERVAL));
return 0; return 0;
} }
...@@ -1774,6 +1847,19 @@ mpt_detach(struct pci_dev *pdev) ...@@ -1774,6 +1847,19 @@ mpt_detach(struct pci_dev *pdev)
MPT_ADAPTER *ioc = pci_get_drvdata(pdev); MPT_ADAPTER *ioc = pci_get_drvdata(pdev);
char pname[32]; char pname[32];
u8 cb_idx; 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); sprintf(pname, MPT_PROCFS_MPTBASEDIR "/%s/summary", ioc->name);
remove_proc_entry(pname, NULL); remove_proc_entry(pname, NULL);
...@@ -7456,7 +7542,6 @@ EXPORT_SYMBOL(mpt_resume); ...@@ -7456,7 +7542,6 @@ EXPORT_SYMBOL(mpt_resume);
EXPORT_SYMBOL(mpt_suspend); EXPORT_SYMBOL(mpt_suspend);
#endif #endif
EXPORT_SYMBOL(ioc_list); EXPORT_SYMBOL(ioc_list);
EXPORT_SYMBOL(mpt_proc_root_dir);
EXPORT_SYMBOL(mpt_register); EXPORT_SYMBOL(mpt_register);
EXPORT_SYMBOL(mpt_deregister); EXPORT_SYMBOL(mpt_deregister);
EXPORT_SYMBOL(mpt_event_register); EXPORT_SYMBOL(mpt_event_register);
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
* LSIFC9xx/LSI409xx Fibre Channel * LSIFC9xx/LSI409xx Fibre Channel
* running LSI Fusion MPT (Message Passing Technology) firmware. * 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) * (mailto:DL-MPTFusionLinux@lsi.com)
* *
*/ */
...@@ -73,11 +73,11 @@ ...@@ -73,11 +73,11 @@
#endif #endif
#ifndef COPYRIGHT #ifndef COPYRIGHT
#define COPYRIGHT "Copyright (c) 1999-2007 " MODULEAUTHOR #define COPYRIGHT "Copyright (c) 1999-2008 " MODULEAUTHOR
#endif #endif
#define MPT_LINUX_VERSION_COMMON "3.04.06" #define MPT_LINUX_VERSION_COMMON "3.04.07"
#define MPT_LINUX_PACKAGE_NAME "@(#)mptlinux-3.04.06" #define MPT_LINUX_PACKAGE_NAME "@(#)mptlinux-3.04.07"
#define WHAT_MAGIC_STRING "@" "(" "#" ")" #define WHAT_MAGIC_STRING "@" "(" "#" ")"
#define show_mptmod_ver(s,ver) \ #define show_mptmod_ver(s,ver) \
...@@ -176,6 +176,8 @@ ...@@ -176,6 +176,8 @@
/* debug print string length used for events and iocstatus */ /* debug print string length used for events and iocstatus */
# define EVENT_DESCR_STR_SZ 100 # define EVENT_DESCR_STR_SZ 100
#define MPT_POLLING_INTERVAL 1000 /* in milliseconds */
#ifdef __KERNEL__ /* { */ #ifdef __KERNEL__ /* { */
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
...@@ -709,6 +711,12 @@ typedef struct _MPT_ADAPTER ...@@ -709,6 +711,12 @@ typedef struct _MPT_ADAPTER
struct workqueue_struct *fc_rescan_work_q; struct workqueue_struct *fc_rescan_work_q;
struct scsi_cmnd **ScsiLookup; struct scsi_cmnd **ScsiLookup;
spinlock_t scsi_lookup_lock; 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; } MPT_ADAPTER;
/* /*
...@@ -919,7 +927,6 @@ extern int mpt_raid_phys_disk_pg0(MPT_ADAPTER *ioc, u8 phys_disk_num, pRaidPhys ...@@ -919,7 +927,6 @@ extern int mpt_raid_phys_disk_pg0(MPT_ADAPTER *ioc, u8 phys_disk_num, pRaidPhys
* Public data decl's... * Public data decl's...
*/ */
extern struct list_head ioc_list; extern struct list_head ioc_list;
extern struct proc_dir_entry *mpt_proc_root_dir;
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
#endif /* } __KERNEL__ */ #endif /* } __KERNEL__ */
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
* For use with LSI PCI chip/adapters * For use with LSI PCI chip/adapters
* running LSI Fusion MPT (Message Passing Technology) firmware. * 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) * (mailto:DL-MPTFusionLinux@lsi.com)
* *
*/ */
...@@ -66,7 +66,7 @@ ...@@ -66,7 +66,7 @@
#include <scsi/scsi_host.h> #include <scsi/scsi_host.h>
#include <scsi/scsi_tcq.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" #define MODULEAUTHOR "LSI Corporation"
#include "mptbase.h" #include "mptbase.h"
#include "mptctl.h" #include "mptctl.h"
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment