Merge git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi-misc-2.6

* git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi-misc-2.6: (102 commits)
  [SCSI] scsi_dh: fix kconfig related build errors
  [SCSI] sym53c8xx: Fix bogus sym_que_entry re-implementation of container_of
  [SCSI] scsi_cmnd.h: remove double inclusion of linux/blkdev.h
  [SCSI] make struct scsi_{host,target}_type static
  [SCSI] fix locking in host use of blk_plug_device()
  [SCSI] zfcp: Cleanup external header file
  [SCSI] zfcp: Cleanup code in zfcp_erp.c
  [SCSI] zfcp: zfcp_fsf cleanup.
  [SCSI] zfcp: consolidate sysfs things into one file.
  [SCSI] zfcp: Cleanup of code in zfcp_aux.c
  [SCSI] zfcp: Cleanup of code in zfcp_scsi.c
  [SCSI] zfcp: Move status accessors from zfcp to SCSI include file.
  [SCSI] zfcp: Small QDIO cleanups
  [SCSI] zfcp: Adapter reopen for large number of unsolicited status
  [SCSI] zfcp: Fix error checking for ELS ADISC requests
  [SCSI] zfcp: wait until adapter is finished with ERP during auto-port
  [SCSI] ibmvfc: IBM Power Virtual Fibre Channel Adapter Client Driver
  [SCSI] sg: Add target reset support
  [SCSI] lib: Add support for the T10 (SCSI) Data Integrity Field CRC
  [SCSI] sd: Move scsi_disk() accessor function to sd.h
  ...
This commit is contained in:
Linus Torvalds 2008-07-15 18:58:04 -07:00
commit 89a93f2f48
106 changed files with 14864 additions and 14654 deletions

View File

@ -56,19 +56,33 @@ Supported Cards/Chipsets
9005:0285:9005:02d1 Adaptec 5405 (Voodoo40)
9005:0285:15d9:02d2 SMC AOC-USAS-S8i-LP
9005:0285:15d9:02d3 SMC AOC-USAS-S8iR-LP
9005:0285:9005:02d4 Adaptec 2045 (Voodoo04 Lite)
9005:0285:9005:02d5 Adaptec 2405 (Voodoo40 Lite)
9005:0285:9005:02d6 Adaptec 2445 (Voodoo44 Lite)
9005:0285:9005:02d7 Adaptec 2805 (Voodoo80 Lite)
9005:0285:9005:02d4 Adaptec ASR-2045 (Voodoo04 Lite)
9005:0285:9005:02d5 Adaptec ASR-2405 (Voodoo40 Lite)
9005:0285:9005:02d6 Adaptec ASR-2445 (Voodoo44 Lite)
9005:0285:9005:02d7 Adaptec ASR-2805 (Voodoo80 Lite)
9005:0285:9005:02d8 Adaptec 5405G (Voodoo40 PM)
9005:0285:9005:02d9 Adaptec 5445G (Voodoo44 PM)
9005:0285:9005:02da Adaptec 5805G (Voodoo80 PM)
9005:0285:9005:02db Adaptec 5085G (Voodoo08 PM)
9005:0285:9005:02dc Adaptec 51245G (Voodoo124 PM)
9005:0285:9005:02dd Adaptec 51645G (Voodoo164 PM)
9005:0285:9005:02de Adaptec 52445G (Voodoo244 PM)
9005:0285:9005:02df Adaptec ASR-2045G (Voodoo04 Lite PM)
9005:0285:9005:02e0 Adaptec ASR-2405G (Voodoo40 Lite PM)
9005:0285:9005:02e1 Adaptec ASR-2445G (Voodoo44 Lite PM)
9005:0285:9005:02e2 Adaptec ASR-2805G (Voodoo80 Lite PM)
1011:0046:9005:0364 Adaptec 5400S (Mustang)
1011:0046:9005:0365 Adaptec 5400S (Mustang)
9005:0287:9005:0800 Adaptec Themisto (Jupiter)
9005:0200:9005:0200 Adaptec Themisto (Jupiter)
9005:0286:9005:0800 Adaptec Callisto (Jupiter)
1011:0046:9005:1364 Dell PERC 2/QC (Quad Channel, Mustang)
1011:0046:9005:1365 Dell PERC 2/QC (Quad Channel, Mustang)
1028:0001:1028:0001 Dell PERC 2/Si (Iguana)
1028:0003:1028:0003 Dell PERC 3/Si (SlimFast)
1028:0002:1028:0002 Dell PERC 3/Di (Opal)
1028:0004:1028:0004 Dell PERC 3/DiF (Iguana)
1028:0004:1028:0004 Dell PERC 3/SiF (Iguana)
1028:0004:1028:00d0 Dell PERC 3/DiF (Iguana)
1028:0002:1028:00d1 Dell PERC 3/DiV (Viper)
1028:0002:1028:00d9 Dell PERC 3/DiL (Lexus)
1028:000a:1028:0106 Dell PERC 3/DiJ (Jaguar)

View File

@ -740,8 +740,13 @@ static int bsg_put_device(struct bsg_device *bd)
mutex_lock(&bsg_mutex);
do_free = atomic_dec_and_test(&bd->ref_count);
if (!do_free)
if (!do_free) {
mutex_unlock(&bsg_mutex);
goto out;
}
hlist_del(&bd->dev_list);
mutex_unlock(&bsg_mutex);
dprintk("%s: tearing down\n", bd->name);
@ -757,10 +762,8 @@ static int bsg_put_device(struct bsg_device *bd)
*/
ret = bsg_complete_all_commands(bd);
hlist_del(&bd->dev_list);
kfree(bd);
out:
mutex_unlock(&bsg_mutex);
kref_put(&q->bsg_dev.ref, bsg_kref_release_function);
if (do_free)
blk_put_queue(q);

View File

@ -71,6 +71,10 @@
#include "iscsi_iser.h"
static struct scsi_host_template iscsi_iser_sht;
static struct iscsi_transport iscsi_iser_transport;
static struct scsi_transport_template *iscsi_iser_scsi_transport;
static unsigned int iscsi_max_lun = 512;
module_param_named(max_lun, iscsi_max_lun, uint, S_IRUGO);
@ -91,7 +95,6 @@ iscsi_iser_recv(struct iscsi_conn *conn,
struct iscsi_hdr *hdr, char *rx_data, int rx_data_len)
{
int rc = 0;
uint32_t ret_itt;
int datalen;
int ahslen;
@ -107,12 +110,7 @@ iscsi_iser_recv(struct iscsi_conn *conn,
/* read AHS */
ahslen = hdr->hlength * 4;
/* verify itt (itt encoding: age+cid+itt) */
rc = iscsi_verify_itt(conn, hdr, &ret_itt);
if (!rc)
rc = iscsi_complete_pdu(conn, hdr, rx_data, rx_data_len);
rc = iscsi_complete_pdu(conn, hdr, rx_data, rx_data_len);
if (rc && rc != ISCSI_ERR_NO_SCSI_CMD)
goto error;
@ -123,25 +121,33 @@ 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
iscsi_iser_cmd_init(struct iscsi_cmd_task *ctask)
iscsi_iser_task_init(struct iscsi_task *task)
{
struct iscsi_iser_conn *iser_conn = ctask->conn->dd_data;
struct iscsi_iser_cmd_task *iser_ctask = ctask->dd_data;
struct iscsi_iser_conn *iser_conn = task->conn->dd_data;
struct iscsi_iser_task *iser_task = task->dd_data;
iser_ctask->command_sent = 0;
iser_ctask->iser_conn = iser_conn;
iser_ctask_rdma_init(iser_ctask);
/* mgmt task */
if (!task->sc) {
iser_task->desc.data = task->data;
return 0;
}
iser_task->command_sent = 0;
iser_task->iser_conn = iser_conn;
iser_task_rdma_init(iser_task);
return 0;
}
/**
* iscsi_mtask_xmit - xmit management(immediate) task
* iscsi_iser_mtask_xmit - xmit management(immediate) task
* @conn: iscsi connection
* @mtask: task management task
* @task: task management task
*
* Notes:
* The function can return -EAGAIN in which case caller must
@ -150,20 +156,19 @@ iscsi_iser_cmd_init(struct iscsi_cmd_task *ctask)
*
**/
static int
iscsi_iser_mtask_xmit(struct iscsi_conn *conn,
struct iscsi_mgmt_task *mtask)
iscsi_iser_mtask_xmit(struct iscsi_conn *conn, struct iscsi_task *task)
{
int error = 0;
debug_scsi("mtask deq [cid %d itt 0x%x]\n", conn->id, mtask->itt);
debug_scsi("task deq [cid %d itt 0x%x]\n", conn->id, task->itt);
error = iser_send_control(conn, mtask);
error = iser_send_control(conn, task);
/* since iser xmits control with zero copy, mtasks can not be recycled
/* since iser xmits control with zero copy, tasks can not be recycled
* right after sending them.
* The recycling scheme is based on whether a response is expected
* - if yes, the mtask is recycled at iscsi_complete_pdu
* - if no, the mtask is recycled at iser_snd_completion
* - if yes, the task is recycled at iscsi_complete_pdu
* - if no, the task is recycled at iser_snd_completion
*/
if (error && error != -ENOBUFS)
iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
@ -172,99 +177,88 @@ iscsi_iser_mtask_xmit(struct iscsi_conn *conn,
}
static int
iscsi_iser_ctask_xmit_unsol_data(struct iscsi_conn *conn,
struct iscsi_cmd_task *ctask)
iscsi_iser_task_xmit_unsol_data(struct iscsi_conn *conn,
struct iscsi_task *task)
{
struct iscsi_data hdr;
int error = 0;
/* Send data-out PDUs while there's still unsolicited data to send */
while (ctask->unsol_count > 0) {
iscsi_prep_unsolicit_data_pdu(ctask, &hdr);
while (task->unsol_count > 0) {
iscsi_prep_unsolicit_data_pdu(task, &hdr);
debug_scsi("Sending data-out: itt 0x%x, data count %d\n",
hdr.itt, ctask->data_count);
hdr.itt, task->data_count);
/* the buffer description has been passed with the command */
/* Send the command */
error = iser_send_data_out(conn, ctask, &hdr);
error = iser_send_data_out(conn, task, &hdr);
if (error) {
ctask->unsol_datasn--;
goto iscsi_iser_ctask_xmit_unsol_data_exit;
task->unsol_datasn--;
goto iscsi_iser_task_xmit_unsol_data_exit;
}
ctask->unsol_count -= ctask->data_count;
task->unsol_count -= task->data_count;
debug_scsi("Need to send %d more as data-out PDUs\n",
ctask->unsol_count);
task->unsol_count);
}
iscsi_iser_ctask_xmit_unsol_data_exit:
iscsi_iser_task_xmit_unsol_data_exit:
return error;
}
static int
iscsi_iser_ctask_xmit(struct iscsi_conn *conn,
struct iscsi_cmd_task *ctask)
iscsi_iser_task_xmit(struct iscsi_task *task)
{
struct iscsi_iser_cmd_task *iser_ctask = ctask->dd_data;
struct iscsi_conn *conn = task->conn;
struct iscsi_iser_task *iser_task = task->dd_data;
int error = 0;
if (ctask->sc->sc_data_direction == DMA_TO_DEVICE) {
BUG_ON(scsi_bufflen(ctask->sc) == 0);
if (!task->sc)
return iscsi_iser_mtask_xmit(conn, task);
if (task->sc->sc_data_direction == DMA_TO_DEVICE) {
BUG_ON(scsi_bufflen(task->sc) == 0);
debug_scsi("cmd [itt %x total %d imm %d unsol_data %d\n",
ctask->itt, scsi_bufflen(ctask->sc),
ctask->imm_count, ctask->unsol_count);
task->itt, scsi_bufflen(task->sc),
task->imm_count, task->unsol_count);
}
debug_scsi("ctask deq [cid %d itt 0x%x]\n",
conn->id, ctask->itt);
debug_scsi("task deq [cid %d itt 0x%x]\n",
conn->id, task->itt);
/* Send the cmd PDU */
if (!iser_ctask->command_sent) {
error = iser_send_command(conn, ctask);
if (!iser_task->command_sent) {
error = iser_send_command(conn, task);
if (error)
goto iscsi_iser_ctask_xmit_exit;
iser_ctask->command_sent = 1;
goto iscsi_iser_task_xmit_exit;
iser_task->command_sent = 1;
}
/* Send unsolicited data-out PDU(s) if necessary */
if (ctask->unsol_count)
error = iscsi_iser_ctask_xmit_unsol_data(conn, ctask);
if (task->unsol_count)
error = iscsi_iser_task_xmit_unsol_data(conn, task);
iscsi_iser_ctask_xmit_exit:
iscsi_iser_task_xmit_exit:
if (error && error != -ENOBUFS)
iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
return error;
}
static void
iscsi_iser_cleanup_ctask(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
iscsi_iser_cleanup_task(struct iscsi_conn *conn, struct iscsi_task *task)
{
struct iscsi_iser_cmd_task *iser_ctask = ctask->dd_data;
struct iscsi_iser_task *iser_task = task->dd_data;
if (iser_ctask->status == ISER_TASK_STATUS_STARTED) {
iser_ctask->status = ISER_TASK_STATUS_COMPLETED;
iser_ctask_rdma_finalize(iser_ctask);
/* mgmt tasks do not need special cleanup */
if (!task->sc)
return;
if (iser_task->status == ISER_TASK_STATUS_STARTED) {
iser_task->status = ISER_TASK_STATUS_COMPLETED;
iser_task_rdma_finalize(iser_task);
}
}
static struct iser_conn *
iscsi_iser_ib_conn_lookup(__u64 ep_handle)
{
struct iser_conn *ib_conn;
struct iser_conn *uib_conn = (struct iser_conn *)(unsigned long)ep_handle;
mutex_lock(&ig.connlist_mutex);
list_for_each_entry(ib_conn, &ig.connlist, conn_list) {
if (ib_conn == uib_conn) {
mutex_unlock(&ig.connlist_mutex);
return ib_conn;
}
}
mutex_unlock(&ig.connlist_mutex);
iser_err("no conn exists for eph %llx\n",(unsigned long long)ep_handle);
return NULL;
}
static struct iscsi_cls_conn *
iscsi_iser_conn_create(struct iscsi_cls_session *cls_session, uint32_t conn_idx)
{
@ -272,7 +266,7 @@ iscsi_iser_conn_create(struct iscsi_cls_session *cls_session, uint32_t conn_idx)
struct iscsi_cls_conn *cls_conn;
struct iscsi_iser_conn *iser_conn;
cls_conn = iscsi_conn_setup(cls_session, conn_idx);
cls_conn = iscsi_conn_setup(cls_session, sizeof(*iser_conn), conn_idx);
if (!cls_conn)
return NULL;
conn = cls_conn->dd_data;
@ -283,21 +277,11 @@ iscsi_iser_conn_create(struct iscsi_cls_session *cls_session, uint32_t conn_idx)
*/
conn->max_recv_dlength = 128;
iser_conn = kzalloc(sizeof(*iser_conn), GFP_KERNEL);
if (!iser_conn)
goto conn_alloc_fail;
/* currently this is the only field which need to be initiated */
rwlock_init(&iser_conn->lock);
iser_conn = conn->dd_data;
conn->dd_data = iser_conn;
iser_conn->iscsi_conn = conn;
return cls_conn;
conn_alloc_fail:
iscsi_conn_teardown(cls_conn);
return NULL;
}
static void
@ -305,11 +289,18 @@ iscsi_iser_conn_destroy(struct iscsi_cls_conn *cls_conn)
{
struct iscsi_conn *conn = cls_conn->dd_data;
struct iscsi_iser_conn *iser_conn = conn->dd_data;
struct iser_conn *ib_conn = iser_conn->ib_conn;
iscsi_conn_teardown(cls_conn);
if (iser_conn->ib_conn)
iser_conn->ib_conn->iser_conn = NULL;
kfree(iser_conn);
/*
* Userspace will normally call the stop callback and
* already have freed the ib_conn, but if it goofed up then
* we free it here.
*/
if (ib_conn) {
ib_conn->iser_conn = NULL;
iser_conn_put(ib_conn);
}
}
static int
@ -320,6 +311,7 @@ iscsi_iser_conn_bind(struct iscsi_cls_session *cls_session,
struct iscsi_conn *conn = cls_conn->dd_data;
struct iscsi_iser_conn *iser_conn;
struct iser_conn *ib_conn;
struct iscsi_endpoint *ep;
int error;
error = iscsi_conn_bind(cls_session, cls_conn, is_leading);
@ -328,12 +320,14 @@ iscsi_iser_conn_bind(struct iscsi_cls_session *cls_session,
/* the transport ep handle comes from user space so it must be
* verified against the global ib connections list */
ib_conn = iscsi_iser_ib_conn_lookup(transport_eph);
if (!ib_conn) {
ep = iscsi_lookup_endpoint(transport_eph);
if (!ep) {
iser_err("can't bind eph %llx\n",
(unsigned long long)transport_eph);
return -EINVAL;
}
ib_conn = ep->dd_data;
/* binds the iSER connection retrieved from the previously
* connected ep_handle to the iSCSI layer connection. exchanges
* connection pointers */
@ -341,12 +335,32 @@ iscsi_iser_conn_bind(struct iscsi_cls_session *cls_session,
iser_conn = conn->dd_data;
ib_conn->iser_conn = iser_conn;
iser_conn->ib_conn = ib_conn;
conn->recv_lock = &iser_conn->lock;
iser_conn_get(ib_conn);
return 0;
}
static void
iscsi_iser_conn_stop(struct iscsi_cls_conn *cls_conn, int flag)
{
struct iscsi_conn *conn = cls_conn->dd_data;
struct iscsi_iser_conn *iser_conn = conn->dd_data;
struct iser_conn *ib_conn = iser_conn->ib_conn;
/*
* Userspace may have goofed up and not bound the connection or
* might have only partially setup the connection.
*/
if (ib_conn) {
iscsi_conn_stop(cls_conn, flag);
/*
* There is no unbind event so the stop callback
* must release the ref from the bind.
*/
iser_conn_put(ib_conn);
}
iser_conn->ib_conn = NULL;
}
static int
iscsi_iser_conn_start(struct iscsi_cls_conn *cls_conn)
{
@ -360,55 +374,75 @@ iscsi_iser_conn_start(struct iscsi_cls_conn *cls_conn)
return iscsi_conn_start(cls_conn);
}
static struct iscsi_transport iscsi_iser_transport;
static void iscsi_iser_session_destroy(struct iscsi_cls_session *cls_session)
{
struct Scsi_Host *shost = iscsi_session_to_shost(cls_session);
iscsi_host_remove(shost);
iscsi_host_free(shost);
}
static struct iscsi_cls_session *
iscsi_iser_session_create(struct iscsi_transport *iscsit,
struct scsi_transport_template *scsit,
uint16_t cmds_max, uint16_t qdepth,
uint32_t initial_cmdsn, uint32_t *hostno)
iscsi_iser_session_create(struct iscsi_endpoint *ep,
uint16_t cmds_max, uint16_t qdepth,
uint32_t initial_cmdsn, uint32_t *hostno)
{
struct iscsi_cls_session *cls_session;
struct iscsi_session *session;
struct Scsi_Host *shost;
int i;
uint32_t hn;
struct iscsi_cmd_task *ctask;
struct iscsi_mgmt_task *mtask;
struct iscsi_iser_cmd_task *iser_ctask;
struct iser_desc *desc;
struct iscsi_task *task;
struct iscsi_iser_task *iser_task;
struct iser_conn *ib_conn;
shost = iscsi_host_alloc(&iscsi_iser_sht, 0, ISCSI_MAX_CMD_PER_LUN);
if (!shost)
return NULL;
shost->transportt = iscsi_iser_scsi_transport;
shost->max_lun = iscsi_max_lun;
shost->max_id = 0;
shost->max_channel = 0;
shost->max_cmd_len = 16;
/*
* older userspace tools (before 2.0-870) did not pass us
* the leading conn's ep so this will be NULL;
*/
if (ep)
ib_conn = ep->dd_data;
if (iscsi_host_add(shost,
ep ? ib_conn->device->ib_device->dma_device : NULL))
goto free_host;
*hostno = shost->host_no;
/*
* we do not support setting can_queue cmd_per_lun from userspace yet
* because we preallocate so many resources
*/
cls_session = iscsi_session_setup(iscsit, scsit,
cls_session = iscsi_session_setup(&iscsi_iser_transport, shost,
ISCSI_DEF_XMIT_CMDS_MAX,
ISCSI_MAX_CMD_PER_LUN,
sizeof(struct iscsi_iser_cmd_task),
sizeof(struct iser_desc),
initial_cmdsn, &hn);
sizeof(struct iscsi_iser_task),
initial_cmdsn, 0);
if (!cls_session)
return NULL;
*hostno = hn;
session = class_to_transport_session(cls_session);
goto remove_host;
session = cls_session->dd_data;
shost->can_queue = session->scsi_cmds_max;
/* libiscsi setup itts, data and pool so just set desc fields */
for (i = 0; i < session->cmds_max; i++) {
ctask = session->cmds[i];
iser_ctask = ctask->dd_data;
ctask->hdr = (struct iscsi_cmd *)&iser_ctask->desc.iscsi_header;
ctask->hdr_max = sizeof(iser_ctask->desc.iscsi_header);
task = session->cmds[i];
iser_task = task->dd_data;
task->hdr = (struct iscsi_cmd *)&iser_task->desc.iscsi_header;
task->hdr_max = sizeof(iser_task->desc.iscsi_header);
}
for (i = 0; i < session->mgmtpool_max; i++) {
mtask = session->mgmt_cmds[i];
desc = mtask->dd_data;
mtask->hdr = &desc->iscsi_header;
desc->data = mtask->data;
}
return cls_session;
remove_host:
iscsi_host_remove(shost);
free_host:
iscsi_host_free(shost);
return NULL;
}
static int
@ -481,34 +515,37 @@ iscsi_iser_conn_get_stats(struct iscsi_cls_conn *cls_conn, struct iscsi_stats *s
stats->custom[3].value = conn->fmr_unalign_cnt;
}
static int
iscsi_iser_ep_connect(struct sockaddr *dst_addr, int non_blocking,
__u64 *ep_handle)
static struct iscsi_endpoint *
iscsi_iser_ep_connect(struct sockaddr *dst_addr, int non_blocking)
{
int err;
struct iser_conn *ib_conn;
struct iscsi_endpoint *ep;
err = iser_conn_init(&ib_conn);
if (err)
goto out;
ep = iscsi_create_endpoint(sizeof(*ib_conn));
if (!ep)
return ERR_PTR(-ENOMEM);
err = iser_connect(ib_conn, NULL, (struct sockaddr_in *)dst_addr, non_blocking);
if (!err)
*ep_handle = (__u64)(unsigned long)ib_conn;
ib_conn = ep->dd_data;
ib_conn->ep = ep;
iser_conn_init(ib_conn);
out:
return err;
err = iser_connect(ib_conn, NULL, (struct sockaddr_in *)dst_addr,
non_blocking);
if (err) {
iscsi_destroy_endpoint(ep);
return ERR_PTR(err);
}
return ep;
}
static int
iscsi_iser_ep_poll(__u64 ep_handle, int timeout_ms)
iscsi_iser_ep_poll(struct iscsi_endpoint *ep, int timeout_ms)
{
struct iser_conn *ib_conn = iscsi_iser_ib_conn_lookup(ep_handle);
struct iser_conn *ib_conn;
int rc;
if (!ib_conn)
return -EINVAL;
ib_conn = ep->dd_data;
rc = wait_event_interruptible_timeout(ib_conn->wait,
ib_conn->state == ISER_CONN_UP,
msecs_to_jiffies(timeout_ms));
@ -530,13 +567,21 @@ iscsi_iser_ep_poll(__u64 ep_handle, int timeout_ms)
}
static void
iscsi_iser_ep_disconnect(__u64 ep_handle)
iscsi_iser_ep_disconnect(struct iscsi_endpoint *ep)
{
struct iser_conn *ib_conn;
ib_conn = iscsi_iser_ib_conn_lookup(ep_handle);
if (!ib_conn)
return;
ib_conn = ep->dd_data;
if (ib_conn->iser_conn)
/*
* Must suspend xmit path if the ep is bound to the
* iscsi_conn, so we know we are not accessing the ib_conn
* when we free it.
*
* This may not be bound if the ep poll failed.
*/
iscsi_suspend_tx(ib_conn->iser_conn->iscsi_conn);
iser_err("ib conn %p state %d\n",ib_conn, ib_conn->state);
iser_conn_terminate(ib_conn);
@ -547,7 +592,6 @@ static struct scsi_host_template iscsi_iser_sht = {
.name = "iSCSI Initiator over iSER, v." DRV_VER,
.queuecommand = iscsi_queuecommand,
.change_queue_depth = iscsi_change_queue_depth,
.can_queue = ISCSI_DEF_XMIT_CMDS_MAX - 1,
.sg_tablesize = ISCSI_ISER_SG_TABLESIZE,
.max_sectors = 1024,
.cmd_per_lun = ISCSI_MAX_CMD_PER_LUN,
@ -581,17 +625,14 @@ static struct iscsi_transport iscsi_iser_transport = {
ISCSI_USERNAME | ISCSI_PASSWORD |
ISCSI_USERNAME_IN | ISCSI_PASSWORD_IN |
ISCSI_FAST_ABORT | ISCSI_ABORT_TMO |
ISCSI_PING_TMO | ISCSI_RECV_TMO,
ISCSI_PING_TMO | ISCSI_RECV_TMO |
ISCSI_IFACE_NAME | ISCSI_INITIATOR_NAME,
.host_param_mask = ISCSI_HOST_HWADDRESS |
ISCSI_HOST_NETDEV_NAME |
ISCSI_HOST_INITIATOR_NAME,
.host_template = &iscsi_iser_sht,
.conndata_size = sizeof(struct iscsi_conn),
.max_lun = ISCSI_ISER_MAX_LUN,
.max_cmd_len = ISCSI_ISER_MAX_CMD_LEN,
/* session management */
.create_session = iscsi_iser_session_create,
.destroy_session = iscsi_session_teardown,
.destroy_session = iscsi_iser_session_destroy,
/* connection management */
.create_conn = iscsi_iser_conn_create,
.bind_conn = iscsi_iser_conn_bind,
@ -600,17 +641,16 @@ static struct iscsi_transport iscsi_iser_transport = {
.get_conn_param = iscsi_conn_get_param,
.get_session_param = iscsi_session_get_param,
.start_conn = iscsi_iser_conn_start,
.stop_conn = iscsi_conn_stop,
.stop_conn = iscsi_iser_conn_stop,
/* iscsi host params */
.get_host_param = iscsi_host_get_param,
.set_host_param = iscsi_host_set_param,
/* IO */
.send_pdu = iscsi_conn_send_pdu,
.get_stats = iscsi_iser_conn_get_stats,
.init_cmd_task = iscsi_iser_cmd_init,
.xmit_cmd_task = iscsi_iser_ctask_xmit,
.xmit_mgmt_task = iscsi_iser_mtask_xmit,
.cleanup_cmd_task = iscsi_iser_cleanup_ctask,
.init_task = iscsi_iser_task_init,
.xmit_task = iscsi_iser_task_xmit,
.cleanup_task = iscsi_iser_cleanup_task,
/* recovery */
.session_recovery_timedout = iscsi_session_recovery_timedout,
@ -630,8 +670,6 @@ static int __init iser_init(void)
return -EINVAL;
}
iscsi_iser_transport.max_lun = iscsi_max_lun;
memset(&ig, 0, sizeof(struct iser_global));
ig.desc_cache = kmem_cache_create("iser_descriptors",
@ -647,7 +685,9 @@ static int __init iser_init(void)
mutex_init(&ig.connlist_mutex);
INIT_LIST_HEAD(&ig.connlist);
if (!iscsi_register_transport(&iscsi_iser_transport)) {
iscsi_iser_scsi_transport = iscsi_register_transport(
&iscsi_iser_transport);
if (!iscsi_iser_scsi_transport) {
iser_err("iscsi_register_transport failed\n");
err = -EINVAL;
goto register_transport_failure;

View File

@ -94,7 +94,6 @@
/* support upto 512KB in one RDMA */
#define ISCSI_ISER_SG_TABLESIZE (0x80000 >> SHIFT_4K)
#define ISCSI_ISER_MAX_LUN 256
#define ISCSI_ISER_MAX_CMD_LEN 16
/* QP settings */
/* Maximal bounds on received asynchronous PDUs */
@ -172,7 +171,8 @@ struct iser_data_buf {
/* fwd declarations */
struct iser_device;
struct iscsi_iser_conn;
struct iscsi_iser_cmd_task;
struct iscsi_iser_task;
struct iscsi_endpoint;
struct iser_mem_reg {
u32 lkey;
@ -196,7 +196,7 @@ struct iser_regd_buf {
#define MAX_REGD_BUF_VECTOR_LEN 2
struct iser_dto {
struct iscsi_iser_cmd_task *ctask;
struct iscsi_iser_task *task;
struct iser_conn *ib_conn;
int notify_enable;
@ -240,7 +240,9 @@ struct iser_device {
struct iser_conn {
struct iscsi_iser_conn *iser_conn; /* iser conn for upcalls */
struct iscsi_endpoint *ep;
enum iser_ib_conn_state state; /* rdma connection state */
atomic_t refcount;
spinlock_t lock; /* used for state changes */
struct iser_device *device; /* device context */
struct rdma_cm_id *cma_id; /* CMA ID */
@ -259,11 +261,9 @@ struct iser_conn {
struct iscsi_iser_conn {
struct iscsi_conn *iscsi_conn;/* ptr to iscsi conn */
struct iser_conn *ib_conn; /* iSER IB conn */
rwlock_t lock;
};
struct iscsi_iser_cmd_task {
struct iscsi_iser_task {
struct iser_desc desc;
struct iscsi_iser_conn *iser_conn;
enum iser_task_status status;
@ -296,22 +296,26 @@ extern int iser_debug_level;
/* allocate connection resources needed for rdma functionality */
int iser_conn_set_full_featured_mode(struct iscsi_conn *conn);
int iser_send_control(struct iscsi_conn *conn,
struct iscsi_mgmt_task *mtask);
int iser_send_control(struct iscsi_conn *conn,
struct iscsi_task *task);
int iser_send_command(struct iscsi_conn *conn,
struct iscsi_cmd_task *ctask);
int iser_send_command(struct iscsi_conn *conn,
struct iscsi_task *task);
int iser_send_data_out(struct iscsi_conn *conn,
struct iscsi_cmd_task *ctask,
struct iscsi_data *hdr);
int iser_send_data_out(struct iscsi_conn *conn,
struct iscsi_task *task,
struct iscsi_data *hdr);
void iscsi_iser_recv(struct iscsi_conn *conn,
struct iscsi_hdr *hdr,
char *rx_data,
int rx_data_len);
int iser_conn_init(struct iser_conn **ib_conn);
void iser_conn_init(struct iser_conn *ib_conn);
void iser_conn_get(struct iser_conn *ib_conn);
void iser_conn_put(struct iser_conn *ib_conn);
void iser_conn_terminate(struct iser_conn *ib_conn);
@ -320,9 +324,9 @@ void iser_rcv_completion(struct iser_desc *desc,
void iser_snd_completion(struct iser_desc *desc);
void iser_ctask_rdma_init(struct iscsi_iser_cmd_task *ctask);
void iser_task_rdma_init(struct iscsi_iser_task *task);
void iser_ctask_rdma_finalize(struct iscsi_iser_cmd_task *ctask);
void iser_task_rdma_finalize(struct iscsi_iser_task *task);
void iser_dto_buffs_release(struct iser_dto *dto);
@ -332,10 +336,10 @@ void iser_reg_single(struct iser_device *device,
struct iser_regd_buf *regd_buf,
enum dma_data_direction direction);
void iser_finalize_rdma_unaligned_sg(struct iscsi_iser_cmd_task *ctask,
void iser_finalize_rdma_unaligned_sg(struct iscsi_iser_task *task,
enum iser_data_dir cmd_dir);
int iser_reg_rdma_mem(struct iscsi_iser_cmd_task *ctask,
int iser_reg_rdma_mem(struct iscsi_iser_task *task,
enum iser_data_dir cmd_dir);
int iser_connect(struct iser_conn *ib_conn,
@ -355,10 +359,10 @@ int iser_post_send(struct iser_desc *tx_desc);
int iser_conn_state_comp(struct iser_conn *ib_conn,
enum iser_ib_conn_state comp);
int iser_dma_map_task_data(struct iscsi_iser_cmd_task *iser_ctask,
int iser_dma_map_task_data(struct iscsi_iser_task *iser_task,
struct iser_data_buf *data,
enum iser_data_dir iser_dir,
enum dma_data_direction dma_dir);
void iser_dma_unmap_task_data(struct iscsi_iser_cmd_task *iser_ctask);
void iser_dma_unmap_task_data(struct iscsi_iser_task *iser_task);
#endif

View File

@ -64,46 +64,46 @@ static void iser_dto_add_regd_buff(struct iser_dto *dto,
/* Register user buffer memory and initialize passive rdma
* dto descriptor. Total data size is stored in
* iser_ctask->data[ISER_DIR_IN].data_len
* iser_task->data[ISER_DIR_IN].data_len
*/
static int iser_prepare_read_cmd(struct iscsi_cmd_task *ctask,
static int iser_prepare_read_cmd(struct iscsi_task *task,
unsigned int edtl)
{
struct iscsi_iser_cmd_task *iser_ctask = ctask->dd_data;
struct iscsi_iser_task *iser_task = task->dd_data;
struct iser_regd_buf *regd_buf;
int err;
struct iser_hdr *hdr = &iser_ctask->desc.iser_header;
struct iser_data_buf *buf_in = &iser_ctask->data[ISER_DIR_IN];
struct iser_hdr *hdr = &iser_task->desc.iser_header;
struct iser_data_buf *buf_in = &iser_task->data[ISER_DIR_IN];
err = iser_dma_map_task_data(iser_ctask,
err = iser_dma_map_task_data(iser_task,
buf_in,
ISER_DIR_IN,
DMA_FROM_DEVICE);
if (err)
return err;
if (edtl > iser_ctask->data[ISER_DIR_IN].data_len) {
if (edtl > iser_task->data[ISER_DIR_IN].data_len) {
iser_err("Total data length: %ld, less than EDTL: "
"%d, in READ cmd BHS itt: %d, conn: 0x%p\n",
iser_ctask->data[ISER_DIR_IN].data_len, edtl,
ctask->itt, iser_ctask->iser_conn);
iser_task->data[ISER_DIR_IN].data_len, edtl,
task->itt, iser_task->iser_conn);
return -EINVAL;
}
err = iser_reg_rdma_mem(iser_ctask,ISER_DIR_IN);
err = iser_reg_rdma_mem(iser_task,ISER_DIR_IN);
if (err) {
iser_err("Failed to set up Data-IN RDMA\n");
return err;
}
regd_buf = &iser_ctask->rdma_regd[ISER_DIR_IN];
regd_buf = &iser_task->rdma_regd[ISER_DIR_IN];
hdr->flags |= ISER_RSV;
hdr->read_stag = cpu_to_be32(regd_buf->reg.rkey);
hdr->read_va = cpu_to_be64(regd_buf->reg.va);
iser_dbg("Cmd itt:%d READ tags RKEY:%#.4X VA:%#llX\n",
ctask->itt, regd_buf->reg.rkey,
task->itt, regd_buf->reg.rkey,
(unsigned long long)regd_buf->reg.va);
return 0;
@ -111,43 +111,43 @@ static int iser_prepare_read_cmd(struct iscsi_cmd_task *ctask,
/* Register user buffer memory and initialize passive rdma
* dto descriptor. Total data size is stored in
* ctask->data[ISER_DIR_OUT].data_len
* task->data[ISER_DIR_OUT].data_len
*/
static int
iser_prepare_write_cmd(struct iscsi_cmd_task *ctask,
iser_prepare_write_cmd(struct iscsi_task *task,
unsigned int imm_sz,
unsigned int unsol_sz,
unsigned int edtl)
{
struct iscsi_iser_cmd_task *iser_ctask = ctask->dd_data;
struct iscsi_iser_task *iser_task = task->dd_data;
struct iser_regd_buf *regd_buf;
int err;
struct iser_dto *send_dto = &iser_ctask->desc.dto;
struct iser_hdr *hdr = &iser_ctask->desc.iser_header;
struct iser_data_buf *buf_out = &iser_ctask->data[ISER_DIR_OUT];
struct iser_dto *send_dto = &iser_task->desc.dto;
struct iser_hdr *hdr = &iser_task->desc.iser_header;
struct iser_data_buf *buf_out = &iser_task->data[ISER_DIR_OUT];
err = iser_dma_map_task_data(iser_ctask,
err = iser_dma_map_task_data(iser_task,
buf_out,
ISER_DIR_OUT,
DMA_TO_DEVICE);
if (err)
return err;
if (edtl > iser_ctask->data[ISER_DIR_OUT].data_len) {
if (edtl > iser_task->data[ISER_DIR_OUT].data_len) {
iser_err("Total data length: %ld, less than EDTL: %d, "
"in WRITE cmd BHS itt: %d, conn: 0x%p\n",
iser_ctask->data[ISER_DIR_OUT].data_len,
edtl, ctask->itt, ctask->conn);
iser_task->data[ISER_DIR_OUT].data_len,
edtl, task->itt, task->conn);
return -EINVAL;
}
err = iser_reg_rdma_mem(iser_ctask,ISER_DIR_OUT);
err = iser_reg_rdma_mem(iser_task,ISER_DIR_OUT);
if (err != 0) {
iser_err("Failed to register write cmd RDMA mem\n");
return err;
}
regd_buf = &iser_ctask->rdma_regd[ISER_DIR_OUT];
regd_buf = &iser_task->rdma_regd[ISER_DIR_OUT];
if (unsol_sz < edtl) {
hdr->flags |= ISER_WSV;
@ -156,13 +156,13 @@ iser_prepare_write_cmd(struct iscsi_cmd_task *ctask,
iser_dbg("Cmd itt:%d, WRITE tags, RKEY:%#.4X "
"VA:%#llX + unsol:%d\n",
ctask->itt, regd_buf->reg.rkey,
task->itt, regd_buf->reg.rkey,
(unsigned long long)regd_buf->reg.va, unsol_sz);
}
if (imm_sz > 0) {
iser_dbg("Cmd itt:%d, WRITE, adding imm.data sz: %d\n",
ctask->itt, imm_sz);
task->itt, imm_sz);
iser_dto_add_regd_buff(send_dto,
regd_buf,
0,
@ -314,38 +314,38 @@ iser_check_xmit(struct iscsi_conn *conn, void *task)
/**
* iser_send_command - send command PDU
*/
int iser_send_command(struct iscsi_conn *conn,
struct iscsi_cmd_task *ctask)
int iser_send_command(struct iscsi_conn *conn,
struct iscsi_task *task)
{
struct iscsi_iser_conn *iser_conn = conn->dd_data;
struct iscsi_iser_cmd_task *iser_ctask = ctask->dd_data;
struct iscsi_iser_task *iser_task = task->dd_data;
struct iser_dto *send_dto = NULL;
unsigned long edtl;
int err = 0;
struct iser_data_buf *data_buf;
struct iscsi_cmd *hdr = ctask->hdr;
struct scsi_cmnd *sc = ctask->sc;
struct iscsi_cmd *hdr = task->hdr;
struct scsi_cmnd *sc = task->sc;
if (!iser_conn_state_comp(iser_conn->ib_conn, ISER_CONN_UP)) {
iser_err("Failed to send, conn: 0x%p is not up\n", iser_conn->ib_conn);
return -EPERM;
}
if (iser_check_xmit(conn, ctask))
if (iser_check_xmit(conn, task))
return -ENOBUFS;
edtl = ntohl(hdr->data_length);
/* build the tx desc regd header and add it to the tx desc dto */
iser_ctask->desc.type = ISCSI_TX_SCSI_COMMAND;
send_dto = &iser_ctask->desc.dto;
send_dto->ctask = iser_ctask;
iser_create_send_desc(iser_conn, &iser_ctask->desc);
iser_task->desc.type = ISCSI_TX_SCSI_COMMAND;
send_dto = &iser_task->desc.dto;
send_dto->task = iser_task;
iser_create_send_desc(iser_conn, &iser_task->desc);
if (hdr->flags & ISCSI_FLAG_CMD_READ)
data_buf = &iser_ctask->data[ISER_DIR_IN];
data_buf = &iser_task->data[ISER_DIR_IN];
else
data_buf = &iser_ctask->data[ISER_DIR_OUT];
data_buf = &iser_task->data[ISER_DIR_OUT];
if (scsi_sg_count(sc)) { /* using a scatter list */
data_buf->buf = scsi_sglist(sc);
@ -355,15 +355,15 @@ int iser_send_command(struct iscsi_conn *conn,
data_buf->data_len = scsi_bufflen(sc);
if (hdr->flags & ISCSI_FLAG_CMD_READ) {
err = iser_prepare_read_cmd(ctask, edtl);
err = iser_prepare_read_cmd(task, edtl);
if (err)
goto send_command_error;
}
if (hdr->flags & ISCSI_FLAG_CMD_WRITE) {
err = iser_prepare_write_cmd(ctask,
ctask->imm_count,
ctask->imm_count +
ctask->unsol_count,
err = iser_prepare_write_cmd(task,
task->imm_count,
task->imm_count +
task->unsol_count,
edtl);
if (err)
goto send_command_error;
@ -378,27 +378,27 @@ int iser_send_command(struct iscsi_conn *conn,
goto send_command_error;
}
iser_ctask->status = ISER_TASK_STATUS_STARTED;
iser_task->status = ISER_TASK_STATUS_STARTED;
err = iser_post_send(&iser_ctask->desc);
err = iser_post_send(&iser_task->desc);
if (!err)
return 0;
send_command_error:
iser_dto_buffs_release(send_dto);
iser_err("conn %p failed ctask->itt %d err %d\n",conn, ctask->itt, err);
iser_err("conn %p failed task->itt %d err %d\n",conn, task->itt, err);
return err;
}
/**
* iser_send_data_out - send data out PDU
*/
int iser_send_data_out(struct iscsi_conn *conn,
struct iscsi_cmd_task *ctask,
int iser_send_data_out(struct iscsi_conn *conn,
struct iscsi_task *task,
struct iscsi_data *hdr)
{
struct iscsi_iser_conn *iser_conn = conn->dd_data;
struct iscsi_iser_cmd_task *iser_ctask = ctask->dd_data;
struct iscsi_iser_task *iser_task = task->dd_data;
struct iser_desc *tx_desc = NULL;
struct iser_dto *send_dto = NULL;
unsigned long buf_offset;
@ -411,7 +411,7 @@ int iser_send_data_out(struct iscsi_conn *conn,
return -EPERM;
}
if (iser_check_xmit(conn, ctask))
if (iser_check_xmit(conn, task))
return -ENOBUFS;
itt = (__force uint32_t)hdr->itt;
@ -432,7 +432,7 @@ int iser_send_data_out(struct iscsi_conn *conn,
/* build the tx desc regd header and add it to the tx desc dto */
send_dto = &tx_desc->dto;
send_dto->ctask = iser_ctask;
send_dto->task = iser_task;
iser_create_send_desc(iser_conn, tx_desc);
iser_reg_single(iser_conn->ib_conn->device,
@ -440,15 +440,15 @@ int iser_send_data_out(struct iscsi_conn *conn,
/* all data was registered for RDMA, we can use the lkey */
iser_dto_add_regd_buff(send_dto,
&iser_ctask->rdma_regd[ISER_DIR_OUT],
&iser_task->rdma_regd[ISER_DIR_OUT],
buf_offset,
data_seg_len);
if (buf_offset + data_seg_len > iser_ctask->data[ISER_DIR_OUT].data_len) {
if (buf_offset + data_seg_len > iser_task->data[ISER_DIR_OUT].data_len) {
iser_err("Offset:%ld & DSL:%ld in Data-Out "
"inconsistent with total len:%ld, itt:%d\n",
buf_offset, data_seg_len,
iser_ctask->data[ISER_DIR_OUT].data_len, itt);
iser_task->data[ISER_DIR_OUT].data_len, itt);
err = -EINVAL;
goto send_data_out_error;
}
@ -468,10 +468,11 @@ int iser_send_data_out(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 iser_desc *mdesc = mtask->dd_data;
struct iscsi_iser_task *iser_task = task->dd_data;
struct iser_desc *mdesc = &iser_task->desc;
struct iser_dto *send_dto = NULL;
unsigned long data_seg_len;
int err = 0;
@ -483,27 +484,27 @@ int iser_send_control(struct iscsi_conn *conn,
return -EPERM;
}
if (iser_check_xmit(conn,mtask))
if (iser_check_xmit(conn, task))
return -ENOBUFS;
/* build the tx desc regd header and add it to the tx desc dto */
mdesc->type = ISCSI_TX_CONTROL;
send_dto = &mdesc->dto;
send_dto->ctask = NULL;
send_dto->task = NULL;
iser_create_send_desc(iser_conn, mdesc);
device = iser_conn->ib_conn->device;
iser_reg_single(device, send_dto->regd[0], DMA_TO_DEVICE);
data_seg_len = ntoh24(mtask->hdr->dlength);
data_seg_len = ntoh24(task->hdr->dlength);
if (data_seg_len > 0) {
regd_buf = &mdesc->data_regd_buf;
memset(regd_buf, 0, sizeof(struct iser_regd_buf));
regd_buf->device = device;
regd_buf->virt_addr = mtask->data;
regd_buf->data_size = mtask->data_count;
regd_buf->virt_addr = task->data;
regd_buf->data_size = task->data_count;
iser_reg_single(device, regd_buf,
DMA_TO_DEVICE);
iser_dto_add_regd_buff(send_dto, regd_buf,
@ -533,15 +534,13 @@ int iser_send_control(struct iscsi_conn *conn,
void iser_rcv_completion(struct iser_desc *rx_desc,
unsigned long dto_xfer_len)
{
struct iser_dto *dto = &rx_desc->dto;
struct iser_dto *dto = &rx_desc->dto;
struct iscsi_iser_conn *conn = dto->ib_conn->iser_conn;
struct iscsi_session *session = conn->iscsi_conn->session;
struct iscsi_cmd_task *ctask;
struct iscsi_iser_cmd_task *iser_ctask;
struct iscsi_task *task;
struct iscsi_iser_task *iser_task;
struct iscsi_hdr *hdr;
char *rx_data = NULL;
int rx_data_len = 0;
unsigned int itt;
unsigned char opcode;
hdr = &rx_desc->iscsi_header;
@ -557,19 +556,24 @@ void iser_rcv_completion(struct iser_desc *rx_desc,
opcode = hdr->opcode & ISCSI_OPCODE_MASK;
if (opcode == ISCSI_OP_SCSI_CMD_RSP) {
itt = get_itt(hdr->itt); /* mask out cid and age bits */
if (!(itt < session->cmds_max))
iser_err("itt can't be matched to task!!! "
"conn %p opcode %d cmds_max %d itt %d\n",
conn->iscsi_conn,opcode,session->cmds_max,itt);
/* use the mapping given with the cmds array indexed by itt */
ctask = (struct iscsi_cmd_task *)session->cmds[itt];
iser_ctask = ctask->dd_data;
iser_dbg("itt %d ctask %p\n",itt,ctask);
iser_ctask->status = ISER_TASK_STATUS_COMPLETED;
iser_ctask_rdma_finalize(iser_ctask);
}
spin_lock(&conn->iscsi_conn->session->lock);
task = iscsi_itt_to_ctask(conn->iscsi_conn, hdr->itt);
if (task)
__iscsi_get_task(task);
spin_unlock(&conn->iscsi_conn->session->lock);
if (!task)
iser_err("itt can't be matched to task!!! "
"conn %p opcode %d itt %d\n",
conn->iscsi_conn, opcode, hdr->itt);
else {
iser_task = task->dd_data;
iser_dbg("itt %d task %p\n",hdr->itt, task);
iser_task->status = ISER_TASK_STATUS_COMPLETED;
iser_task_rdma_finalize(iser_task);
iscsi_put_task(task);
}
}
iser_dto_buffs_release(dto);
iscsi_iser_recv(conn->iscsi_conn, hdr, rx_data, rx_data_len);
@ -590,7 +594,7 @@ void iser_snd_completion(struct iser_desc *tx_desc)
struct iser_conn *ib_conn = dto->ib_conn;
struct iscsi_iser_conn *iser_conn = ib_conn->iser_conn;
struct iscsi_conn *conn = iser_conn->iscsi_conn;
struct iscsi_mgmt_task *mtask;
struct iscsi_task *task;
int resume_tx = 0;
iser_dbg("Initiator, Data sent dto=0x%p\n", dto);
@ -613,36 +617,31 @@ void iser_snd_completion(struct iser_desc *tx_desc)
if (tx_desc->type == ISCSI_TX_CONTROL) {
/* this arithmetic is legal by libiscsi dd_data allocation */
mtask = (void *) ((long)(void *)tx_desc -
sizeof(struct iscsi_mgmt_task));
if (mtask->hdr->itt == RESERVED_ITT) {
struct iscsi_session *session = conn->session;
spin_lock(&conn->session->lock);
iscsi_free_mgmt_task(conn, mtask);
spin_unlock(&session->lock);
}
task = (void *) ((long)(void *)tx_desc -
sizeof(struct iscsi_task));
if (task->hdr->itt == RESERVED_ITT)
iscsi_put_task(task);
}
}
void iser_ctask_rdma_init(struct iscsi_iser_cmd_task *iser_ctask)
void iser_task_rdma_init(struct iscsi_iser_task *iser_task)
{
iser_ctask->status = ISER_TASK_STATUS_INIT;
iser_task->status = ISER_TASK_STATUS_INIT;
iser_ctask->dir[ISER_DIR_IN] = 0;
iser_ctask->dir[ISER_DIR_OUT] = 0;
iser_task->dir[ISER_DIR_IN] = 0;
iser_task->dir[ISER_DIR_OUT] = 0;
iser_ctask->data[ISER_DIR_IN].data_len = 0;
iser_ctask->data[ISER_DIR_OUT].data_len = 0;
iser_task->data[ISER_DIR_IN].data_len = 0;
iser_task->data[ISER_DIR_OUT].data_len = 0;
memset(&iser_ctask->rdma_regd[ISER_DIR_IN], 0,
memset(&iser_task->rdma_regd[ISER_DIR_IN], 0,
sizeof(struct iser_regd_buf));
memset(&iser_ctask->rdma_regd[ISER_DIR_OUT], 0,
memset(&iser_task->rdma_regd[ISER_DIR_OUT], 0,
sizeof(struct iser_regd_buf));
}
void iser_ctask_rdma_finalize(struct iscsi_iser_cmd_task *iser_ctask)
void iser_task_rdma_finalize(struct iscsi_iser_task *iser_task)
{
int deferred;
int is_rdma_aligned = 1;
@ -651,17 +650,17 @@ void iser_ctask_rdma_finalize(struct iscsi_iser_cmd_task *iser_ctask)
/* if we were reading, copy back to unaligned sglist,
* anyway dma_unmap and free the copy
*/
if (iser_ctask->data_copy[ISER_DIR_IN].copy_buf != NULL) {
if (iser_task->data_copy[ISER_DIR_IN].copy_buf != NULL) {
is_rdma_aligned = 0;
iser_finalize_rdma_unaligned_sg(iser_ctask, ISER_DIR_IN);
iser_finalize_rdma_unaligned_sg(iser_task, ISER_DIR_IN);
}
if (iser_ctask->data_copy[ISER_DIR_OUT].copy_buf != NULL) {
if (iser_task->data_copy[ISER_DIR_OUT].copy_buf != NULL) {
is_rdma_aligned = 0;
iser_finalize_rdma_unaligned_sg(iser_ctask, ISER_DIR_OUT);
iser_finalize_rdma_unaligned_sg(iser_task, ISER_DIR_OUT);
}
if (iser_ctask->dir[ISER_DIR_IN]) {
regd = &iser_ctask->rdma_regd[ISER_DIR_IN];
if (iser_task->dir[ISER_DIR_IN]) {
regd = &iser_task->rdma_regd[ISER_DIR_IN];
deferred = iser_regd_buff_release(regd);
if (deferred) {
iser_err("%d references remain for BUF-IN rdma reg\n",
@ -669,8 +668,8 @@ void iser_ctask_rdma_finalize(struct iscsi_iser_cmd_task *iser_ctask)
}
}
if (iser_ctask->dir[ISER_DIR_OUT]) {
regd = &iser_ctask->rdma_regd[ISER_DIR_OUT];
if (iser_task->dir[ISER_DIR_OUT]) {
regd = &iser_task->rdma_regd[ISER_DIR_OUT];
deferred = iser_regd_buff_release(regd);
if (deferred) {
iser_err("%d references remain for BUF-OUT rdma reg\n",
@ -680,7 +679,7 @@ void iser_ctask_rdma_finalize(struct iscsi_iser_cmd_task *iser_ctask)
/* if the data was unaligned, it was already unmapped and then copied */
if (is_rdma_aligned)
iser_dma_unmap_task_data(iser_ctask);
iser_dma_unmap_task_data(iser_task);
}
void iser_dto_buffs_release(struct iser_dto *dto)

View File

@ -99,13 +99,13 @@ void iser_reg_single(struct iser_device *device,
/**
* iser_start_rdma_unaligned_sg
*/
static int iser_start_rdma_unaligned_sg(struct iscsi_iser_cmd_task *iser_ctask,
static int iser_start_rdma_unaligned_sg(struct iscsi_iser_task *iser_task,
enum iser_data_dir cmd_dir)
{
int dma_nents;
struct ib_device *dev;
char *mem = NULL;
struct iser_data_buf *data = &iser_ctask->data[cmd_dir];
struct iser_data_buf *data = &iser_task->data[cmd_dir];
unsigned long cmd_data_len = data->data_len;
if (cmd_data_len > ISER_KMALLOC_THRESHOLD)
@ -138,37 +138,37 @@ static int iser_start_rdma_unaligned_sg(struct iscsi_iser_cmd_task *iser_ctask,
}
}
sg_init_one(&iser_ctask->data_copy[cmd_dir].sg_single, mem, cmd_data_len);
iser_ctask->data_copy[cmd_dir].buf =
&iser_ctask->data_copy[cmd_dir].sg_single;
iser_ctask->data_copy[cmd_dir].size = 1;
sg_init_one(&iser_task->data_copy[cmd_dir].sg_single, mem, cmd_data_len);
iser_task->data_copy[cmd_dir].buf =
&iser_task->data_copy[cmd_dir].sg_single;
iser_task->data_copy[cmd_dir].size = 1;
iser_ctask->data_copy[cmd_dir].copy_buf = mem;
iser_task->data_copy[cmd_dir].copy_buf = mem;
dev = iser_ctask->iser_conn->ib_conn->device->ib_device;
dev = iser_task->iser_conn->ib_conn->device->ib_device;
dma_nents = ib_dma_map_sg(dev,
&iser_ctask->data_copy[cmd_dir].sg_single,
&iser_task->data_copy[cmd_dir].sg_single,
1,
(cmd_dir == ISER_DIR_OUT) ?
DMA_TO_DEVICE : DMA_FROM_DEVICE);
BUG_ON(dma_nents == 0);
iser_ctask->data_copy[cmd_dir].dma_nents = dma_nents;
iser_task->data_copy[cmd_dir].dma_nents = dma_nents;
return 0;
}
/**
* iser_finalize_rdma_unaligned_sg
*/
void iser_finalize_rdma_unaligned_sg(struct iscsi_iser_cmd_task *iser_ctask,
void iser_finalize_rdma_unaligned_sg(struct iscsi_iser_task *iser_task,
enum iser_data_dir cmd_dir)
{
struct ib_device *dev;
struct iser_data_buf *mem_copy;
unsigned long cmd_data_len;
dev = iser_ctask->iser_conn->ib_conn->device->ib_device;
mem_copy = &iser_ctask->data_copy[cmd_dir];
dev = iser_task->iser_conn->ib_conn->device->ib_device;
mem_copy = &iser_task->data_copy[cmd_dir];
ib_dma_unmap_sg(dev, &mem_copy->sg_single, 1,
(cmd_dir == ISER_DIR_OUT) ?
@ -184,8 +184,8 @@ void iser_finalize_rdma_unaligned_sg(struct iscsi_iser_cmd_task *iser_ctask,
/* copy back read RDMA to unaligned sg */
mem = mem_copy->copy_buf;
sgl = (struct scatterlist *)iser_ctask->data[ISER_DIR_IN].buf;
sg_size = iser_ctask->data[ISER_DIR_IN].size;
sgl = (struct scatterlist *)iser_task->data[ISER_DIR_IN].buf;
sg_size = iser_task->data[ISER_DIR_IN].size;
p = mem;
for_each_sg(sgl, sg, sg_size, i) {
@ -198,7 +198,7 @@ void iser_finalize_rdma_unaligned_sg(struct iscsi_iser_cmd_task *iser_ctask,
}
}
cmd_data_len = iser_ctask->data[cmd_dir].data_len;
cmd_data_len = iser_task->data[cmd_dir].data_len;
if (cmd_data_len > ISER_KMALLOC_THRESHOLD)
free_pages((unsigned long)mem_copy->copy_buf,
@ -376,15 +376,15 @@ static void iser_page_vec_build(struct iser_data_buf *data,
}
}
int iser_dma_map_task_data(struct iscsi_iser_cmd_task *iser_ctask,
struct iser_data_buf *data,
enum iser_data_dir iser_dir,
enum dma_data_direction dma_dir)
int iser_dma_map_task_data(struct iscsi_iser_task *iser_task,
struct iser_data_buf *data,
enum iser_data_dir iser_dir,
enum dma_data_direction dma_dir)
{
struct ib_device *dev;
iser_ctask->dir[iser_dir] = 1;
dev = iser_ctask->iser_conn->ib_conn->device->ib_device;
iser_task->dir[iser_dir] = 1;
dev = iser_task->iser_conn->ib_conn->device->ib_device;
data->dma_nents = ib_dma_map_sg(dev, data->buf, data->size, dma_dir);
if (data->dma_nents == 0) {
@ -394,20 +394,20 @@ int iser_dma_map_task_data(struct iscsi_iser_cmd_task *iser_ctask,
return 0;
}
void iser_dma_unmap_task_data(struct iscsi_iser_cmd_task *iser_ctask)
void iser_dma_unmap_task_data(struct iscsi_iser_task *iser_task)
{
struct ib_device *dev;
struct iser_data_buf *data;
dev = iser_ctask->iser_conn->ib_conn->device->ib_device;
dev = iser_task->iser_conn->ib_conn->device->ib_device;
if (iser_ctask->dir[ISER_DIR_IN]) {
data = &iser_ctask->data[ISER_DIR_IN];
if (iser_task->dir[ISER_DIR_IN]) {
data = &iser_task->data[ISER_DIR_IN];
ib_dma_unmap_sg(dev, data->buf, data->size, DMA_FROM_DEVICE);
}
if (iser_ctask->dir[ISER_DIR_OUT]) {
data = &iser_ctask->data[ISER_DIR_OUT];
if (iser_task->dir[ISER_DIR_OUT]) {
data = &iser_task->data[ISER_DIR_OUT];
ib_dma_unmap_sg(dev, data->buf, data->size, DMA_TO_DEVICE);
}
}
@ -418,21 +418,21 @@ void iser_dma_unmap_task_data(struct iscsi_iser_cmd_task *iser_ctask)
*
* returns 0 on success, errno code on failure
*/
int iser_reg_rdma_mem(struct iscsi_iser_cmd_task *iser_ctask,
int iser_reg_rdma_mem(struct iscsi_iser_task *iser_task,
enum iser_data_dir cmd_dir)
{
struct iscsi_conn *iscsi_conn = iser_ctask->iser_conn->iscsi_conn;
struct iser_conn *ib_conn = iser_ctask->iser_conn->ib_conn;
struct iscsi_conn *iscsi_conn = iser_task->iser_conn->iscsi_conn;
struct iser_conn *ib_conn = iser_task->iser_conn->ib_conn;
struct iser_device *device = ib_conn->device;
struct ib_device *ibdev = device->ib_device;
struct iser_data_buf *mem = &iser_ctask->data[cmd_dir];
struct iser_data_buf *mem = &iser_task->data[cmd_dir];
struct iser_regd_buf *regd_buf;
int aligned_len;
int err;
int i;
struct scatterlist *sg;
regd_buf = &iser_ctask->rdma_regd[cmd_dir];
regd_buf = &iser_task->rdma_regd[cmd_dir];
aligned_len = iser_data_buf_aligned_len(mem, ibdev);
if (aligned_len != mem->dma_nents) {
@ -442,13 +442,13 @@ int iser_reg_rdma_mem(struct iscsi_iser_cmd_task *iser_ctask,
iser_data_buf_dump(mem, ibdev);
/* unmap the command data before accessing it */
iser_dma_unmap_task_data(iser_ctask);
iser_dma_unmap_task_data(iser_task);
/* allocate copy buf, if we are writing, copy the */
/* unaligned scatterlist, dma map the copy */
if (iser_start_rdma_unaligned_sg(iser_ctask, cmd_dir) != 0)
if (iser_start_rdma_unaligned_sg(iser_task, cmd_dir) != 0)
return -ENOMEM;
mem = &iser_ctask->data_copy[cmd_dir];
mem = &iser_task->data_copy[cmd_dir];
}
/* if there a single dma entry, FMR is not needed */
@ -472,8 +472,9 @@ int iser_reg_rdma_mem(struct iscsi_iser_cmd_task *iser_ctask,
err = iser_reg_page_vec(ib_conn, ib_conn->page_vec, &regd_buf->reg);
if (err) {
iser_data_buf_dump(mem, ibdev);
iser_err("mem->dma_nents = %d (dlength = 0x%x)\n", mem->dma_nents,
ntoh24(iser_ctask->desc.iscsi_header.dlength));
iser_err("mem->dma_nents = %d (dlength = 0x%x)\n",
mem->dma_nents,
ntoh24(iser_task->desc.iscsi_header.dlength));
iser_err("page_vec: data_size = 0x%x, length = %d, offset = 0x%x\n",
ib_conn->page_vec->data_size, ib_conn->page_vec->length,
ib_conn->page_vec->offset);

View File

@ -323,7 +323,18 @@ static void iser_conn_release(struct iser_conn *ib_conn)
iser_device_try_release(device);
if (ib_conn->iser_conn)
ib_conn->iser_conn->ib_conn = NULL;
kfree(ib_conn);
iscsi_destroy_endpoint(ib_conn->ep);
}
void iser_conn_get(struct iser_conn *ib_conn)
{
atomic_inc(&ib_conn->refcount);
}
void iser_conn_put(struct iser_conn *ib_conn)
{
if (atomic_dec_and_test(&ib_conn->refcount))
iser_conn_release(ib_conn);
}
/**
@ -347,7 +358,7 @@ void iser_conn_terminate(struct iser_conn *ib_conn)
wait_event_interruptible(ib_conn->wait,
ib_conn->state == ISER_CONN_DOWN);
iser_conn_release(ib_conn);
iser_conn_put(ib_conn);
}
static void iser_connect_error(struct rdma_cm_id *cma_id)
@ -481,24 +492,15 @@ static int iser_cma_handler(struct rdma_cm_id *cma_id, struct rdma_cm_event *eve
return ret;
}
int iser_conn_init(struct iser_conn **ibconn)
void iser_conn_init(struct iser_conn *ib_conn)
{
struct iser_conn *ib_conn;
ib_conn = kzalloc(sizeof *ib_conn, GFP_KERNEL);
if (!ib_conn) {
iser_err("can't alloc memory for struct iser_conn\n");
return -ENOMEM;
}
ib_conn->state = ISER_CONN_INIT;
init_waitqueue_head(&ib_conn->wait);
atomic_set(&ib_conn->post_recv_buf_count, 0);
atomic_set(&ib_conn->post_send_buf_count, 0);
atomic_set(&ib_conn->refcount, 1);
INIT_LIST_HEAD(&ib_conn->conn_list);
spin_lock_init(&ib_conn->lock);
*ibconn = ib_conn;
return 0;
}
/**

View File

@ -252,27 +252,14 @@ config DM_ZERO
config DM_MULTIPATH
tristate "Multipath target"
depends on BLK_DEV_DM
# nasty syntax but means make DM_MULTIPATH independent
# of SCSI_DH if the latter isn't defined but if
# it is, DM_MULTIPATH must depend on it. We get a build
# error if SCSI_DH=m and DM_MULTIPATH=y
depends on SCSI_DH || !SCSI_DH
---help---
Allow volume managers to support multipath hardware.
config DM_MULTIPATH_EMC
tristate "EMC CX/AX multipath support"
depends on DM_MULTIPATH && BLK_DEV_DM
---help---
Multipath support for EMC CX/AX series hardware.
config DM_MULTIPATH_RDAC
tristate "LSI/Engenio RDAC multipath support (EXPERIMENTAL)"
depends on DM_MULTIPATH && BLK_DEV_DM && SCSI && EXPERIMENTAL
---help---
Multipath support for LSI/Engenio RDAC.
config DM_MULTIPATH_HP
tristate "HP MSA multipath support (EXPERIMENTAL)"
depends on DM_MULTIPATH && BLK_DEV_DM && SCSI && EXPERIMENTAL
---help---
Multipath support for HP MSA (Active/Passive) series hardware.
config DM_DELAY
tristate "I/O delaying target (EXPERIMENTAL)"
depends on BLK_DEV_DM && EXPERIMENTAL

View File

@ -4,11 +4,9 @@
dm-mod-objs := dm.o dm-table.o dm-target.o dm-linear.o dm-stripe.o \
dm-ioctl.o dm-io.o dm-kcopyd.o
dm-multipath-objs := dm-hw-handler.o dm-path-selector.o dm-mpath.o
dm-multipath-objs := dm-path-selector.o dm-mpath.o
dm-snapshot-objs := dm-snap.o dm-exception-store.o
dm-mirror-objs := dm-raid1.o
dm-rdac-objs := dm-mpath-rdac.o
dm-hp-sw-objs := dm-mpath-hp-sw.o
md-mod-objs := md.o bitmap.o
raid456-objs := raid5.o raid6algos.o raid6recov.o raid6tables.o \
raid6int1.o raid6int2.o raid6int4.o \
@ -35,9 +33,6 @@ obj-$(CONFIG_BLK_DEV_DM) += dm-mod.o
obj-$(CONFIG_DM_CRYPT) += dm-crypt.o
obj-$(CONFIG_DM_DELAY) += dm-delay.o
obj-$(CONFIG_DM_MULTIPATH) += dm-multipath.o dm-round-robin.o
obj-$(CONFIG_DM_MULTIPATH_EMC) += dm-emc.o
obj-$(CONFIG_DM_MULTIPATH_HP) += dm-hp-sw.o
obj-$(CONFIG_DM_MULTIPATH_RDAC) += dm-rdac.o
obj-$(CONFIG_DM_SNAPSHOT) += dm-snapshot.o
obj-$(CONFIG_DM_MIRROR) += dm-mirror.o dm-log.o
obj-$(CONFIG_DM_ZERO) += dm-zero.o

View File

@ -1,345 +0,0 @@
/*
* Copyright (C) 2004 SUSE LINUX Products GmbH. All rights reserved.
* Copyright (C) 2004 Red Hat, Inc. All rights reserved.
*
* This file is released under the GPL.
*
* Multipath support for EMC CLARiiON AX/CX-series hardware.
*/
#include "dm.h"
#include "dm-hw-handler.h"
#include <scsi/scsi.h>
#include <scsi/scsi_cmnd.h>
#define DM_MSG_PREFIX "multipath emc"
struct emc_handler {
spinlock_t lock;
/* Whether we should send the short trespass command (FC-series)
* or the long version (default for AX/CX CLARiiON arrays). */
unsigned short_trespass;
/* Whether or not to honor SCSI reservations when initiating a
* switch-over. Default: Don't. */
unsigned hr;
unsigned char sense[SCSI_SENSE_BUFFERSIZE];
};
#define TRESPASS_PAGE 0x22
#define EMC_FAILOVER_TIMEOUT (60 * HZ)
/* Code borrowed from dm-lsi-rdac by Mike Christie */
static inline void free_bio(struct bio *bio)
{
__free_page(bio->bi_io_vec[0].bv_page);
bio_put(bio);
}
static void emc_endio(struct bio *bio, int error)
{
struct dm_path *path = bio->bi_private;
/* We also need to look at the sense keys here whether or not to
* switch to the next PG etc.
*
* For now simple logic: either it works or it doesn't.
*/
if (error)
dm_pg_init_complete(path, MP_FAIL_PATH);
else
dm_pg_init_complete(path, 0);
/* request is freed in block layer */
free_bio(bio);
}
static struct bio *get_failover_bio(struct dm_path *path, unsigned data_size)
{
struct bio *bio;
struct page *page;
bio = bio_alloc(GFP_ATOMIC, 1);
if (!bio) {
DMERR("get_failover_bio: bio_alloc() failed.");
return NULL;
}
bio->bi_rw |= (1 << BIO_RW);
bio->bi_bdev = path->dev->bdev;
bio->bi_sector = 0;
bio->bi_private = path;
bio->bi_end_io = emc_endio;
page = alloc_page(GFP_ATOMIC);
if (!page) {
DMERR("get_failover_bio: alloc_page() failed.");
bio_put(bio);
return NULL;
}
if (bio_add_page(bio, page, data_size, 0) != data_size) {
DMERR("get_failover_bio: bio_add_page() failed.");
__free_page(page);
bio_put(bio);
return NULL;
}
return bio;
}
static struct request *get_failover_req(struct emc_handler *h,
struct bio *bio, struct dm_path *path)
{
struct request *rq;
struct block_device *bdev = bio->bi_bdev;
struct request_queue *q = bdev_get_queue(bdev);
/* FIXME: Figure out why it fails with GFP_ATOMIC. */
rq = blk_get_request(q, WRITE, __GFP_WAIT);
if (!rq) {
DMERR("get_failover_req: blk_get_request failed");
return NULL;
}
blk_rq_append_bio(q, rq, bio);
rq->sense = h->sense;
memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE);
rq->sense_len = 0;
rq->timeout = EMC_FAILOVER_TIMEOUT;
rq->cmd_type = REQ_TYPE_BLOCK_PC;
rq->cmd_flags |= REQ_FAILFAST | REQ_NOMERGE;
return rq;
}
static struct request *emc_trespass_get(struct emc_handler *h,
struct dm_path *path)
{
struct bio *bio;
struct request *rq;
unsigned char *page22;
unsigned char long_trespass_pg[] = {
0, 0, 0, 0,
TRESPASS_PAGE, /* Page code */
0x09, /* Page length - 2 */
h->hr ? 0x01 : 0x81, /* Trespass code + Honor reservation bit */
0xff, 0xff, /* Trespass target */
0, 0, 0, 0, 0, 0 /* Reserved bytes / unknown */
};
unsigned char short_trespass_pg[] = {
0, 0, 0, 0,
TRESPASS_PAGE, /* Page code */
0x02, /* Page length - 2 */
h->hr ? 0x01 : 0x81, /* Trespass code + Honor reservation bit */
0xff, /* Trespass target */
};
unsigned data_size = h->short_trespass ? sizeof(short_trespass_pg) :
sizeof(long_trespass_pg);
/* get bio backing */
if (data_size > PAGE_SIZE)
/* this should never happen */
return NULL;
bio = get_failover_bio(path, data_size);
if (!bio) {
DMERR("emc_trespass_get: no bio");
return NULL;
}
page22 = (unsigned char *)bio_data(bio);
memset(page22, 0, data_size);
memcpy(page22, h->short_trespass ?
short_trespass_pg : long_trespass_pg, data_size);
/* get request for block layer packet command */
rq = get_failover_req(h, bio, path);
if (!rq) {
DMERR("emc_trespass_get: no rq");
free_bio(bio);
return NULL;
}
/* Prepare the command. */
rq->cmd[0] = MODE_SELECT;
rq->cmd[1] = 0x10;
rq->cmd[4] = data_size;
rq->cmd_len = COMMAND_SIZE(rq->cmd[0]);
return rq;
}
static void emc_pg_init(struct hw_handler *hwh, unsigned bypassed,
struct dm_path *path)
{
struct request *rq;
struct request_queue *q = bdev_get_queue(path->dev->bdev);
/*
* We can either blindly init the pg (then look at the sense),
* or we can send some commands to get the state here (then
* possibly send the fo cmnd), or we can also have the
* initial state passed into us and then get an update here.
*/
if (!q) {
DMINFO("emc_pg_init: no queue");
goto fail_path;
}
/* FIXME: The request should be pre-allocated. */
rq = emc_trespass_get(hwh->context, path);
if (!rq) {
DMERR("emc_pg_init: no rq");
goto fail_path;
}
DMINFO("emc_pg_init: sending switch-over command");
elv_add_request(q, rq, ELEVATOR_INSERT_FRONT, 1);
return;
fail_path:
dm_pg_init_complete(path, MP_FAIL_PATH);
}
static struct emc_handler *alloc_emc_handler(void)
{
struct emc_handler *h = kzalloc(sizeof(*h), GFP_KERNEL);
if (h)
spin_lock_init(&h->lock);
return h;
}
static int emc_create(struct hw_handler *hwh, unsigned argc, char **argv)
{
struct emc_handler *h;
unsigned hr, short_trespass;
if (argc == 0) {
/* No arguments: use defaults */
hr = 0;
short_trespass = 0;
} else if (argc != 2) {
DMWARN("incorrect number of arguments");
return -EINVAL;
} else {
if ((sscanf(argv[0], "%u", &short_trespass) != 1)
|| (short_trespass > 1)) {
DMWARN("invalid trespass mode selected");
return -EINVAL;
}
if ((sscanf(argv[1], "%u", &hr) != 1)
|| (hr > 1)) {
DMWARN("invalid honor reservation flag selected");
return -EINVAL;
}
}
h = alloc_emc_handler();
if (!h)
return -ENOMEM;
hwh->context = h;
if ((h->short_trespass = short_trespass))
DMWARN("short trespass command will be send");
else
DMWARN("long trespass command will be send");
if ((h->hr = hr))
DMWARN("honor reservation bit will be set");
else
DMWARN("honor reservation bit will not be set (default)");
return 0;
}
static void emc_destroy(struct hw_handler *hwh)
{
struct emc_handler *h = (struct emc_handler *) hwh->context;
kfree(h);
hwh->context = NULL;
}
static unsigned emc_error(struct hw_handler *hwh, struct bio *bio)
{
/* FIXME: Patch from axboe still missing */
#if 0
int sense;
if (bio->bi_error & BIO_SENSE) {
sense = bio->bi_error & 0xffffff; /* sense key / asc / ascq */
if (sense == 0x020403) {
/* LUN Not Ready - Manual Intervention Required
* indicates this is a passive path.
*
* FIXME: However, if this is seen and EVPD C0
* indicates that this is due to a NDU in
* progress, we should set FAIL_PATH too.
* This indicates we might have to do a SCSI
* inquiry in the end_io path. Ugh. */
return MP_BYPASS_PG | MP_RETRY_IO;
} else if (sense == 0x052501) {
/* An array based copy is in progress. Do not
* fail the path, do not bypass to another PG,
* do not retry. Fail the IO immediately.
* (Actually this is the same conclusion as in
* the default handler, but lets make sure.) */
return 0;
} else if (sense == 0x062900) {
/* Unit Attention Code. This is the first IO
* to the new path, so just retry. */
return MP_RETRY_IO;
}
}
#endif
/* Try default handler */
return dm_scsi_err_handler(hwh, bio);
}
static struct hw_handler_type emc_hwh = {
.name = "emc",
.module = THIS_MODULE,
.create = emc_create,
.destroy = emc_destroy,
.pg_init = emc_pg_init,
.error = emc_error,
};
static int __init dm_emc_init(void)
{
int r = dm_register_hw_handler(&emc_hwh);
if (r < 0)
DMERR("register failed %d", r);
DMINFO("version 0.0.3 loaded");
return r;
}
static void __exit dm_emc_exit(void)
{
int r = dm_unregister_hw_handler(&emc_hwh);
if (r < 0)
DMERR("unregister failed %d", r);
}
module_init(dm_emc_init);
module_exit(dm_emc_exit);
MODULE_DESCRIPTION(DM_NAME " EMC CX/AX/FC-family multipath");
MODULE_AUTHOR("Lars Marowsky-Bree <lmb@suse.de>");
MODULE_LICENSE("GPL");

View File

@ -1,213 +0,0 @@
/*
* Copyright (C) 2004 Red Hat, Inc. All rights reserved.
*
* This file is released under the GPL.
*
* Multipath hardware handler registration.
*/
#include "dm.h"
#include "dm-hw-handler.h"
#include <linux/slab.h>
struct hwh_internal {
struct hw_handler_type hwht;
struct list_head list;
long use;
};
#define hwht_to_hwhi(__hwht) container_of((__hwht), struct hwh_internal, hwht)
static LIST_HEAD(_hw_handlers);
static DECLARE_RWSEM(_hwh_lock);
static struct hwh_internal *__find_hw_handler_type(const char *name)
{
struct hwh_internal *hwhi;
list_for_each_entry(hwhi, &_hw_handlers, list) {
if (!strcmp(name, hwhi->hwht.name))
return hwhi;
}
return NULL;
}
static struct hwh_internal *get_hw_handler(const char *name)
{
struct hwh_internal *hwhi;
down_read(&_hwh_lock);
hwhi = __find_hw_handler_type(name);
if (hwhi) {
if ((hwhi->use == 0) && !try_module_get(hwhi->hwht.module))
hwhi = NULL;
else
hwhi->use++;
}
up_read(&_hwh_lock);
return hwhi;
}
struct hw_handler_type *dm_get_hw_handler(const char *name)
{
struct hwh_internal *hwhi;
if (!name)
return NULL;
hwhi = get_hw_handler(name);
if (!hwhi) {
request_module("dm-%s", name);
hwhi = get_hw_handler(name);
}
return hwhi ? &hwhi->hwht : NULL;
}
void dm_put_hw_handler(struct hw_handler_type *hwht)
{
struct hwh_internal *hwhi;
if (!hwht)
return;
down_read(&_hwh_lock);
hwhi = __find_hw_handler_type(hwht->name);
if (!hwhi)
goto out;
if (--hwhi->use == 0)
module_put(hwhi->hwht.module);
BUG_ON(hwhi->use < 0);
out:
up_read(&_hwh_lock);
}
static struct hwh_internal *_alloc_hw_handler(struct hw_handler_type *hwht)
{
struct hwh_internal *hwhi = kzalloc(sizeof(*hwhi), GFP_KERNEL);
if (hwhi)
hwhi->hwht = *hwht;
return hwhi;
}
int dm_register_hw_handler(struct hw_handler_type *hwht)
{
int r = 0;
struct hwh_internal *hwhi = _alloc_hw_handler(hwht);
if (!hwhi)
return -ENOMEM;
down_write(&_hwh_lock);
if (__find_hw_handler_type(hwht->name)) {
kfree(hwhi);
r = -EEXIST;
} else
list_add(&hwhi->list, &_hw_handlers);
up_write(&_hwh_lock);
return r;
}
int dm_unregister_hw_handler(struct hw_handler_type *hwht)
{
struct hwh_internal *hwhi;
down_write(&_hwh_lock);
hwhi = __find_hw_handler_type(hwht->name);
if (!hwhi) {
up_write(&_hwh_lock);
return -EINVAL;
}
if (hwhi->use) {
up_write(&_hwh_lock);
return -ETXTBSY;
}
list_del(&hwhi->list);
up_write(&_hwh_lock);
kfree(hwhi);
return 0;
}
unsigned dm_scsi_err_handler(struct hw_handler *hwh, struct bio *bio)
{
#if 0
int sense_key, asc, ascq;
if (bio->bi_error & BIO_SENSE) {
/* FIXME: This is just an initial guess. */
/* key / asc / ascq */
sense_key = (bio->bi_error >> 16) & 0xff;
asc = (bio->bi_error >> 8) & 0xff;
ascq = bio->bi_error & 0xff;
switch (sense_key) {
/* This block as a whole comes from the device.
* So no point retrying on another path. */
case 0x03: /* Medium error */
case 0x05: /* Illegal request */
case 0x07: /* Data protect */
case 0x08: /* Blank check */
case 0x0a: /* copy aborted */
case 0x0c: /* obsolete - no clue ;-) */
case 0x0d: /* volume overflow */
case 0x0e: /* data miscompare */
case 0x0f: /* reserved - no idea either. */
return MP_ERROR_IO;
/* For these errors it's unclear whether they
* come from the device or the controller.
* So just lets try a different path, and if
* it eventually succeeds, user-space will clear
* the paths again... */
case 0x02: /* Not ready */
case 0x04: /* Hardware error */
case 0x09: /* vendor specific */
case 0x0b: /* Aborted command */
return MP_FAIL_PATH;
case 0x06: /* Unit attention - might want to decode */
if (asc == 0x04 && ascq == 0x01)
/* "Unit in the process of
* becoming ready" */
return 0;
return MP_FAIL_PATH;
/* FIXME: For Unit Not Ready we may want
* to have a generic pg activation
* feature (START_UNIT). */
/* Should these two ever end up in the
* error path? I don't think so. */
case 0x00: /* No sense */
case 0x01: /* Recovered error */
return 0;
}
}
#endif
/* We got no idea how to decode the other kinds of errors ->
* assume generic error condition. */
return MP_FAIL_PATH;
}
EXPORT_SYMBOL_GPL(dm_register_hw_handler);
EXPORT_SYMBOL_GPL(dm_unregister_hw_handler);
EXPORT_SYMBOL_GPL(dm_scsi_err_handler);

View File

@ -1,63 +0,0 @@
/*
* Copyright (C) 2004 Red Hat, Inc. All rights reserved.
*
* This file is released under the GPL.
*
* Multipath hardware handler registration.
*/
#ifndef DM_HW_HANDLER_H
#define DM_HW_HANDLER_H
#include <linux/device-mapper.h>
#include "dm-mpath.h"
struct hw_handler_type;
struct hw_handler {
struct hw_handler_type *type;
struct mapped_device *md;
void *context;
};
/*
* Constructs a hardware handler object, takes custom arguments
*/
/* Information about a hardware handler type */
struct hw_handler_type {
char *name;
struct module *module;
int (*create) (struct hw_handler *handler, unsigned int argc,
char **argv);
void (*destroy) (struct hw_handler *hwh);
void (*pg_init) (struct hw_handler *hwh, unsigned bypassed,
struct dm_path *path);
unsigned (*error) (struct hw_handler *hwh, struct bio *bio);
int (*status) (struct hw_handler *hwh, status_type_t type,
char *result, unsigned int maxlen);
};
/* Register a hardware handler */
int dm_register_hw_handler(struct hw_handler_type *type);
/* Unregister a hardware handler */
int dm_unregister_hw_handler(struct hw_handler_type *type);
/* Returns a registered hardware handler type */
struct hw_handler_type *dm_get_hw_handler(const char *name);
/* Releases a hardware handler */
void dm_put_hw_handler(struct hw_handler_type *hwht);
/* Default err function */
unsigned dm_scsi_err_handler(struct hw_handler *hwh, struct bio *bio);
/* Error flags for err and dm_pg_init_complete */
#define MP_FAIL_PATH 1
#define MP_BYPASS_PG 2
#define MP_ERROR_IO 4 /* Don't retry this I/O */
#define MP_RETRY 8
#endif

View File

@ -1,247 +0,0 @@
/*
* Copyright (C) 2005 Mike Christie, All rights reserved.
* Copyright (C) 2007 Red Hat, Inc. All rights reserved.
* Authors: Mike Christie
* Dave Wysochanski
*
* This file is released under the GPL.
*
* This module implements the specific path activation code for
* HP StorageWorks and FSC FibreCat Asymmetric (Active/Passive)
* storage arrays.
* These storage arrays have controller-based failover, not
* LUN-based failover. However, LUN-based failover is the design
* of dm-multipath. Thus, this module is written for LUN-based failover.
*/
#include <linux/blkdev.h>
#include <linux/list.h>
#include <linux/types.h>
#include <scsi/scsi.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_dbg.h>
#include "dm.h"
#include "dm-hw-handler.h"
#define DM_MSG_PREFIX "multipath hp-sw"
#define DM_HP_HWH_NAME "hp-sw"
#define DM_HP_HWH_VER "1.0.0"
struct hp_sw_context {
unsigned char sense[SCSI_SENSE_BUFFERSIZE];
};
/*
* hp_sw_error_is_retryable - Is an HP-specific check condition retryable?
* @req: path activation request
*
* Examine error codes of request and determine whether the error is retryable.
* Some error codes are already retried by scsi-ml (see
* scsi_decide_disposition), but some HP specific codes are not.
* The intent of this routine is to supply the logic for the HP specific
* check conditions.
*
* Returns:
* 1 - command completed with retryable error
* 0 - command completed with non-retryable error
*
* Possible optimizations
* 1. More hardware-specific error codes
*/
static int hp_sw_error_is_retryable(struct request *req)
{
/*
* NOT_READY is known to be retryable
* For now we just dump out the sense data and call it retryable
*/
if (status_byte(req->errors) == CHECK_CONDITION)
__scsi_print_sense(DM_HP_HWH_NAME, req->sense, req->sense_len);
/*
* At this point we don't have complete information about all the error
* codes from this hardware, so we are just conservative and retry
* when in doubt.
*/
return 1;
}
/*
* hp_sw_end_io - Completion handler for HP path activation.
* @req: path activation request
* @error: scsi-ml error
*
* Check sense data, free request structure, and notify dm that
* pg initialization has completed.
*
* Context: scsi-ml softirq
*
*/
static void hp_sw_end_io(struct request *req, int error)
{
struct dm_path *path = req->end_io_data;
unsigned err_flags = 0;
if (!error) {
DMDEBUG("%s path activation command - success",
path->dev->name);
goto out;
}
if (hp_sw_error_is_retryable(req)) {
DMDEBUG("%s path activation command - retry",
path->dev->name);
err_flags = MP_RETRY;
goto out;
}
DMWARN("%s path activation fail - error=0x%x",
path->dev->name, error);
err_flags = MP_FAIL_PATH;
out:
req->end_io_data = NULL;
__blk_put_request(req->q, req);
dm_pg_init_complete(path, err_flags);
}
/*
* hp_sw_get_request - Allocate an HP specific path activation request
* @path: path on which request will be sent (needed for request queue)
*
* The START command is used for path activation request.
* These arrays are controller-based failover, not LUN based.
* One START command issued to a single path will fail over all
* LUNs for the same controller.
*
* Possible optimizations
* 1. Make timeout configurable
* 2. Preallocate request
*/
static struct request *hp_sw_get_request(struct dm_path *path)
{
struct request *req;
struct block_device *bdev = path->dev->bdev;
struct request_queue *q = bdev_get_queue(bdev);
struct hp_sw_context *h = path->hwhcontext;
req = blk_get_request(q, WRITE, GFP_NOIO);
if (!req)
goto out;
req->timeout = 60 * HZ;
req->errors = 0;
req->cmd_type = REQ_TYPE_BLOCK_PC;
req->cmd_flags |= REQ_FAILFAST | REQ_NOMERGE;
req->end_io_data = path;
req->sense = h->sense;
memset(req->sense, 0, SCSI_SENSE_BUFFERSIZE);
req->cmd[0] = START_STOP;
req->cmd[4] = 1;
req->cmd_len = COMMAND_SIZE(req->cmd[0]);
out:
return req;
}
/*
* hp_sw_pg_init - HP path activation implementation.
* @hwh: hardware handler specific data
* @bypassed: unused; is the path group bypassed? (see dm-mpath.c)
* @path: path to send initialization command
*
* Send an HP-specific path activation command on 'path'.
* Do not try to optimize in any way, just send the activation command.
* More than one path activation command may be sent to the same controller.
* This seems to work fine for basic failover support.
*
* Possible optimizations
* 1. Detect an in-progress activation request and avoid submitting another one
* 2. Model the controller and only send a single activation request at a time
* 3. Determine the state of a path before sending an activation request
*
* Context: kmpathd (see process_queued_ios() in dm-mpath.c)
*/
static void hp_sw_pg_init(struct hw_handler *hwh, unsigned bypassed,
struct dm_path *path)
{
struct request *req;
struct hp_sw_context *h;
path->hwhcontext = hwh->context;
h = hwh->context;
req = hp_sw_get_request(path);
if (!req) {
DMERR("%s path activation command - allocation fail",
path->dev->name);
goto retry;
}
DMDEBUG("%s path activation command - sent", path->dev->name);
blk_execute_rq_nowait(req->q, NULL, req, 1, hp_sw_end_io);
return;
retry:
dm_pg_init_complete(path, MP_RETRY);
}
static int hp_sw_create(struct hw_handler *hwh, unsigned argc, char **argv)
{
struct hp_sw_context *h;
h = kmalloc(sizeof(*h), GFP_KERNEL);
if (!h)
return -ENOMEM;
hwh->context = h;
return 0;
}
static void hp_sw_destroy(struct hw_handler *hwh)
{
struct hp_sw_context *h = hwh->context;
kfree(h);
}
static struct hw_handler_type hp_sw_hwh = {
.name = DM_HP_HWH_NAME,
.module = THIS_MODULE,
.create = hp_sw_create,
.destroy = hp_sw_destroy,
.pg_init = hp_sw_pg_init,
};
static int __init hp_sw_init(void)
{
int r;
r = dm_register_hw_handler(&hp_sw_hwh);
if (r < 0)
DMERR("register failed %d", r);
else
DMINFO("version " DM_HP_HWH_VER " loaded");
return r;
}
static void __exit hp_sw_exit(void)
{
int r;
r = dm_unregister_hw_handler(&hp_sw_hwh);
if (r < 0)
DMERR("unregister failed %d", r);
}
module_init(hp_sw_init);
module_exit(hp_sw_exit);
MODULE_DESCRIPTION("DM Multipath HP StorageWorks / FSC FibreCat (A/P) support");
MODULE_AUTHOR("Mike Christie, Dave Wysochanski <dm-devel@redhat.com>");
MODULE_LICENSE("GPL");
MODULE_VERSION(DM_HP_HWH_VER);

View File

@ -1,700 +0,0 @@
/*
* Engenio/LSI RDAC DM HW handler
*
* Copyright (C) 2005 Mike Christie. All rights reserved.
* Copyright (C) Chandra Seetharaman, IBM Corp. 2007
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
*/
#include <scsi/scsi.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_eh.h>
#define DM_MSG_PREFIX "multipath rdac"
#include "dm.h"
#include "dm-hw-handler.h"
#define RDAC_DM_HWH_NAME "rdac"
#define RDAC_DM_HWH_VER "0.4"
/*
* LSI mode page stuff
*
* These struct definitions and the forming of the
* mode page were taken from the LSI RDAC 2.4 GPL'd
* driver, and then converted to Linux conventions.
*/
#define RDAC_QUIESCENCE_TIME 20;
/*
* Page Codes
*/
#define RDAC_PAGE_CODE_REDUNDANT_CONTROLLER 0x2c
/*
* Controller modes definitions
*/
#define RDAC_MODE_TRANSFER_ALL_LUNS 0x01
#define RDAC_MODE_TRANSFER_SPECIFIED_LUNS 0x02
/*
* RDAC Options field
*/
#define RDAC_FORCED_QUIESENCE 0x02
#define RDAC_FAILOVER_TIMEOUT (60 * HZ)
struct rdac_mode_6_hdr {
u8 data_len;
u8 medium_type;
u8 device_params;
u8 block_desc_len;
};
struct rdac_mode_10_hdr {
u16 data_len;
u8 medium_type;
u8 device_params;
u16 reserved;
u16 block_desc_len;
};
struct rdac_mode_common {
u8 controller_serial[16];
u8 alt_controller_serial[16];
u8 rdac_mode[2];
u8 alt_rdac_mode[2];
u8 quiescence_timeout;
u8 rdac_options;
};
struct rdac_pg_legacy {
struct rdac_mode_6_hdr hdr;
u8 page_code;
u8 page_len;
struct rdac_mode_common common;
#define MODE6_MAX_LUN 32
u8 lun_table[MODE6_MAX_LUN];
u8 reserved2[32];
u8 reserved3;
u8 reserved4;
};
struct rdac_pg_expanded {
struct rdac_mode_10_hdr hdr;
u8 page_code;
u8 subpage_code;
u8 page_len[2];
struct rdac_mode_common common;
u8 lun_table[256];
u8 reserved3;
u8 reserved4;
};
struct c9_inquiry {
u8 peripheral_info;
u8 page_code; /* 0xC9 */
u8 reserved1;
u8 page_len;
u8 page_id[4]; /* "vace" */
u8 avte_cvp;
u8 path_prio;
u8 reserved2[38];
};
#define SUBSYS_ID_LEN 16
#define SLOT_ID_LEN 2
struct c4_inquiry {
u8 peripheral_info;
u8 page_code; /* 0xC4 */
u8 reserved1;
u8 page_len;
u8 page_id[4]; /* "subs" */
u8 subsys_id[SUBSYS_ID_LEN];
u8 revision[4];
u8 slot_id[SLOT_ID_LEN];
u8 reserved[2];
};
struct rdac_controller {
u8 subsys_id[SUBSYS_ID_LEN];
u8 slot_id[SLOT_ID_LEN];
int use_10_ms;
struct kref kref;
struct list_head node; /* list of all controllers */
spinlock_t lock;
int submitted;
struct list_head cmd_list; /* list of commands to be submitted */
union {
struct rdac_pg_legacy legacy;
struct rdac_pg_expanded expanded;
} mode_select;
};
struct c8_inquiry {
u8 peripheral_info;
u8 page_code; /* 0xC8 */
u8 reserved1;
u8 page_len;
u8 page_id[4]; /* "edid" */
u8 reserved2[3];
u8 vol_uniq_id_len;
u8 vol_uniq_id[16];
u8 vol_user_label_len;
u8 vol_user_label[60];
u8 array_uniq_id_len;
u8 array_unique_id[16];
u8 array_user_label_len;
u8 array_user_label[60];
u8 lun[8];
};
struct c2_inquiry {
u8 peripheral_info;
u8 page_code; /* 0xC2 */
u8 reserved1;
u8 page_len;
u8 page_id[4]; /* "swr4" */
u8 sw_version[3];
u8 sw_date[3];
u8 features_enabled;
u8 max_lun_supported;
u8 partitions[239]; /* Total allocation length should be 0xFF */
};
struct rdac_handler {
struct list_head entry; /* list waiting to submit MODE SELECT */
unsigned timeout;
struct rdac_controller *ctlr;
#define UNINITIALIZED_LUN (1 << 8)
unsigned lun;
unsigned char sense[SCSI_SENSE_BUFFERSIZE];
struct dm_path *path;
struct work_struct work;
#define SEND_C2_INQUIRY 1
#define SEND_C4_INQUIRY 2
#define SEND_C8_INQUIRY 3
#define SEND_C9_INQUIRY 4
#define SEND_MODE_SELECT 5
int cmd_to_send;
union {
struct c2_inquiry c2;
struct c4_inquiry c4;
struct c8_inquiry c8;
struct c9_inquiry c9;
} inq;
};
static LIST_HEAD(ctlr_list);
static DEFINE_SPINLOCK(list_lock);
static struct workqueue_struct *rdac_wkqd;
static inline int had_failures(struct request *req, int error)
{
return (error || host_byte(req->errors) != DID_OK ||
msg_byte(req->errors) != COMMAND_COMPLETE);
}
static void rdac_resubmit_all(struct rdac_handler *h)
{
struct rdac_controller *ctlr = h->ctlr;
struct rdac_handler *tmp, *h1;
spin_lock(&ctlr->lock);
list_for_each_entry_safe(h1, tmp, &ctlr->cmd_list, entry) {
h1->cmd_to_send = SEND_C9_INQUIRY;
queue_work(rdac_wkqd, &h1->work);
list_del(&h1->entry);
}
ctlr->submitted = 0;
spin_unlock(&ctlr->lock);
}
static void mode_select_endio(struct request *req, int error)
{
struct rdac_handler *h = req->end_io_data;
struct scsi_sense_hdr sense_hdr;
int sense = 0, fail = 0;
if (had_failures(req, error)) {
fail = 1;
goto failed;
}
if (status_byte(req->errors) == CHECK_CONDITION) {
scsi_normalize_sense(req->sense, SCSI_SENSE_BUFFERSIZE,
&sense_hdr);
sense = (sense_hdr.sense_key << 16) | (sense_hdr.asc << 8) |
sense_hdr.ascq;
/* If it is retryable failure, submit the c9 inquiry again */
if (sense == 0x59136 || sense == 0x68b02 || sense == 0xb8b02 ||
sense == 0x62900) {
/* 0x59136 - Command lock contention
* 0x[6b]8b02 - Quiesense in progress or achieved
* 0x62900 - Power On, Reset, or Bus Device Reset
*/
h->cmd_to_send = SEND_C9_INQUIRY;
queue_work(rdac_wkqd, &h->work);
goto done;
}
if (sense)
DMINFO("MODE_SELECT failed on %s with sense 0x%x",
h->path->dev->name, sense);
}
failed:
if (fail || sense)
dm_pg_init_complete(h->path, MP_FAIL_PATH);
else
dm_pg_init_complete(h->path, 0);
done:
rdac_resubmit_all(h);
__blk_put_request(req->q, req);
}
static struct request *get_rdac_req(struct rdac_handler *h,
void *buffer, unsigned buflen, int rw)
{
struct request *rq;
struct request_queue *q = bdev_get_queue(h->path->dev->bdev);
rq = blk_get_request(q, rw, GFP_KERNEL);
if (!rq) {
DMINFO("get_rdac_req: blk_get_request failed");
return NULL;
}
if (buflen && blk_rq_map_kern(q, rq, buffer, buflen, GFP_KERNEL)) {
blk_put_request(rq);
DMINFO("get_rdac_req: blk_rq_map_kern failed");
return NULL;
}
rq->sense = h->sense;
memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE);
rq->sense_len = 0;
rq->end_io_data = h;
rq->timeout = h->timeout;
rq->cmd_type = REQ_TYPE_BLOCK_PC;
rq->cmd_flags |= REQ_FAILFAST | REQ_NOMERGE;
return rq;
}
static struct request *rdac_failover_get(struct rdac_handler *h)
{
struct request *rq;
struct rdac_mode_common *common;
unsigned data_size;
if (h->ctlr->use_10_ms) {
struct rdac_pg_expanded *rdac_pg;
data_size = sizeof(struct rdac_pg_expanded);
rdac_pg = &h->ctlr->mode_select.expanded;
memset(rdac_pg, 0, data_size);
common = &rdac_pg->common;
rdac_pg->page_code = RDAC_PAGE_CODE_REDUNDANT_CONTROLLER + 0x40;
rdac_pg->subpage_code = 0x1;
rdac_pg->page_len[0] = 0x01;
rdac_pg->page_len[1] = 0x28;
rdac_pg->lun_table[h->lun] = 0x81;
} else {
struct rdac_pg_legacy *rdac_pg;
data_size = sizeof(struct rdac_pg_legacy);
rdac_pg = &h->ctlr->mode_select.legacy;
memset(rdac_pg, 0, data_size);
common = &rdac_pg->common;
rdac_pg->page_code = RDAC_PAGE_CODE_REDUNDANT_CONTROLLER;
rdac_pg->page_len = 0x68;
rdac_pg->lun_table[h->lun] = 0x81;
}
common->rdac_mode[1] = RDAC_MODE_TRANSFER_SPECIFIED_LUNS;
common->quiescence_timeout = RDAC_QUIESCENCE_TIME;
common->rdac_options = RDAC_FORCED_QUIESENCE;
/* get request for block layer packet command */
rq = get_rdac_req(h, &h->ctlr->mode_select, data_size, WRITE);
if (!rq) {
DMERR("rdac_failover_get: no rq");
return NULL;
}
/* Prepare the command. */
if (h->ctlr->use_10_ms) {
rq->cmd[0] = MODE_SELECT_10;
rq->cmd[7] = data_size >> 8;
rq->cmd[8] = data_size & 0xff;
} else {
rq->cmd[0] = MODE_SELECT;
rq->cmd[4] = data_size;
}
rq->cmd_len = COMMAND_SIZE(rq->cmd[0]);
return rq;
}
/* Acquires h->ctlr->lock */
static void submit_mode_select(struct rdac_handler *h)
{
struct request *rq;
struct request_queue *q = bdev_get_queue(h->path->dev->bdev);
spin_lock(&h->ctlr->lock);
if (h->ctlr->submitted) {
list_add(&h->entry, &h->ctlr->cmd_list);
goto drop_lock;
}
if (!q) {
DMINFO("submit_mode_select: no queue");
goto fail_path;
}
rq = rdac_failover_get(h);
if (!rq) {
DMERR("submit_mode_select: no rq");
goto fail_path;
}
DMINFO("queueing MODE_SELECT command on %s", h->path->dev->name);
blk_execute_rq_nowait(q, NULL, rq, 1, mode_select_endio);
h->ctlr->submitted = 1;
goto drop_lock;
fail_path:
dm_pg_init_complete(h->path, MP_FAIL_PATH);
drop_lock:
spin_unlock(&h->ctlr->lock);
}
static void release_ctlr(struct kref *kref)
{
struct rdac_controller *ctlr;
ctlr = container_of(kref, struct rdac_controller, kref);
spin_lock(&list_lock);
list_del(&ctlr->node);
spin_unlock(&list_lock);
kfree(ctlr);
}
static struct rdac_controller *get_controller(u8 *subsys_id, u8 *slot_id)
{
struct rdac_controller *ctlr, *tmp;
spin_lock(&list_lock);
list_for_each_entry(tmp, &ctlr_list, node) {
if ((memcmp(tmp->subsys_id, subsys_id, SUBSYS_ID_LEN) == 0) &&
(memcmp(tmp->slot_id, slot_id, SLOT_ID_LEN) == 0)) {
kref_get(&tmp->kref);
spin_unlock(&list_lock);
return tmp;
}
}
ctlr = kmalloc(sizeof(*ctlr), GFP_ATOMIC);
if (!ctlr)
goto done;
/* initialize fields of controller */
memcpy(ctlr->subsys_id, subsys_id, SUBSYS_ID_LEN);
memcpy(ctlr->slot_id, slot_id, SLOT_ID_LEN);
kref_init(&ctlr->kref);
spin_lock_init(&ctlr->lock);
ctlr->submitted = 0;
ctlr->use_10_ms = -1;
INIT_LIST_HEAD(&ctlr->cmd_list);
list_add(&ctlr->node, &ctlr_list);
done:
spin_unlock(&list_lock);
return ctlr;
}
static void c4_endio(struct request *req, int error)
{
struct rdac_handler *h = req->end_io_data;
struct c4_inquiry *sp;
if (had_failures(req, error)) {
dm_pg_init_complete(h->path, MP_FAIL_PATH);
goto done;
}
sp = &h->inq.c4;
h->ctlr = get_controller(sp->subsys_id, sp->slot_id);
if (h->ctlr) {
h->cmd_to_send = SEND_C9_INQUIRY;
queue_work(rdac_wkqd, &h->work);
} else
dm_pg_init_complete(h->path, MP_FAIL_PATH);
done:
__blk_put_request(req->q, req);
}
static void c2_endio(struct request *req, int error)
{
struct rdac_handler *h = req->end_io_data;
struct c2_inquiry *sp;
if (had_failures(req, error)) {
dm_pg_init_complete(h->path, MP_FAIL_PATH);
goto done;
}
sp = &h->inq.c2;
/* If more than MODE6_MAX_LUN luns are supported, use mode select 10 */
if (sp->max_lun_supported >= MODE6_MAX_LUN)
h->ctlr->use_10_ms = 1;
else
h->ctlr->use_10_ms = 0;
h->cmd_to_send = SEND_MODE_SELECT;
queue_work(rdac_wkqd, &h->work);
done:
__blk_put_request(req->q, req);
}
static void c9_endio(struct request *req, int error)
{
struct rdac_handler *h = req->end_io_data;
struct c9_inquiry *sp;
if (had_failures(req, error)) {
dm_pg_init_complete(h->path, MP_FAIL_PATH);
goto done;
}
/* We need to look at the sense keys here to take clear action.
* For now simple logic: If the host is in AVT mode or if controller
* owns the lun, return dm_pg_init_complete(), otherwise submit
* MODE SELECT.
*/
sp = &h->inq.c9;
/* If in AVT mode, return success */
if ((sp->avte_cvp >> 7) == 0x1) {
dm_pg_init_complete(h->path, 0);
goto done;
}
/* If the controller on this path owns the LUN, return success */
if (sp->avte_cvp & 0x1) {
dm_pg_init_complete(h->path, 0);
goto done;
}
if (h->ctlr) {
if (h->ctlr->use_10_ms == -1)
h->cmd_to_send = SEND_C2_INQUIRY;
else
h->cmd_to_send = SEND_MODE_SELECT;
} else
h->cmd_to_send = SEND_C4_INQUIRY;
queue_work(rdac_wkqd, &h->work);
done:
__blk_put_request(req->q, req);
}
static void c8_endio(struct request *req, int error)
{
struct rdac_handler *h = req->end_io_data;
struct c8_inquiry *sp;
if (had_failures(req, error)) {
dm_pg_init_complete(h->path, MP_FAIL_PATH);
goto done;
}
/* We need to look at the sense keys here to take clear action.
* For now simple logic: Get the lun from the inquiry page.
*/
sp = &h->inq.c8;
h->lun = sp->lun[7]; /* currently it uses only one byte */
h->cmd_to_send = SEND_C9_INQUIRY;
queue_work(rdac_wkqd, &h->work);
done:
__blk_put_request(req->q, req);
}
static void submit_inquiry(struct rdac_handler *h, int page_code,
unsigned int len, rq_end_io_fn endio)
{
struct request *rq;
struct request_queue *q = bdev_get_queue(h->path->dev->bdev);
if (!q)
goto fail_path;
rq = get_rdac_req(h, &h->inq, len, READ);
if (!rq)
goto fail_path;
/* Prepare the command. */
rq->cmd[0] = INQUIRY;
rq->cmd[1] = 1;
rq->cmd[2] = page_code;
rq->cmd[4] = len;
rq->cmd_len = COMMAND_SIZE(INQUIRY);
blk_execute_rq_nowait(q, NULL, rq, 1, endio);
return;
fail_path:
dm_pg_init_complete(h->path, MP_FAIL_PATH);
}
static void service_wkq(struct work_struct *work)
{
struct rdac_handler *h = container_of(work, struct rdac_handler, work);
switch (h->cmd_to_send) {
case SEND_C2_INQUIRY:
submit_inquiry(h, 0xC2, sizeof(struct c2_inquiry), c2_endio);
break;
case SEND_C4_INQUIRY:
submit_inquiry(h, 0xC4, sizeof(struct c4_inquiry), c4_endio);
break;
case SEND_C8_INQUIRY:
submit_inquiry(h, 0xC8, sizeof(struct c8_inquiry), c8_endio);
break;
case SEND_C9_INQUIRY:
submit_inquiry(h, 0xC9, sizeof(struct c9_inquiry), c9_endio);
break;
case SEND_MODE_SELECT:
submit_mode_select(h);
break;
default:
BUG();
}
}
/*
* only support subpage2c until we confirm that this is just a matter of
* of updating firmware or not, and RDAC (basic AVT works already) for now
* but we can add these in in when we get time and testers
*/
static int rdac_create(struct hw_handler *hwh, unsigned argc, char **argv)
{
struct rdac_handler *h;
unsigned timeout;
if (argc == 0) {
/* No arguments: use defaults */
timeout = RDAC_FAILOVER_TIMEOUT;
} else if (argc != 1) {
DMWARN("incorrect number of arguments");
return -EINVAL;
} else {
if (sscanf(argv[1], "%u", &timeout) != 1) {
DMWARN("invalid timeout value");
return -EINVAL;
}
}
h = kzalloc(sizeof(*h), GFP_KERNEL);
if (!h)
return -ENOMEM;
hwh->context = h;
h->timeout = timeout;
h->lun = UNINITIALIZED_LUN;
INIT_WORK(&h->work, service_wkq);
DMWARN("using RDAC command with timeout %u", h->timeout);
return 0;
}
static void rdac_destroy(struct hw_handler *hwh)
{
struct rdac_handler *h = hwh->context;
if (h->ctlr)
kref_put(&h->ctlr->kref, release_ctlr);
kfree(h);
hwh->context = NULL;
}
static unsigned rdac_error(struct hw_handler *hwh, struct bio *bio)
{
/* Try default handler */
return dm_scsi_err_handler(hwh, bio);
}
static void rdac_pg_init(struct hw_handler *hwh, unsigned bypassed,
struct dm_path *path)
{
struct rdac_handler *h = hwh->context;
h->path = path;
switch (h->lun) {
case UNINITIALIZED_LUN:
submit_inquiry(h, 0xC8, sizeof(struct c8_inquiry), c8_endio);
break;
default:
submit_inquiry(h, 0xC9, sizeof(struct c9_inquiry), c9_endio);
}
}
static struct hw_handler_type rdac_handler = {
.name = RDAC_DM_HWH_NAME,
.module = THIS_MODULE,
.create = rdac_create,
.destroy = rdac_destroy,
.pg_init = rdac_pg_init,
.error = rdac_error,
};
static int __init rdac_init(void)
{
int r;
rdac_wkqd = create_singlethread_workqueue("rdac_wkqd");
if (!rdac_wkqd) {
DMERR("Failed to create workqueue rdac_wkqd.");
return -ENOMEM;
}
r = dm_register_hw_handler(&rdac_handler);
if (r < 0) {
DMERR("%s: register failed %d", RDAC_DM_HWH_NAME, r);
destroy_workqueue(rdac_wkqd);
return r;
}
DMINFO("%s: version %s loaded", RDAC_DM_HWH_NAME, RDAC_DM_HWH_VER);
return 0;
}
static void __exit rdac_exit(void)
{
int r = dm_unregister_hw_handler(&rdac_handler);
destroy_workqueue(rdac_wkqd);
if (r < 0)
DMERR("%s: unregister failed %d", RDAC_DM_HWH_NAME, r);
}
module_init(rdac_init);
module_exit(rdac_exit);
MODULE_DESCRIPTION("DM Multipath LSI/Engenio RDAC support");
MODULE_AUTHOR("Mike Christie, Chandra Seetharaman");
MODULE_LICENSE("GPL");
MODULE_VERSION(RDAC_DM_HWH_VER);

View File

@ -7,7 +7,6 @@
#include "dm.h"
#include "dm-path-selector.h"
#include "dm-hw-handler.h"
#include "dm-bio-list.h"
#include "dm-bio-record.h"
#include "dm-uevent.h"
@ -20,6 +19,7 @@
#include <linux/slab.h>
#include <linux/time.h>
#include <linux/workqueue.h>
#include <scsi/scsi_dh.h>
#include <asm/atomic.h>
#define DM_MSG_PREFIX "multipath"
@ -61,7 +61,8 @@ struct multipath {
spinlock_t lock;
struct hw_handler hw_handler;
const char *hw_handler_name;
struct work_struct activate_path;
unsigned nr_priority_groups;
struct list_head priority_groups;
unsigned pg_init_required; /* pg_init needs calling? */
@ -106,9 +107,10 @@ typedef int (*action_fn) (struct pgpath *pgpath);
static struct kmem_cache *_mpio_cache;
static struct workqueue_struct *kmultipathd;
static struct workqueue_struct *kmultipathd, *kmpath_handlerd;
static void process_queued_ios(struct work_struct *work);
static void trigger_event(struct work_struct *work);
static void activate_path(struct work_struct *work);
/*-----------------------------------------------
@ -178,6 +180,7 @@ static struct multipath *alloc_multipath(struct dm_target *ti)
m->queue_io = 1;
INIT_WORK(&m->process_queued_ios, process_queued_ios);
INIT_WORK(&m->trigger_event, trigger_event);
INIT_WORK(&m->activate_path, activate_path);
m->mpio_pool = mempool_create_slab_pool(MIN_IOS, _mpio_cache);
if (!m->mpio_pool) {
kfree(m);
@ -193,18 +196,13 @@ static struct multipath *alloc_multipath(struct dm_target *ti)
static void free_multipath(struct multipath *m)
{
struct priority_group *pg, *tmp;
struct hw_handler *hwh = &m->hw_handler;
list_for_each_entry_safe(pg, tmp, &m->priority_groups, list) {
list_del(&pg->list);
free_priority_group(pg, m->ti);
}
if (hwh->type) {
hwh->type->destroy(hwh);
dm_put_hw_handler(hwh->type);
}
kfree(m->hw_handler_name);
mempool_destroy(m->mpio_pool);
kfree(m);
}
@ -216,12 +214,10 @@ static void free_multipath(struct multipath *m)
static void __switch_pg(struct multipath *m, struct pgpath *pgpath)
{
struct hw_handler *hwh = &m->hw_handler;
m->current_pg = pgpath->pg;
/* Must we initialise the PG first, and queue I/O till it's ready? */
if (hwh->type && hwh->type->pg_init) {
if (m->hw_handler_name) {
m->pg_init_required = 1;
m->queue_io = 1;
} else {
@ -409,7 +405,6 @@ static void process_queued_ios(struct work_struct *work)
{
struct multipath *m =
container_of(work, struct multipath, process_queued_ios);
struct hw_handler *hwh = &m->hw_handler;
struct pgpath *pgpath = NULL;
unsigned init_required = 0, must_queue = 1;
unsigned long flags;
@ -439,7 +434,7 @@ static void process_queued_ios(struct work_struct *work)
spin_unlock_irqrestore(&m->lock, flags);
if (init_required)
hwh->type->pg_init(hwh, pgpath->pg->bypassed, &pgpath->path);
queue_work(kmpath_handlerd, &m->activate_path);
if (!must_queue)
dispatch_queued_ios(m);
@ -652,8 +647,6 @@ static struct priority_group *parse_priority_group(struct arg_set *as,
static int parse_hw_handler(struct arg_set *as, struct multipath *m)
{
int r;
struct hw_handler_type *hwht;
unsigned hw_argc;
struct dm_target *ti = m->ti;
@ -661,30 +654,20 @@ static int parse_hw_handler(struct arg_set *as, struct multipath *m)
{0, 1024, "invalid number of hardware handler args"},
};
r = read_param(_params, shift(as), &hw_argc, &ti->error);
if (r)
if (read_param(_params, shift(as), &hw_argc, &ti->error))
return -EINVAL;
if (!hw_argc)
return 0;
hwht = dm_get_hw_handler(shift(as));
if (!hwht) {
m->hw_handler_name = kstrdup(shift(as), GFP_KERNEL);
request_module("scsi_dh_%s", m->hw_handler_name);
if (scsi_dh_handler_exist(m->hw_handler_name) == 0) {
ti->error = "unknown hardware handler type";
kfree(m->hw_handler_name);
m->hw_handler_name = NULL;
return -EINVAL;
}
m->hw_handler.md = dm_table_get_md(ti->table);
dm_put(m->hw_handler.md);
r = hwht->create(&m->hw_handler, hw_argc - 1, as->argv);
if (r) {
dm_put_hw_handler(hwht);
ti->error = "hardware handler constructor failed";
return r;
}
m->hw_handler.type = hwht;
consume(as, hw_argc - 1);
return 0;
@ -808,6 +791,7 @@ static void multipath_dtr(struct dm_target *ti)
{
struct multipath *m = (struct multipath *) ti->private;
flush_workqueue(kmpath_handlerd);
flush_workqueue(kmultipathd);
free_multipath(m);
}
@ -1025,52 +1009,85 @@ static int pg_init_limit_reached(struct multipath *m, struct pgpath *pgpath)
return limit_reached;
}
/*
* pg_init must call this when it has completed its initialisation
*/
void dm_pg_init_complete(struct dm_path *path, unsigned err_flags)
static void pg_init_done(struct dm_path *path, int errors)
{
struct pgpath *pgpath = path_to_pgpath(path);
struct priority_group *pg = pgpath->pg;
struct multipath *m = pg->m;
unsigned long flags;
/*
* If requested, retry pg_init until maximum number of retries exceeded.
* If retry not requested and PG already bypassed, always fail the path.
*/
if (err_flags & MP_RETRY) {
if (pg_init_limit_reached(m, pgpath))
err_flags |= MP_FAIL_PATH;
} else if (err_flags && pg->bypassed)
err_flags |= MP_FAIL_PATH;
if (err_flags & MP_FAIL_PATH)
/* device or driver problems */
switch (errors) {
case SCSI_DH_OK:
break;
case SCSI_DH_NOSYS:
if (!m->hw_handler_name) {
errors = 0;
break;
}
DMERR("Cannot failover device because scsi_dh_%s was not "
"loaded.", m->hw_handler_name);
/*
* Fail path for now, so we do not ping pong
*/
fail_path(pgpath);
if (err_flags & MP_BYPASS_PG)
break;
case SCSI_DH_DEV_TEMP_BUSY:
/*
* Probably doing something like FW upgrade on the
* controller so try the other pg.
*/
bypass_pg(m, pg, 1);
break;
/* TODO: For SCSI_DH_RETRY we should wait a couple seconds */
case SCSI_DH_RETRY:
case SCSI_DH_IMM_RETRY:
case SCSI_DH_RES_TEMP_UNAVAIL:
if (pg_init_limit_reached(m, pgpath))
fail_path(pgpath);
errors = 0;
break;
default:
/*
* We probably do not want to fail the path for a device
* error, but this is what the old dm did. In future
* patches we can do more advanced handling.
*/
fail_path(pgpath);
}
spin_lock_irqsave(&m->lock, flags);
if (err_flags & ~MP_RETRY) {
if (errors) {
DMERR("Could not failover device. Error %d.", errors);
m->current_pgpath = NULL;
m->current_pg = NULL;
} else if (!m->pg_init_required)
} else if (!m->pg_init_required) {
m->queue_io = 0;
pg->bypassed = 0;
}
m->pg_init_in_progress = 0;
queue_work(kmultipathd, &m->process_queued_ios);
spin_unlock_irqrestore(&m->lock, flags);
}
static void activate_path(struct work_struct *work)
{
int ret;
struct multipath *m =
container_of(work, struct multipath, activate_path);
struct dm_path *path = &m->current_pgpath->path;
ret = scsi_dh_activate(bdev_get_queue(path->dev->bdev));
pg_init_done(path, ret);
}
/*
* end_io handling
*/
static int do_end_io(struct multipath *m, struct bio *bio,
int error, struct dm_mpath_io *mpio)
{
struct hw_handler *hwh = &m->hw_handler;
unsigned err_flags = MP_FAIL_PATH; /* Default behavior */
unsigned long flags;
if (!error)
@ -1097,19 +1114,8 @@ static int do_end_io(struct multipath *m, struct bio *bio,
}
spin_unlock_irqrestore(&m->lock, flags);
if (hwh->type && hwh->type->error)
err_flags = hwh->type->error(hwh, bio);
if (mpio->pgpath) {
if (err_flags & MP_FAIL_PATH)
fail_path(mpio->pgpath);
if (err_flags & MP_BYPASS_PG)
bypass_pg(m, mpio->pgpath->pg, 1);
}
if (err_flags & MP_ERROR_IO)
return -EIO;
if (mpio->pgpath)
fail_path(mpio->pgpath);
requeue:
dm_bio_restore(&mpio->details, bio);
@ -1194,7 +1200,6 @@ static int multipath_status(struct dm_target *ti, status_type_t type,
int sz = 0;
unsigned long flags;
struct multipath *m = (struct multipath *) ti->private;
struct hw_handler *hwh = &m->hw_handler;
struct priority_group *pg;
struct pgpath *p;
unsigned pg_num;
@ -1214,12 +1219,10 @@ static int multipath_status(struct dm_target *ti, status_type_t type,
DMEMIT("pg_init_retries %u ", m->pg_init_retries);
}
if (hwh->type && hwh->type->status)
sz += hwh->type->status(hwh, type, result + sz, maxlen - sz);
else if (!hwh->type || type == STATUSTYPE_INFO)
if (!m->hw_handler_name || type == STATUSTYPE_INFO)
DMEMIT("0 ");
else
DMEMIT("1 %s ", hwh->type->name);
DMEMIT("1 %s ", m->hw_handler_name);
DMEMIT("%u ", m->nr_priority_groups);
@ -1422,6 +1425,21 @@ static int __init dm_multipath_init(void)
return -ENOMEM;
}
/*
* A separate workqueue is used to handle the device handlers
* to avoid overloading existing workqueue. Overloading the
* old workqueue would also create a bottleneck in the
* path of the storage hardware device activation.
*/
kmpath_handlerd = create_singlethread_workqueue("kmpath_handlerd");
if (!kmpath_handlerd) {
DMERR("failed to create workqueue kmpath_handlerd");
destroy_workqueue(kmultipathd);
dm_unregister_target(&multipath_target);
kmem_cache_destroy(_mpio_cache);
return -ENOMEM;
}
DMINFO("version %u.%u.%u loaded",
multipath_target.version[0], multipath_target.version[1],
multipath_target.version[2]);
@ -1433,6 +1451,7 @@ static void __exit dm_multipath_exit(void)
{
int r;
destroy_workqueue(kmpath_handlerd);
destroy_workqueue(kmultipathd);
r = dm_unregister_target(&multipath_target);
@ -1441,8 +1460,6 @@ static void __exit dm_multipath_exit(void)
kmem_cache_destroy(_mpio_cache);
}
EXPORT_SYMBOL_GPL(dm_pg_init_complete);
module_init(dm_multipath_init);
module_exit(dm_multipath_exit);

View File

@ -16,7 +16,6 @@ struct dm_path {
unsigned is_active; /* Read-only */
void *pscontext; /* For path-selector use */
void *hwhcontext; /* For hw-handler use */
};
/* Callback for hwh_pg_init_fn to use when complete */

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2000-2007 LSI Corporation.
* Copyright (c) 2000-2008 LSI Corporation.
*
*
* Name: mpi.h

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2000-2007 LSI Corporation.
* Copyright (c) 2000-2008 LSI Corporation.
*
*
* Name: mpi_cnfg.h

View File

@ -5,7 +5,7 @@
* For use with LSI PCI chip/adapter(s)
* running LSI Fusion MPT (Message Passing Technology) firmware.
*
* Copyright (c) 1999-2007 LSI Corporation
* Copyright (c) 1999-2008 LSI Corporation
* (mailto:DL-MPTFusionLinux@lsi.com)
*
*/
@ -103,7 +103,7 @@ static int mfcounter = 0;
* Public data...
*/
struct proc_dir_entry *mpt_proc_root_dir;
static struct proc_dir_entry *mpt_proc_root_dir;
#define WHOINIT_UNKNOWN 0xAA
@ -253,6 +253,55 @@ mpt_get_cb_idx(MPT_DRIVER_CLASS dclass)
return 0;
}
/**
* mpt_fault_reset_work - work performed on workq after ioc fault
* @work: input argument, used to derive ioc
*
**/
static void
mpt_fault_reset_work(struct work_struct *work)
{
MPT_ADAPTER *ioc =
container_of(work, MPT_ADAPTER, fault_reset_work.work);
u32 ioc_raw_state;
int rc;
unsigned long flags;
if (ioc->diagPending || !ioc->active)
goto out;
ioc_raw_state = mpt_GetIocState(ioc, 0);
if ((ioc_raw_state & MPI_IOC_STATE_MASK) == MPI_IOC_STATE_FAULT) {
printk(MYIOC_s_WARN_FMT "IOC is in FAULT state (%04xh)!!!\n",
ioc->name, ioc_raw_state & MPI_DOORBELL_DATA_MASK);
printk(MYIOC_s_WARN_FMT "Issuing HardReset from %s!!\n",
ioc->name, __FUNCTION__);
rc = mpt_HardResetHandler(ioc, CAN_SLEEP);
printk(MYIOC_s_WARN_FMT "%s: HardReset: %s\n", ioc->name,
__FUNCTION__, (rc == 0) ? "success" : "failed");
ioc_raw_state = mpt_GetIocState(ioc, 0);
if ((ioc_raw_state & MPI_IOC_STATE_MASK) == MPI_IOC_STATE_FAULT)
printk(MYIOC_s_WARN_FMT "IOC is in FAULT state after "
"reset (%04xh)\n", ioc->name, ioc_raw_state &
MPI_DOORBELL_DATA_MASK);
}
out:
/*
* Take turns polling alternate controller
*/
if (ioc->alt_ioc)
ioc = ioc->alt_ioc;
/* rearm the timer */
spin_lock_irqsave(&ioc->fault_reset_work_lock, flags);
if (ioc->reset_work_q)
queue_delayed_work(ioc->reset_work_q, &ioc->fault_reset_work,
msecs_to_jiffies(MPT_POLLING_INTERVAL));
spin_unlock_irqrestore(&ioc->fault_reset_work_lock, flags);
}
/*
* Process turbo (context) reply...
*/
@ -1616,6 +1665,22 @@ mpt_attach(struct pci_dev *pdev, const struct pci_device_id *id)
/* Find lookup slot. */
INIT_LIST_HEAD(&ioc->list);
/* Initialize workqueue */
INIT_DELAYED_WORK(&ioc->fault_reset_work, mpt_fault_reset_work);
spin_lock_init(&ioc->fault_reset_work_lock);
snprintf(ioc->reset_work_q_name, KOBJ_NAME_LEN, "mpt_poll_%d", ioc->id);
ioc->reset_work_q =
create_singlethread_workqueue(ioc->reset_work_q_name);
if (!ioc->reset_work_q) {
printk(MYIOC_s_ERR_FMT "Insufficient memory to add adapter!\n",
ioc->name);
pci_release_selected_regions(pdev, ioc->bars);
kfree(ioc);
return -ENOMEM;
}
dinitprintk(ioc, printk(MYIOC_s_INFO_FMT "facts @ %p, pfacts[0] @ %p\n",
ioc->name, &ioc->facts, &ioc->pfacts[0]));
@ -1727,6 +1792,10 @@ mpt_attach(struct pci_dev *pdev, const struct pci_device_id *id)
iounmap(ioc->memmap);
if (r != -5)
pci_release_selected_regions(pdev, ioc->bars);
destroy_workqueue(ioc->reset_work_q);
ioc->reset_work_q = NULL;
kfree(ioc);
pci_set_drvdata(pdev, NULL);
return r;
@ -1759,6 +1828,10 @@ mpt_attach(struct pci_dev *pdev, const struct pci_device_id *id)
}
#endif
if (!ioc->alt_ioc)
queue_delayed_work(ioc->reset_work_q, &ioc->fault_reset_work,
msecs_to_jiffies(MPT_POLLING_INTERVAL));
return 0;
}
@ -1774,6 +1847,19 @@ mpt_detach(struct pci_dev *pdev)
MPT_ADAPTER *ioc = pci_get_drvdata(pdev);
char pname[32];
u8 cb_idx;
unsigned long flags;
struct workqueue_struct *wq;
/*
* Stop polling ioc for fault condition
*/
spin_lock_irqsave(&ioc->fault_reset_work_lock, flags);
wq = ioc->reset_work_q;
ioc->reset_work_q = NULL;
spin_unlock_irqrestore(&ioc->fault_reset_work_lock, flags);
cancel_delayed_work(&ioc->fault_reset_work);
destroy_workqueue(wq);
sprintf(pname, MPT_PROCFS_MPTBASEDIR "/%s/summary", ioc->name);
remove_proc_entry(pname, NULL);
@ -7456,7 +7542,6 @@ EXPORT_SYMBOL(mpt_resume);
EXPORT_SYMBOL(mpt_suspend);
#endif
EXPORT_SYMBOL(ioc_list);
EXPORT_SYMBOL(mpt_proc_root_dir);
EXPORT_SYMBOL(mpt_register);
EXPORT_SYMBOL(mpt_deregister);
EXPORT_SYMBOL(mpt_event_register);

View File

@ -5,7 +5,7 @@
* LSIFC9xx/LSI409xx Fibre Channel
* running LSI Fusion MPT (Message Passing Technology) firmware.
*
* Copyright (c) 1999-2007 LSI Corporation
* Copyright (c) 1999-2008 LSI Corporation
* (mailto:DL-MPTFusionLinux@lsi.com)
*
*/
@ -73,11 +73,11 @@
#endif
#ifndef COPYRIGHT
#define COPYRIGHT "Copyright (c) 1999-2007 " MODULEAUTHOR
#define COPYRIGHT "Copyright (c) 1999-2008 " MODULEAUTHOR
#endif
#define MPT_LINUX_VERSION_COMMON "3.04.06"
#define MPT_LINUX_PACKAGE_NAME "@(#)mptlinux-3.04.06"
#define MPT_LINUX_VERSION_COMMON "3.04.07"
#define MPT_LINUX_PACKAGE_NAME "@(#)mptlinux-3.04.07"
#define WHAT_MAGIC_STRING "@" "(" "#" ")"
#define show_mptmod_ver(s,ver) \
@ -176,6 +176,8 @@
/* debug print string length used for events and iocstatus */
# define EVENT_DESCR_STR_SZ 100
#define MPT_POLLING_INTERVAL 1000 /* in milliseconds */
#ifdef __KERNEL__ /* { */
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
@ -709,6 +711,12 @@ typedef struct _MPT_ADAPTER
struct workqueue_struct *fc_rescan_work_q;
struct scsi_cmnd **ScsiLookup;
spinlock_t scsi_lookup_lock;
char reset_work_q_name[KOBJ_NAME_LEN];
struct workqueue_struct *reset_work_q;
struct delayed_work fault_reset_work;
spinlock_t fault_reset_work_lock;
} MPT_ADAPTER;
/*
@ -919,7 +927,6 @@ extern int mpt_raid_phys_disk_pg0(MPT_ADAPTER *ioc, u8 phys_disk_num, pRaidPhys
* Public data decl's...
*/
extern struct list_head ioc_list;
extern struct proc_dir_entry *mpt_proc_root_dir;
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
#endif /* } __KERNEL__ */

View File

@ -4,7 +4,7 @@
* For use with LSI PCI chip/adapters
* running LSI Fusion MPT (Message Passing Technology) firmware.
*
* Copyright (c) 1999-2007 LSI Corporation
* Copyright (c) 1999-2008 LSI Corporation
* (mailto:DL-MPTFusionLinux@lsi.com)
*
*/
@ -66,7 +66,7 @@
#include <scsi/scsi_host.h>
#include <scsi/scsi_tcq.h>
#define COPYRIGHT "Copyright (c) 1999-2007 LSI Corporation"
#define COPYRIGHT "Copyright (c) 1999-2008 LSI Corporation"
#define MODULEAUTHOR "LSI Corporation"
#include "mptbase.h"
#include "mptctl.h"

View File

@ -5,7 +5,7 @@
* LSIFC9xx/LSI409xx Fibre Channel
* running LSI Fusion MPT (Message Passing Technology) firmware.
*
* Copyright (c) 1999-2007 LSI Corporation
* Copyright (c) 1999-2008 LSI Corporation
* (mailto:DL-MPTFusionLinux@lsi.com)
*
*/

View File

@ -3,7 +3,7 @@
* For use with LSI PCI chip/adapter(s)
* running LSI Fusion MPT (Message Passing Technology) firmware.
*
* Copyright (c) 1999-2007 LSI Corporation
* Copyright (c) 1999-2008 LSI Corporation
* (mailto:DL-MPTFusionLinux@lsi.com)
*
*/

View File

@ -3,7 +3,7 @@
* For use with LSI PCI chip/adapter(s)
* running LSI Fusion MPT (Message Passing Technology) firmware.
*
* Copyright (c) 1999-2007 LSI Corporation
* Copyright (c) 1999-2008 LSI Corporation
* (mailto:DL-MPTFusionLinux@lsi.com)
*
*/

View File

@ -4,7 +4,7 @@
* For use with LSI Fibre Channel PCI chip/adapters
* running LSI Fusion MPT (Message Passing Technology) firmware.
*
* Copyright (c) 2000-2007 LSI Corporation
* Copyright (c) 2000-2008 LSI Corporation
* (mailto:DL-MPTFusionLinux@lsi.com)
*
*/

View File

@ -4,7 +4,7 @@
* For use with LSI Fibre Channel PCI chip/adapters
* running LSI Fusion MPT (Message Passing Technology) firmware.
*
* Copyright (c) 2000-2007 LSI Corporation
* Copyright (c) 2000-2008 LSI Corporation
* (mailto:DL-MPTFusionLinux@lsi.com)
*
*/

View File

@ -3,7 +3,7 @@
* For use with LSI PCI chip/adapter(s)
* running LSI Fusion MPT (Message Passing Technology) firmware.
*
* Copyright (c) 1999-2007 LSI Corporation
* Copyright (c) 1999-2008 LSI Corporation
* (mailto:DL-MPTFusionLinux@lsi.com)
*/
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/

View File

@ -5,7 +5,7 @@
* LSIFC9xx/LSI409xx Fibre Channel
* running LSI MPT (Message Passing Technology) firmware.
*
* Copyright (c) 1999-2007 LSI Corporation
* Copyright (c) 1999-2008 LSI Corporation
* (mailto:DL-MPTFusionLinux@lsi.com)
*
*/

View File

@ -3,7 +3,7 @@
* For use with LSI PCI chip/adapter(s)
* running LSI Fusion MPT (Message Passing Technology) firmware.
*
* Copyright (c) 1999-2007 LSI Corporation
* Copyright (c) 1999-2008 LSI Corporation
* (mailto:DL-MPTFusionLinux@lsi.com)
*
*/

View File

@ -5,7 +5,7 @@
* LSIFC9xx/LSI409xx Fibre Channel
* running LSI Fusion MPT (Message Passing Technology) firmware.
*
* Copyright (c) 1999-2007 LSI Corporation
* Copyright (c) 1999-2008 LSI Corporation
* (mailto:DL-MPTFusionLinux@lsi.com)
*
*/

View File

@ -3,7 +3,7 @@
* For use with LSI PCI chip/adapter(s)
* running LSI Fusion MPT (Message Passing Technology) firmware.
*
* Copyright (c) 1999-2007 LSI Corporation
* Copyright (c) 1999-2008 LSI Corporation
* (mailto:DL-MPTFusionLinux@lsi.com)
*
*/
@ -447,6 +447,7 @@ static int mptspi_target_alloc(struct scsi_target *starget)
spi_max_offset(starget) = ioc->spi_data.maxSyncOffset;
spi_offset(starget) = 0;
spi_period(starget) = 0xFF;
mptspi_write_width(starget, 0);
return 0;

View File

@ -3,7 +3,6 @@
#
zfcp-objs := zfcp_aux.o zfcp_ccw.o zfcp_scsi.o zfcp_erp.o zfcp_qdio.o \
zfcp_fsf.o zfcp_dbf.o zfcp_sysfs_adapter.o zfcp_sysfs_port.o \
zfcp_sysfs_unit.o zfcp_sysfs_driver.o
zfcp_fsf.o zfcp_dbf.o zfcp_sysfs.o zfcp_fc.o zfcp_cfdc.o
obj-$(CONFIG_ZFCP) += zfcp.o

File diff suppressed because it is too large Load Diff

View File

@ -1,64 +1,13 @@
/*
* This file is part of the zfcp device driver for
* FCP adapters for IBM System z9 and zSeries.
* zfcp device driver
*
* (C) Copyright IBM Corp. 2002, 2006
* Registration and callback for the s390 common I/O layer.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* Copyright IBM Corporation 2002, 2008
*/
#include "zfcp_ext.h"
#define ZFCP_LOG_AREA ZFCP_LOG_AREA_CONFIG
static int zfcp_ccw_probe(struct ccw_device *);
static void zfcp_ccw_remove(struct ccw_device *);
static int zfcp_ccw_set_online(struct ccw_device *);
static int zfcp_ccw_set_offline(struct ccw_device *);
static int zfcp_ccw_notify(struct ccw_device *, int);
static void zfcp_ccw_shutdown(struct ccw_device *);
static struct ccw_device_id zfcp_ccw_device_id[] = {
{CCW_DEVICE_DEVTYPE(ZFCP_CONTROL_UNIT_TYPE,
ZFCP_CONTROL_UNIT_MODEL,
ZFCP_DEVICE_TYPE,
ZFCP_DEVICE_MODEL)},
{CCW_DEVICE_DEVTYPE(ZFCP_CONTROL_UNIT_TYPE,
ZFCP_CONTROL_UNIT_MODEL,
ZFCP_DEVICE_TYPE,
ZFCP_DEVICE_MODEL_PRIV)},
{},
};
static struct ccw_driver zfcp_ccw_driver = {
.owner = THIS_MODULE,
.name = ZFCP_NAME,
.ids = zfcp_ccw_device_id,
.probe = zfcp_ccw_probe,
.remove = zfcp_ccw_remove,
.set_online = zfcp_ccw_set_online,
.set_offline = zfcp_ccw_set_offline,
.notify = zfcp_ccw_notify,
.shutdown = zfcp_ccw_shutdown,
.driver = {
.groups = zfcp_driver_attr_groups,
},
};
MODULE_DEVICE_TABLE(ccw, zfcp_ccw_device_id);
/**
* zfcp_ccw_probe - probe function of zfcp driver
* @ccw_device: pointer to belonging ccw device
@ -69,19 +18,16 @@ MODULE_DEVICE_TABLE(ccw, zfcp_ccw_device_id);
* In addition the nameserver port will be added to the ports of the adapter
* and its sysfs representation will be created too.
*/
static int
zfcp_ccw_probe(struct ccw_device *ccw_device)
static int zfcp_ccw_probe(struct ccw_device *ccw_device)
{
struct zfcp_adapter *adapter;
int retval = 0;
down(&zfcp_data.config_sema);
adapter = zfcp_adapter_enqueue(ccw_device);
if (!adapter)
if (zfcp_adapter_enqueue(ccw_device)) {
dev_err(&ccw_device->dev,
"Setup of data structures failed.\n");
retval = -EINVAL;
else
ZFCP_LOG_DEBUG("Probed adapter %s\n",
zfcp_get_busid_by_adapter(adapter));
}
up(&zfcp_data.config_sema);
return retval;
}
@ -95,8 +41,7 @@ zfcp_ccw_probe(struct ccw_device *ccw_device)
* ports that belong to this adapter. And in addition all resources of this
* adapter will be freed too.
*/
static void
zfcp_ccw_remove(struct ccw_device *ccw_device)
static void zfcp_ccw_remove(struct ccw_device *ccw_device)
{
struct zfcp_adapter *adapter;
struct zfcp_port *port, *p;
@ -106,8 +51,6 @@ zfcp_ccw_remove(struct ccw_device *ccw_device)
down(&zfcp_data.config_sema);
adapter = dev_get_drvdata(&ccw_device->dev);
ZFCP_LOG_DEBUG("Removing adapter %s\n",
zfcp_get_busid_by_adapter(adapter));
write_lock_irq(&zfcp_data.config_lock);
list_for_each_entry_safe(port, p, &adapter->port_list_head, list) {
list_for_each_entry_safe(unit, u, &port->unit_list_head, list) {
@ -145,8 +88,7 @@ zfcp_ccw_remove(struct ccw_device *ccw_device)
* registered with the SCSI stack, that the QDIO queues will be set up
* and that the adapter will be opened (asynchronously).
*/
static int
zfcp_ccw_set_online(struct ccw_device *ccw_device)
static int zfcp_ccw_set_online(struct ccw_device *ccw_device)
{
struct zfcp_adapter *adapter;
int retval;
@ -155,12 +97,8 @@ zfcp_ccw_set_online(struct ccw_device *ccw_device)
adapter = dev_get_drvdata(&ccw_device->dev);
retval = zfcp_erp_thread_setup(adapter);
if (retval) {
ZFCP_LOG_INFO("error: start of error recovery thread for "
"adapter %s failed\n",
zfcp_get_busid_by_adapter(adapter));
if (retval)
goto out;
}
retval = zfcp_adapter_scsi_register(adapter);
if (retval)
@ -191,8 +129,7 @@ zfcp_ccw_set_online(struct ccw_device *ccw_device)
* This function gets called by the common i/o layer and sets an adapter
* into state offline.
*/
static int
zfcp_ccw_set_offline(struct ccw_device *ccw_device)
static int zfcp_ccw_set_offline(struct ccw_device *ccw_device)
{
struct zfcp_adapter *adapter;
@ -206,15 +143,14 @@ zfcp_ccw_set_offline(struct ccw_device *ccw_device)
}
/**
* zfcp_ccw_notify
* zfcp_ccw_notify - ccw notify function
* @ccw_device: pointer to belonging ccw device
* @event: indicates if adapter was detached or attached
*
* This function gets called by the common i/o layer if an adapter has gone
* or reappeared.
*/
static int
zfcp_ccw_notify(struct ccw_device *ccw_device, int event)
static int zfcp_ccw_notify(struct ccw_device *ccw_device, int event)
{
struct zfcp_adapter *adapter;
@ -222,18 +158,15 @@ zfcp_ccw_notify(struct ccw_device *ccw_device, int event)
adapter = dev_get_drvdata(&ccw_device->dev);
switch (event) {
case CIO_GONE:
ZFCP_LOG_NORMAL("adapter %s: device gone\n",
zfcp_get_busid_by_adapter(adapter));
dev_warn(&adapter->ccw_device->dev, "device gone\n");
zfcp_erp_adapter_shutdown(adapter, 0, 87, NULL);
break;
case CIO_NO_PATH:
ZFCP_LOG_NORMAL("adapter %s: no path\n",
zfcp_get_busid_by_adapter(adapter));
dev_warn(&adapter->ccw_device->dev, "no path\n");
zfcp_erp_adapter_shutdown(adapter, 0, 88, NULL);
break;
case CIO_OPER:
ZFCP_LOG_NORMAL("adapter %s: operational again\n",
zfcp_get_busid_by_adapter(adapter));
dev_info(&adapter->ccw_device->dev, "operational again\n");
zfcp_erp_modify_adapter_status(adapter, 11, NULL,
ZFCP_STATUS_COMMON_RUNNING,
ZFCP_SET);
@ -247,24 +180,10 @@ zfcp_ccw_notify(struct ccw_device *ccw_device, int event)
}
/**
* zfcp_ccw_register - ccw register function
*
* Registers the driver at the common i/o layer. This function will be called
* at module load time/system start.
* zfcp_ccw_shutdown - handle shutdown from cio
* @cdev: device for adapter to shutdown.
*/
int __init
zfcp_ccw_register(void)
{
return ccw_driver_register(&zfcp_ccw_driver);
}
/**
* zfcp_ccw_shutdown - gets called on reboot/shutdown
*
* Makes sure that QDIO queues are down when the system gets stopped.
*/
static void
zfcp_ccw_shutdown(struct ccw_device *cdev)
static void zfcp_ccw_shutdown(struct ccw_device *cdev)
{
struct zfcp_adapter *adapter;
@ -275,4 +194,33 @@ zfcp_ccw_shutdown(struct ccw_device *cdev)
up(&zfcp_data.config_sema);
}
#undef ZFCP_LOG_AREA
static struct ccw_device_id zfcp_ccw_device_id[] = {
{ CCW_DEVICE_DEVTYPE(0x1731, 0x3, 0x1732, 0x3) },
{ CCW_DEVICE_DEVTYPE(0x1731, 0x3, 0x1732, 0x4) }, /* priv. */
{},
};
MODULE_DEVICE_TABLE(ccw, zfcp_ccw_device_id);
static struct ccw_driver zfcp_ccw_driver = {
.owner = THIS_MODULE,
.name = "zfcp",
.ids = zfcp_ccw_device_id,
.probe = zfcp_ccw_probe,
.remove = zfcp_ccw_remove,
.set_online = zfcp_ccw_set_online,
.set_offline = zfcp_ccw_set_offline,
.notify = zfcp_ccw_notify,
.shutdown = zfcp_ccw_shutdown,
};
/**
* zfcp_ccw_register - ccw register function
*
* Registers the driver at the common i/o layer. This function will be called
* at module load time/system start.
*/
int __init zfcp_ccw_register(void)
{
return ccw_driver_register(&zfcp_ccw_driver);
}

View File

@ -0,0 +1,259 @@
/*
* zfcp device driver
*
* Userspace interface for accessing the
* Access Control Lists / Control File Data Channel
*
* Copyright IBM Corporation 2008
*/
#include <linux/types.h>
#include <linux/miscdevice.h>
#include <asm/ccwdev.h>
#include "zfcp_def.h"
#include "zfcp_ext.h"
#include "zfcp_fsf.h"
#define ZFCP_CFDC_CMND_DOWNLOAD_NORMAL 0x00010001
#define ZFCP_CFDC_CMND_DOWNLOAD_FORCE 0x00010101
#define ZFCP_CFDC_CMND_FULL_ACCESS 0x00000201
#define ZFCP_CFDC_CMND_RESTRICTED_ACCESS 0x00000401
#define ZFCP_CFDC_CMND_UPLOAD 0x00010002
#define ZFCP_CFDC_DOWNLOAD 0x00000001
#define ZFCP_CFDC_UPLOAD 0x00000002
#define ZFCP_CFDC_WITH_CONTROL_FILE 0x00010000
#define ZFCP_CFDC_IOC_MAGIC 0xDD
#define ZFCP_CFDC_IOC \
_IOWR(ZFCP_CFDC_IOC_MAGIC, 0, struct zfcp_cfdc_data)
/**
* struct zfcp_cfdc_data - data for ioctl cfdc interface
* @signature: request signature
* @devno: FCP adapter device number
* @command: command code
* @fsf_status: returns status of FSF command to userspace
* @fsf_status_qual: returned to userspace
* @payloads: access conflicts list
* @control_file: access control table
*/
struct zfcp_cfdc_data {
u32 signature;
u32 devno;
u32 command;
u32 fsf_status;
u8 fsf_status_qual[FSF_STATUS_QUALIFIER_SIZE];
u8 payloads[256];
u8 control_file[0];
};
static int zfcp_cfdc_copy_from_user(struct scatterlist *sg,
void __user *user_buffer)
{
unsigned int length;
unsigned int size = ZFCP_CFDC_MAX_SIZE;
while (size) {
length = min((unsigned int)size, sg->length);
if (copy_from_user(sg_virt(sg++), user_buffer, length))
return -EFAULT;
user_buffer += length;
size -= length;
}
return 0;
}
static int zfcp_cfdc_copy_to_user(void __user *user_buffer,
struct scatterlist *sg)
{
unsigned int length;
unsigned int size = ZFCP_CFDC_MAX_SIZE;
while (size) {
length = min((unsigned int) size, sg->length);
if (copy_to_user(user_buffer, sg_virt(sg++), length))
return -EFAULT;
user_buffer += length;
size -= length;
}
return 0;
}
static struct zfcp_adapter *zfcp_cfdc_get_adapter(u32 devno)
{
struct zfcp_adapter *adapter = NULL, *cur_adapter;
struct ccw_dev_id dev_id;
read_lock_irq(&zfcp_data.config_lock);
list_for_each_entry(cur_adapter, &zfcp_data.adapter_list_head, list) {
ccw_device_get_id(cur_adapter->ccw_device, &dev_id);
if (dev_id.devno == devno) {
adapter = cur_adapter;
zfcp_adapter_get(adapter);
break;
}
}
read_unlock_irq(&zfcp_data.config_lock);
return adapter;
}
static int zfcp_cfdc_set_fsf(struct zfcp_fsf_cfdc *fsf_cfdc, int command)
{
switch (command) {
case ZFCP_CFDC_CMND_DOWNLOAD_NORMAL:
fsf_cfdc->command = FSF_QTCB_DOWNLOAD_CONTROL_FILE;
fsf_cfdc->option = FSF_CFDC_OPTION_NORMAL_MODE;
break;
case ZFCP_CFDC_CMND_DOWNLOAD_FORCE:
fsf_cfdc->command = FSF_QTCB_DOWNLOAD_CONTROL_FILE;
fsf_cfdc->option = FSF_CFDC_OPTION_FORCE;
break;
case ZFCP_CFDC_CMND_FULL_ACCESS:
fsf_cfdc->command = FSF_QTCB_DOWNLOAD_CONTROL_FILE;
fsf_cfdc->option = FSF_CFDC_OPTION_FULL_ACCESS;
break;
case ZFCP_CFDC_CMND_RESTRICTED_ACCESS:
fsf_cfdc->command = FSF_QTCB_DOWNLOAD_CONTROL_FILE;
fsf_cfdc->option = FSF_CFDC_OPTION_RESTRICTED_ACCESS;
break;
case ZFCP_CFDC_CMND_UPLOAD:
fsf_cfdc->command = FSF_QTCB_UPLOAD_CONTROL_FILE;
fsf_cfdc->option = 0;
break;
default:
return -EINVAL;
}
return 0;
}
static int zfcp_cfdc_sg_setup(int command, struct scatterlist *sg,
u8 __user *control_file)
{
int retval;
retval = zfcp_sg_setup_table(sg, ZFCP_CFDC_PAGES);
if (retval)
return retval;
sg[ZFCP_CFDC_PAGES - 1].length = ZFCP_CFDC_MAX_SIZE % PAGE_SIZE;
if (command & ZFCP_CFDC_WITH_CONTROL_FILE &&
command & ZFCP_CFDC_DOWNLOAD) {
retval = zfcp_cfdc_copy_from_user(sg, control_file);
if (retval) {
zfcp_sg_free_table(sg, ZFCP_CFDC_PAGES);
return -EFAULT;
}
}
return 0;
}
static void zfcp_cfdc_req_to_sense(struct zfcp_cfdc_data *data,
struct zfcp_fsf_req *req)
{
data->fsf_status = req->qtcb->header.fsf_status;
memcpy(&data->fsf_status_qual, &req->qtcb->header.fsf_status_qual,
sizeof(union fsf_status_qual));
memcpy(&data->payloads, &req->qtcb->bottom.support.els,
sizeof(req->qtcb->bottom.support.els));
}
static long zfcp_cfdc_dev_ioctl(struct file *file, unsigned int command,
unsigned long buffer)
{
struct zfcp_cfdc_data *data;
struct zfcp_cfdc_data __user *data_user;
struct zfcp_adapter *adapter;
struct zfcp_fsf_req *req;
struct zfcp_fsf_cfdc *fsf_cfdc;
int retval;
if (command != ZFCP_CFDC_IOC)
return -ENOTTY;
data_user = (void __user *) buffer;
if (!data_user)
return -EINVAL;
fsf_cfdc = kmalloc(sizeof(struct zfcp_fsf_cfdc), GFP_KERNEL);
if (!fsf_cfdc)
return -ENOMEM;
data = kmalloc(sizeof(struct zfcp_cfdc_data), GFP_KERNEL);
if (!data) {
retval = -ENOMEM;
goto no_mem_sense;
}
retval = copy_from_user(data, data_user, sizeof(*data));
if (retval) {
retval = -EFAULT;
goto free_buffer;
}
if (data->signature != 0xCFDCACDF) {
retval = -EINVAL;
goto free_buffer;
}
retval = zfcp_cfdc_set_fsf(fsf_cfdc, data->command);
adapter = zfcp_cfdc_get_adapter(data->devno);
if (!adapter) {
retval = -ENXIO;
goto free_buffer;
}
retval = zfcp_cfdc_sg_setup(data->command, fsf_cfdc->sg,
data_user->control_file);
if (retval)
goto adapter_put;
req = zfcp_fsf_control_file(adapter, fsf_cfdc);
if (IS_ERR(req)) {
retval = PTR_ERR(req);
goto free_sg;
}
if (req->status & ZFCP_STATUS_FSFREQ_ERROR) {
retval = -ENXIO;
goto free_fsf;
}
zfcp_cfdc_req_to_sense(data, req);
retval = copy_to_user(data_user, data, sizeof(*data_user));
if (retval) {
retval = -EFAULT;
goto free_fsf;
}
if (data->command & ZFCP_CFDC_UPLOAD)
retval = zfcp_cfdc_copy_to_user(&data_user->control_file,
fsf_cfdc->sg);
free_fsf:
zfcp_fsf_req_free(req);
free_sg:
zfcp_sg_free_table(fsf_cfdc->sg, ZFCP_CFDC_PAGES);
adapter_put:
zfcp_adapter_put(adapter);
free_buffer:
kfree(data);
no_mem_sense:
kfree(fsf_cfdc);
return retval;
}
static const struct file_operations zfcp_cfdc_fops = {
.unlocked_ioctl = zfcp_cfdc_dev_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = zfcp_cfdc_dev_ioctl
#endif
};
struct miscdevice zfcp_cfdc_misc = {
.minor = MISC_DYNAMIC_MINOR,
.name = "zfcp_cfdc",
.fops = &zfcp_cfdc_fops,
};

View File

@ -1,22 +1,9 @@
/*
* This file is part of the zfcp device driver for
* FCP adapters for IBM System z9 and zSeries.
* zfcp device driver
*
* (C) Copyright IBM Corp. 2002, 2006
* Debug traces for zfcp.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* Copyright IBM Corporation 2002, 2008
*/
#include <linux/ctype.h>
@ -29,8 +16,6 @@ module_param(dbfsize, uint, 0400);
MODULE_PARM_DESC(dbfsize,
"number of pages for each debug feature area (default 4)");
#define ZFCP_LOG_AREA ZFCP_LOG_AREA_OTHER
static void zfcp_dbf_hexdump(debug_info_t *dbf, void *to, int to_len,
int level, char *from, int from_len)
{
@ -186,8 +171,8 @@ void zfcp_hba_dbf_event_fsf_response(struct zfcp_fsf_req *fsf_req)
fsf_status_qual, FSF_STATUS_QUALIFIER_SIZE);
response->fsf_req_status = fsf_req->status;
response->sbal_first = fsf_req->sbal_first;
response->sbal_curr = fsf_req->sbal_curr;
response->sbal_last = fsf_req->sbal_last;
response->sbal_response = fsf_req->sbal_response;
response->pool = fsf_req->pool != NULL;
response->erp_action = (unsigned long)fsf_req->erp_action;
@ -268,7 +253,7 @@ void zfcp_hba_dbf_event_fsf_unsol(const char *tag, struct zfcp_adapter *adapter,
strncpy(rec->tag, "stat", ZFCP_DBF_TAG_SIZE);
strncpy(rec->tag2, tag, ZFCP_DBF_TAG_SIZE);
rec->u.status.failed = adapter->status_read_failed;
rec->u.status.failed = atomic_read(&adapter->stat_miss);
if (status_buffer != NULL) {
rec->u.status.status_type = status_buffer->status_type;
rec->u.status.status_subtype = status_buffer->status_subtype;
@ -355,8 +340,8 @@ static void zfcp_hba_dbf_view_response(char **p,
FSF_STATUS_QUALIFIER_SIZE, 0, FSF_STATUS_QUALIFIER_SIZE);
zfcp_dbf_out(p, "fsf_req_status", "0x%08x", r->fsf_req_status);
zfcp_dbf_out(p, "sbal_first", "0x%02x", r->sbal_first);
zfcp_dbf_out(p, "sbal_curr", "0x%02x", r->sbal_curr);
zfcp_dbf_out(p, "sbal_last", "0x%02x", r->sbal_last);
zfcp_dbf_out(p, "sbal_response", "0x%02x", r->sbal_response);
zfcp_dbf_out(p, "pool", "0x%02x", r->pool);
switch (r->fsf_command) {
@ -515,13 +500,13 @@ static const char *zfcp_rec_dbf_ids[] = {
[52] = "port boxed close unit",
[53] = "port boxed fcp",
[54] = "unit boxed fcp",
[55] = "port access denied ct",
[56] = "port access denied els",
[57] = "port access denied open port",
[58] = "port access denied close physical",
[59] = "unit access denied open unit",
[55] = "port access denied",
[56] = "",
[57] = "",
[58] = "",
[59] = "unit access denied",
[60] = "shared unit access denied open unit",
[61] = "unit access denied fcp",
[61] = "",
[62] = "request timeout",
[63] = "adisc link test reject or timeout",
[64] = "adisc link test d_id changed",
@ -546,8 +531,8 @@ static const char *zfcp_rec_dbf_ids[] = {
[80] = "exclusive read-only unit access unsupported",
[81] = "shared read-write unit access unsupported",
[82] = "incoming rscn",
[83] = "incoming plogi",
[84] = "incoming logo",
[83] = "incoming wwpn",
[84] = "",
[85] = "online",
[86] = "offline",
[87] = "ccw device gone",
@ -586,8 +571,8 @@ static const char *zfcp_rec_dbf_ids[] = {
[120] = "unknown fsf command",
[121] = "no recommendation for status qualifier",
[122] = "status read physical port closed in error",
[123] = "fc service class not supported ct",
[124] = "fc service class not supported els",
[123] = "fc service class not supported",
[124] = "",
[125] = "need newer zfcp",
[126] = "need newer microcode",
[127] = "arbitrated loop not supported",
@ -595,7 +580,7 @@ static const char *zfcp_rec_dbf_ids[] = {
[129] = "qtcb size mismatch",
[130] = "unknown fsf status ecd",
[131] = "fcp request too big",
[132] = "fc service class not supported fcp",
[132] = "",
[133] = "data direction not valid fcp",
[134] = "command length not valid fcp",
[135] = "status read act update",
@ -603,13 +588,18 @@ static const char *zfcp_rec_dbf_ids[] = {
[137] = "hbaapi port open",
[138] = "hbaapi unit open",
[139] = "hbaapi unit shutdown",
[140] = "qdio error",
[140] = "qdio error outbound",
[141] = "scsi host reset",
[142] = "dismissing fsf request for recovery action",
[143] = "recovery action timed out",
[144] = "recovery action gone",
[145] = "recovery action being processed",
[146] = "recovery action ready for next step",
[147] = "qdio error inbound",
[148] = "nameserver needed for port scan",
[149] = "port scan",
[150] = "ptp attach",
[151] = "port validation failed",
};
static int zfcp_rec_dbf_view_format(debug_info_t *id, struct debug_view *view,
@ -670,24 +660,20 @@ static struct debug_view zfcp_rec_dbf_view = {
* zfcp_rec_dbf_event_thread - trace event related to recovery thread operation
* @id2: identifier for event
* @adapter: adapter
* @lock: non-zero value indicates that erp_lock has not yet been acquired
* This function assumes that the caller is holding erp_lock.
*/
void zfcp_rec_dbf_event_thread(u8 id2, struct zfcp_adapter *adapter, int lock)
void zfcp_rec_dbf_event_thread(u8 id2, struct zfcp_adapter *adapter)
{
struct zfcp_rec_dbf_record *r = &adapter->rec_dbf_buf;
unsigned long flags = 0;
struct list_head *entry;
unsigned ready = 0, running = 0, total;
if (lock)
read_lock_irqsave(&adapter->erp_lock, flags);
list_for_each(entry, &adapter->erp_ready_head)
ready++;
list_for_each(entry, &adapter->erp_running_head)
running++;
total = adapter->erp_total_count;
if (lock)
read_unlock_irqrestore(&adapter->erp_lock, flags);
spin_lock_irqsave(&adapter->rec_dbf_lock, flags);
memset(r, 0, sizeof(*r));
@ -696,10 +682,25 @@ void zfcp_rec_dbf_event_thread(u8 id2, struct zfcp_adapter *adapter, int lock)
r->u.thread.total = total;
r->u.thread.ready = ready;
r->u.thread.running = running;
debug_event(adapter->rec_dbf, 5, r, sizeof(*r));
debug_event(adapter->rec_dbf, 6, r, sizeof(*r));
spin_unlock_irqrestore(&adapter->rec_dbf_lock, flags);
}
/**
* zfcp_rec_dbf_event_thread - trace event related to recovery thread operation
* @id2: identifier for event
* @adapter: adapter
* This function assumes that the caller does not hold erp_lock.
*/
void zfcp_rec_dbf_event_thread_lock(u8 id2, struct zfcp_adapter *adapter)
{
unsigned long flags;
read_lock_irqsave(&adapter->erp_lock, flags);
zfcp_rec_dbf_event_thread(id2, adapter);
read_unlock_irqrestore(&adapter->erp_lock, flags);
}
static void zfcp_rec_dbf_event_target(u8 id2, void *ref,
struct zfcp_adapter *adapter,
atomic_t *status, atomic_t *erp_count,
@ -823,7 +824,7 @@ void zfcp_rec_dbf_event_action(u8 id2, struct zfcp_erp_action *erp_action)
r->u.action.status = erp_action->status;
r->u.action.step = erp_action->step;
r->u.action.fsf_req = (unsigned long)erp_action->fsf_req;
debug_event(adapter->rec_dbf, 4, r, sizeof(*r));
debug_event(adapter->rec_dbf, 5, r, sizeof(*r));
spin_unlock_irqrestore(&adapter->rec_dbf_lock, flags);
}
@ -960,7 +961,7 @@ void zfcp_san_dbf_event_incoming_els(struct zfcp_fsf_req *fsf_req)
zfcp_san_dbf_event_els("iels", 1, fsf_req, buf->d_id,
fc_host_port_id(adapter->scsi_host),
*(u8 *)buf->payload, (void *)buf->payload,
buf->payload.data[0], (void *)buf->payload.data,
length);
}
@ -1064,8 +1065,7 @@ static void zfcp_scsi_dbf_event(const char *tag, const char *tag2, int level,
if (fsf_req != NULL) {
fcp_rsp = (struct fcp_rsp_iu *)
&(fsf_req->qtcb->bottom.io.fcp_rsp);
fcp_rsp_info =
zfcp_get_fcp_rsp_info_ptr(fcp_rsp);
fcp_rsp_info = (unsigned char *) &fcp_rsp[1];
fcp_sns_info =
zfcp_get_fcp_sns_info_ptr(fcp_rsp);
@ -1279,5 +1279,3 @@ void zfcp_adapter_debug_unregister(struct zfcp_adapter *adapter)
adapter->hba_dbf = NULL;
adapter->rec_dbf = NULL;
}
#undef ZFCP_LOG_AREA

View File

@ -38,7 +38,7 @@ struct zfcp_rec_dbf_record_thread {
u32 total;
u32 ready;
u32 running;
} __attribute__ ((packed));
};
struct zfcp_rec_dbf_record_target {
u64 ref;
@ -47,7 +47,7 @@ struct zfcp_rec_dbf_record_target {
u64 wwpn;
u64 fcp_lun;
u32 erp_count;
} __attribute__ ((packed));
};
struct zfcp_rec_dbf_record_trigger {
u8 want;
@ -59,14 +59,14 @@ struct zfcp_rec_dbf_record_trigger {
u64 action;
u64 wwpn;
u64 fcp_lun;
} __attribute__ ((packed));
};
struct zfcp_rec_dbf_record_action {
u32 status;
u32 step;
u64 action;
u64 fsf_req;
} __attribute__ ((packed));
};
struct zfcp_rec_dbf_record {
u8 id;
@ -77,7 +77,7 @@ struct zfcp_rec_dbf_record {
struct zfcp_rec_dbf_record_target target;
struct zfcp_rec_dbf_record_trigger trigger;
} u;
} __attribute__ ((packed));
};
enum {
ZFCP_REC_DBF_ID_ACTION,
@ -97,8 +97,8 @@ struct zfcp_hba_dbf_record_response {
u8 fsf_status_qual[FSF_STATUS_QUALIFIER_SIZE];
u32 fsf_req_status;
u8 sbal_first;
u8 sbal_curr;
u8 sbal_last;
u8 sbal_response;
u8 pool;
u64 erp_action;
union {

View File

@ -1,22 +1,9 @@
/*
* This file is part of the zfcp device driver for
* FCP adapters for IBM System z9 and zSeries.
* zfcp device driver
*
* (C) Copyright IBM Corp. 2002, 2006
* Global definitions for the zfcp device driver.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* Copyright IBM Corporation 2002, 2008
*/
#ifndef ZFCP_DEF_H
@ -26,7 +13,6 @@
#include <linux/init.h>
#include <linux/moduleparam.h>
#include <linux/miscdevice.h>
#include <linux/major.h>
#include <linux/blkdev.h>
#include <linux/delay.h>
@ -53,9 +39,6 @@
/********************* GENERAL DEFINES *********************************/
/* zfcp version number, it consists of major, minor, and patch-level number */
#define ZFCP_VERSION "4.8.0"
/**
* zfcp_sg_to_address - determine kernel address from struct scatterlist
* @list: struct scatterlist
@ -93,11 +76,6 @@ zfcp_address_to_sg(void *address, struct scatterlist *list, unsigned int size)
#define ZFCP_DEVICE_MODEL 0x03
#define ZFCP_DEVICE_MODEL_PRIV 0x04
/* allow as many chained SBALs as are supported by hardware */
#define ZFCP_MAX_SBALS_PER_REQ FSF_MAX_SBALS_PER_REQ
#define ZFCP_MAX_SBALS_PER_CT_REQ FSF_MAX_SBALS_PER_REQ
#define ZFCP_MAX_SBALS_PER_ELS_REQ FSF_MAX_SBALS_PER_ELS_REQ
/* DMQ bug workaround: don't use last SBALE */
#define ZFCP_MAX_SBALES_PER_SBAL (QDIO_MAX_ELEMENTS_PER_BUFFER - 1)
@ -106,42 +84,17 @@ zfcp_address_to_sg(void *address, struct scatterlist *list, unsigned int size)
/* max. number of (data buffer) SBALEs in largest SBAL chain */
#define ZFCP_MAX_SBALES_PER_REQ \
(ZFCP_MAX_SBALS_PER_REQ * ZFCP_MAX_SBALES_PER_SBAL - 2)
(FSF_MAX_SBALS_PER_REQ * ZFCP_MAX_SBALES_PER_SBAL - 2)
/* request ID + QTCB in SBALE 0 + 1 of first SBAL in chain */
#define ZFCP_MAX_SECTORS (ZFCP_MAX_SBALES_PER_REQ * 8)
/* max. number of (data buffer) SBALEs in largest SBAL chain
multiplied with number of sectors per 4k block */
/* FIXME(tune): free space should be one max. SBAL chain plus what? */
#define ZFCP_QDIO_PCI_INTERVAL (QDIO_MAX_BUFFERS_PER_Q \
- (ZFCP_MAX_SBALS_PER_REQ + 4))
#define ZFCP_SBAL_TIMEOUT (5*HZ)
#define ZFCP_TYPE2_RECOVERY_TIME 8 /* seconds */
/* queue polling (values in microseconds) */
#define ZFCP_MAX_INPUT_THRESHOLD 5000 /* FIXME: tune */
#define ZFCP_MAX_OUTPUT_THRESHOLD 1000 /* FIXME: tune */
#define ZFCP_MIN_INPUT_THRESHOLD 1 /* ignored by QDIO layer */
#define ZFCP_MIN_OUTPUT_THRESHOLD 1 /* ignored by QDIO layer */
#define QDIO_SCSI_QFMT 1 /* 1 for FSF */
#define QBUFF_PER_PAGE (PAGE_SIZE / sizeof(struct qdio_buffer))
/********************* FSF SPECIFIC DEFINES *********************************/
#define ZFCP_ULP_INFO_VERSION 26
#define ZFCP_QTCB_VERSION FSF_QTCB_CURRENT_VERSION
/* ATTENTION: value must not be used by hardware */
#define FSF_QTCB_UNSOLICITED_STATUS 0x6305
#define ZFCP_STATUS_READ_FAILED_THRESHOLD 3
#define ZFCP_STATUS_READS_RECOM FSF_STATUS_READS_RECOM
/* Do 1st retry in 1 second, then double the timeout for each following retry */
#define ZFCP_EXCHANGE_CONFIG_DATA_FIRST_SLEEP 1
#define ZFCP_EXCHANGE_CONFIG_DATA_RETRIES 7
/* timeout value for "default timer" for fsf requests */
#define ZFCP_FSF_REQUEST_TIMEOUT (60*HZ)
@ -153,17 +106,9 @@ typedef unsigned long long fcp_lun_t;
/* data length field may be at variable position in FCP-2 FCP_CMND IU */
typedef unsigned int fcp_dl_t;
#define ZFCP_FC_SERVICE_CLASS_DEFAULT FSF_CLASS_3
/* timeout for name-server lookup (in seconds) */
#define ZFCP_NS_GID_PN_TIMEOUT 10
/* largest SCSI command we can process */
/* FCP-2 (FCP_CMND IU) allows up to (255-3+16) */
#define ZFCP_MAX_SCSI_CMND_LENGTH 255
/* maximum number of commands in LUN queue (tagged queueing) */
#define ZFCP_CMND_PER_LUN 32
/* task attribute values in FCP-2 FCP_CMND IU */
#define SIMPLE_Q 0
#define HEAD_OF_Q 1
@ -224,9 +169,9 @@ struct fcp_rsp_iu {
#define RSP_CODE_TASKMAN_FAILED 5
/* see fc-fs */
#define LS_RSCN 0x61040000
#define LS_LOGO 0x05000000
#define LS_PLOGI 0x03000000
#define LS_RSCN 0x61
#define LS_LOGO 0x05
#define LS_PLOGI 0x03
struct fcp_rscn_head {
u8 command;
@ -266,7 +211,6 @@ struct fcp_logo {
* FC-FS stuff
*/
#define R_A_TOV 10 /* seconds */
#define ZFCP_ELS_TIMEOUT (2 * R_A_TOV)
#define ZFCP_LS_RLS 0x0f
#define ZFCP_LS_ADISC 0x52
@ -311,7 +255,10 @@ struct zfcp_rc_entry {
#define ZFCP_CT_DIRECTORY_SERVICE 0xFC
#define ZFCP_CT_NAME_SERVER 0x02
#define ZFCP_CT_SYNCHRONOUS 0x00
#define ZFCP_CT_SCSI_FCP 0x08
#define ZFCP_CT_UNABLE_TO_PERFORM_CMD 0x09
#define ZFCP_CT_GID_PN 0x0121
#define ZFCP_CT_GPN_FT 0x0172
#define ZFCP_CT_MAX_SIZE 0x1020
#define ZFCP_CT_ACCEPT 0x8002
#define ZFCP_CT_REJECT 0x8001
@ -321,107 +268,6 @@ struct zfcp_rc_entry {
*/
#define ZFCP_CT_TIMEOUT (3 * R_A_TOV)
/******************** LOGGING MACROS AND DEFINES *****************************/
/*
* Logging may be applied on certain kinds of driver operations
* independently. Additionally, different log-levels are supported for
* each of these areas.
*/
#define ZFCP_NAME "zfcp"
/* independent log areas */
#define ZFCP_LOG_AREA_OTHER 0
#define ZFCP_LOG_AREA_SCSI 1
#define ZFCP_LOG_AREA_FSF 2
#define ZFCP_LOG_AREA_CONFIG 3
#define ZFCP_LOG_AREA_CIO 4
#define ZFCP_LOG_AREA_QDIO 5
#define ZFCP_LOG_AREA_ERP 6
#define ZFCP_LOG_AREA_FC 7
/* log level values*/
#define ZFCP_LOG_LEVEL_NORMAL 0
#define ZFCP_LOG_LEVEL_INFO 1
#define ZFCP_LOG_LEVEL_DEBUG 2
#define ZFCP_LOG_LEVEL_TRACE 3
/*
* this allows removal of logging code by the preprocessor
* (the most detailed log level still to be compiled in is specified,
* higher log levels are removed)
*/
#define ZFCP_LOG_LEVEL_LIMIT ZFCP_LOG_LEVEL_TRACE
/* get "loglevel" nibble assignment */
#define ZFCP_GET_LOG_VALUE(zfcp_lognibble) \
((atomic_read(&zfcp_data.loglevel) >> (zfcp_lognibble<<2)) & 0xF)
/* set "loglevel" nibble */
#define ZFCP_SET_LOG_NIBBLE(value, zfcp_lognibble) \
(value << (zfcp_lognibble << 2))
/* all log-level defaults are combined to generate initial log-level */
#define ZFCP_LOG_LEVEL_DEFAULTS \
(ZFCP_SET_LOG_NIBBLE(ZFCP_LOG_LEVEL_NORMAL, ZFCP_LOG_AREA_OTHER) | \
ZFCP_SET_LOG_NIBBLE(ZFCP_LOG_LEVEL_NORMAL, ZFCP_LOG_AREA_SCSI) | \
ZFCP_SET_LOG_NIBBLE(ZFCP_LOG_LEVEL_NORMAL, ZFCP_LOG_AREA_FSF) | \
ZFCP_SET_LOG_NIBBLE(ZFCP_LOG_LEVEL_NORMAL, ZFCP_LOG_AREA_CONFIG) | \
ZFCP_SET_LOG_NIBBLE(ZFCP_LOG_LEVEL_NORMAL, ZFCP_LOG_AREA_CIO) | \
ZFCP_SET_LOG_NIBBLE(ZFCP_LOG_LEVEL_NORMAL, ZFCP_LOG_AREA_QDIO) | \
ZFCP_SET_LOG_NIBBLE(ZFCP_LOG_LEVEL_NORMAL, ZFCP_LOG_AREA_ERP) | \
ZFCP_SET_LOG_NIBBLE(ZFCP_LOG_LEVEL_NORMAL, ZFCP_LOG_AREA_FC))
/* check whether we have the right level for logging */
#define ZFCP_LOG_CHECK(level) \
((ZFCP_GET_LOG_VALUE(ZFCP_LOG_AREA)) >= level)
/* logging routine for zfcp */
#define _ZFCP_LOG(fmt, args...) \
printk(KERN_ERR ZFCP_NAME": %s(%d): " fmt, __func__, \
__LINE__ , ##args)
#define ZFCP_LOG(level, fmt, args...) \
do { \
if (ZFCP_LOG_CHECK(level)) \
_ZFCP_LOG(fmt, ##args); \
} while (0)
#if ZFCP_LOG_LEVEL_LIMIT < ZFCP_LOG_LEVEL_NORMAL
# define ZFCP_LOG_NORMAL(fmt, args...) do { } while (0)
#else
# define ZFCP_LOG_NORMAL(fmt, args...) \
do { \
if (ZFCP_LOG_CHECK(ZFCP_LOG_LEVEL_NORMAL)) \
printk(KERN_ERR ZFCP_NAME": " fmt, ##args); \
} while (0)
#endif
#if ZFCP_LOG_LEVEL_LIMIT < ZFCP_LOG_LEVEL_INFO
# define ZFCP_LOG_INFO(fmt, args...) do { } while (0)
#else
# define ZFCP_LOG_INFO(fmt, args...) \
do { \
if (ZFCP_LOG_CHECK(ZFCP_LOG_LEVEL_INFO)) \
printk(KERN_ERR ZFCP_NAME": " fmt, ##args); \
} while (0)
#endif
#if ZFCP_LOG_LEVEL_LIMIT < ZFCP_LOG_LEVEL_DEBUG
# define ZFCP_LOG_DEBUG(fmt, args...) do { } while (0)
#else
# define ZFCP_LOG_DEBUG(fmt, args...) \
ZFCP_LOG(ZFCP_LOG_LEVEL_DEBUG, fmt , ##args)
#endif
#if ZFCP_LOG_LEVEL_LIMIT < ZFCP_LOG_LEVEL_TRACE
# define ZFCP_LOG_TRACE(fmt, args...) do { } while (0)
#else
# define ZFCP_LOG_TRACE(fmt, args...) \
ZFCP_LOG(ZFCP_LOG_LEVEL_TRACE, fmt , ##args)
#endif
/*************** ADAPTER/PORT/UNIT AND FSF_REQ STATUS FLAGS ******************/
/*
@ -441,6 +287,7 @@ do { \
#define ZFCP_STATUS_COMMON_ERP_INUSE 0x01000000
#define ZFCP_STATUS_COMMON_ACCESS_DENIED 0x00800000
#define ZFCP_STATUS_COMMON_ACCESS_BOXED 0x00400000
#define ZFCP_STATUS_COMMON_NOESC 0x00200000
/* adapter status */
#define ZFCP_STATUS_ADAPTER_QDIOUP 0x00000002
@ -496,77 +343,6 @@ do { \
#define ZFCP_STATUS_FSFREQ_RETRY 0x00000800
#define ZFCP_STATUS_FSFREQ_DISMISSED 0x00001000
/*********************** ERROR RECOVERY PROCEDURE DEFINES ********************/
#define ZFCP_MAX_ERPS 3
#define ZFCP_ERP_FSFREQ_TIMEOUT (30 * HZ)
#define ZFCP_ERP_MEMWAIT_TIMEOUT HZ
#define ZFCP_STATUS_ERP_TIMEDOUT 0x10000000
#define ZFCP_STATUS_ERP_CLOSE_ONLY 0x01000000
#define ZFCP_STATUS_ERP_DISMISSING 0x00100000
#define ZFCP_STATUS_ERP_DISMISSED 0x00200000
#define ZFCP_STATUS_ERP_LOWMEM 0x00400000
#define ZFCP_ERP_STEP_UNINITIALIZED 0x00000000
#define ZFCP_ERP_STEP_FSF_XCONFIG 0x00000001
#define ZFCP_ERP_STEP_PHYS_PORT_CLOSING 0x00000010
#define ZFCP_ERP_STEP_PORT_CLOSING 0x00000100
#define ZFCP_ERP_STEP_NAMESERVER_OPEN 0x00000200
#define ZFCP_ERP_STEP_NAMESERVER_LOOKUP 0x00000400
#define ZFCP_ERP_STEP_PORT_OPENING 0x00000800
#define ZFCP_ERP_STEP_UNIT_CLOSING 0x00001000
#define ZFCP_ERP_STEP_UNIT_OPENING 0x00002000
/* Ordered by escalation level (necessary for proper erp-code operation) */
#define ZFCP_ERP_ACTION_REOPEN_ADAPTER 0x4
#define ZFCP_ERP_ACTION_REOPEN_PORT_FORCED 0x3
#define ZFCP_ERP_ACTION_REOPEN_PORT 0x2
#define ZFCP_ERP_ACTION_REOPEN_UNIT 0x1
#define ZFCP_ERP_ACTION_RUNNING 0x1
#define ZFCP_ERP_ACTION_READY 0x2
#define ZFCP_ERP_SUCCEEDED 0x0
#define ZFCP_ERP_FAILED 0x1
#define ZFCP_ERP_CONTINUES 0x2
#define ZFCP_ERP_EXIT 0x3
#define ZFCP_ERP_DISMISSED 0x4
#define ZFCP_ERP_NOMEM 0x5
/******************** CFDC SPECIFIC STUFF *****************************/
/* Firewall data channel sense data record */
struct zfcp_cfdc_sense_data {
u32 signature; /* Request signature */
u32 devno; /* FCP adapter device number */
u32 command; /* Command code */
u32 fsf_status; /* FSF request status and status qualifier */
u8 fsf_status_qual[FSF_STATUS_QUALIFIER_SIZE];
u8 payloads[256]; /* Access conflicts list */
u8 control_file[0]; /* Access control table */
};
#define ZFCP_CFDC_SIGNATURE 0xCFDCACDF
#define ZFCP_CFDC_CMND_DOWNLOAD_NORMAL 0x00010001
#define ZFCP_CFDC_CMND_DOWNLOAD_FORCE 0x00010101
#define ZFCP_CFDC_CMND_FULL_ACCESS 0x00000201
#define ZFCP_CFDC_CMND_RESTRICTED_ACCESS 0x00000401
#define ZFCP_CFDC_CMND_UPLOAD 0x00010002
#define ZFCP_CFDC_DOWNLOAD 0x00000001
#define ZFCP_CFDC_UPLOAD 0x00000002
#define ZFCP_CFDC_WITH_CONTROL_FILE 0x00010000
#define ZFCP_CFDC_DEV_NAME "zfcp_cfdc"
#define ZFCP_CFDC_DEV_MAJOR MISC_MAJOR
#define ZFCP_CFDC_DEV_MINOR MISC_DYNAMIC_MINOR
#define ZFCP_CFDC_MAX_CONTROL_FILE_SIZE 127 * 1024
/************************* STRUCTURE DEFINITIONS *****************************/
struct zfcp_fsf_req;
@ -623,7 +399,6 @@ typedef void (*zfcp_send_ct_handler_t)(unsigned long);
* @resp_count: number of elements in response scatter-gather list
* @handler: handler function (called for response to the request)
* @handler_data: data passed to handler function
* @pool: pointer to memory pool for ct request structure
* @timeout: FSF timeout for this request
* @completion: completion for synchronization purposes
* @status: used to pass error status to calling function
@ -636,7 +411,6 @@ struct zfcp_send_ct {
unsigned int resp_count;
zfcp_send_ct_handler_t handler;
unsigned long handler_data;
mempool_t *pool;
int timeout;
struct completion *completion;
int status;
@ -685,13 +459,13 @@ struct zfcp_send_els {
};
struct zfcp_qdio_queue {
struct qdio_buffer *buffer[QDIO_MAX_BUFFERS_PER_Q]; /* SBALs */
u8 free_index; /* index of next free bfr
struct qdio_buffer *sbal[QDIO_MAX_BUFFERS_PER_Q]; /* SBALs */
u8 first; /* index of next free bfr
in queue (free_count>0) */
atomic_t free_count; /* number of free buffers
atomic_t count; /* number of free buffers
in queue */
rwlock_t queue_lock; /* lock for operations on queue */
int distance_from_int; /* SBALs used since PCI indication
spinlock_t lock; /* lock for operations on queue */
int pci_batch; /* SBALs since PCI indication
was last set */
};
@ -708,6 +482,24 @@ struct zfcp_erp_action {
struct timer_list timer;
};
struct fsf_latency_record {
u32 min;
u32 max;
u64 sum;
};
struct latency_cont {
struct fsf_latency_record channel;
struct fsf_latency_record fabric;
u64 counter;
};
struct zfcp_latencies {
struct latency_cont read;
struct latency_cont write;
struct latency_cont cmd;
spinlock_t lock;
};
struct zfcp_adapter {
struct list_head list; /* list of adapters */
@ -723,24 +515,25 @@ struct zfcp_adapter {
u32 adapter_features; /* FCP channel features */
u32 connection_features; /* host connection features */
u32 hardware_version; /* of FCP channel */
u16 timer_ticks; /* time int for a tick */
struct Scsi_Host *scsi_host; /* Pointer to mid-layer */
struct list_head port_list_head; /* remote port list */
struct list_head port_remove_lh; /* head of ports to be
removed */
u32 ports; /* number of remote ports */
atomic_t reqs_active; /* # active FSF reqs */
unsigned long req_no; /* unique FSF req number */
struct list_head *req_list; /* list of pending reqs */
spinlock_t req_list_lock; /* request list lock */
struct zfcp_qdio_queue request_queue; /* request queue */
struct zfcp_qdio_queue req_q; /* request queue */
u32 fsf_req_seq_no; /* FSF cmnd seq number */
wait_queue_head_t request_wq; /* can be used to wait for
more avaliable SBALs */
struct zfcp_qdio_queue response_queue; /* response queue */
struct zfcp_qdio_queue resp_q; /* response queue */
rwlock_t abort_lock; /* Protects against SCSI
stack abort/command
completion races */
u16 status_read_failed; /* # failed status reads */
atomic_t stat_miss; /* # missing status reads*/
struct work_struct stat_work;
atomic_t status; /* status of this adapter */
struct list_head erp_ready_head; /* error recovery for this
adapter/devices */
@ -774,13 +567,9 @@ struct zfcp_adapter {
struct fc_host_statistics *fc_stats;
struct fsf_qtcb_bottom_port *stats_reset_data;
unsigned long stats_reset;
struct work_struct scan_work;
};
/*
* the struct device sysfs_device must be at the beginning of this structure.
* pointer to struct device is used to free port structure in release function
* of the device. don't change!
*/
struct zfcp_port {
struct device sysfs_device; /* sysfs device */
struct fc_rport *rport; /* rport of fc transport class */
@ -804,10 +593,6 @@ struct zfcp_port {
u32 supported_classes;
};
/* the struct device sysfs_device must be at the beginning of this structure.
* pointer to struct device is used to free unit structure in release function
* of the device. don't change!
*/
struct zfcp_unit {
struct device sysfs_device; /* sysfs device */
struct list_head list; /* list of logical units */
@ -822,6 +607,7 @@ struct zfcp_unit {
struct scsi_device *device; /* scsi device struct pointer */
struct zfcp_erp_action erp_action; /* pending error recovery */
atomic_t erp_counter;
struct zfcp_latencies latencies;
};
/* FSF request */
@ -831,19 +617,19 @@ struct zfcp_fsf_req {
struct zfcp_adapter *adapter; /* adapter request belongs to */
u8 sbal_number; /* nr of SBALs free for use */
u8 sbal_first; /* first SBAL for this request */
u8 sbal_last; /* last possible SBAL for
u8 sbal_last; /* last SBAL for this request */
u8 sbal_limit; /* last possible SBAL for
this reuest */
u8 sbal_curr; /* current SBAL during creation
of request */
u8 sbale_curr; /* current SBALE during creation
of request */
u8 sbal_response; /* SBAL used in interrupt */
wait_queue_head_t completion_wq; /* can be used by a routine
to wait for completion */
volatile u32 status; /* status of this request */
u32 fsf_command; /* FSF Command copy */
struct fsf_qtcb *qtcb; /* address of associated QTCB */
u32 seq_no; /* Sequence number of request */
unsigned long data; /* private data of request */
void *data; /* private data of request */
struct timer_list timer; /* used for erp or scsi er */
struct zfcp_erp_action *erp_action; /* used if this request is
issued on behalf of erp */
@ -851,10 +637,9 @@ struct zfcp_fsf_req {
from emergency pool */
unsigned long long issued; /* request sent time (STCK) */
struct zfcp_unit *unit;
void (*handler)(struct zfcp_fsf_req *);
};
typedef void zfcp_fsf_req_handler_t(struct zfcp_fsf_req*);
/* driver data */
struct zfcp_data {
struct scsi_host_template scsi_host_template;
@ -873,29 +658,11 @@ struct zfcp_data {
char init_busid[BUS_ID_SIZE];
wwn_t init_wwpn;
fcp_lun_t init_fcp_lun;
char *driver_version;
struct kmem_cache *fsf_req_qtcb_cache;
struct kmem_cache *sr_buffer_cache;
struct kmem_cache *gid_pn_cache;
};
/**
* struct zfcp_sg_list - struct describing a scatter-gather list
* @sg: pointer to array of (struct scatterlist)
* @count: number of elements in scatter-gather list
*/
struct zfcp_sg_list {
struct scatterlist *sg;
unsigned int count;
};
/* number of elements for various memory pools */
#define ZFCP_POOL_FSF_REQ_ERP_NR 1
#define ZFCP_POOL_FSF_REQ_SCSI_NR 1
#define ZFCP_POOL_FSF_REQ_ABORT_NR 1
#define ZFCP_POOL_STATUS_READ_NR ZFCP_STATUS_READS_RECOM
#define ZFCP_POOL_DATA_GID_PN_NR 1
/* struct used by memory pools for fsf_requests */
struct zfcp_fsf_req_qtcb {
struct zfcp_fsf_req fsf_req;
@ -905,7 +672,6 @@ struct zfcp_fsf_req_qtcb {
/********************** ZFCP SPECIFIC DEFINES ********************************/
#define ZFCP_REQ_AUTO_CLEANUP 0x00000002
#define ZFCP_WAIT_FOR_SBAL 0x00000004
#define ZFCP_REQ_NO_QTCB 0x00000008
#define ZFCP_SET 0x00000100
@ -916,12 +682,6 @@ struct zfcp_fsf_req_qtcb {
((atomic_read(target) & mask) == mask)
#endif
extern void _zfcp_hex_dump(char *, int);
#define ZFCP_HEX_DUMP(level, addr, count) \
if (ZFCP_LOG_CHECK(level)) { \
_zfcp_hex_dump(addr, count); \
}
#define zfcp_get_busid_by_adapter(adapter) (adapter->ccw_device->dev.bus_id)
#define zfcp_get_busid_by_port(port) (zfcp_get_busid_by_adapter(port->adapter))
#define zfcp_get_busid_by_unit(unit) (zfcp_get_busid_by_port(unit->port))
@ -934,15 +694,6 @@ static inline int zfcp_reqlist_hash(unsigned long req_id)
return req_id % REQUEST_LIST_SIZE;
}
static inline void zfcp_reqlist_add(struct zfcp_adapter *adapter,
struct zfcp_fsf_req *fsf_req)
{
unsigned int idx;
idx = zfcp_reqlist_hash(fsf_req->req_id);
list_add_tail(&fsf_req->list, &adapter->req_list[idx]);
}
static inline void zfcp_reqlist_remove(struct zfcp_adapter *adapter,
struct zfcp_fsf_req *fsf_req)
{

File diff suppressed because it is too large Load Diff

View File

@ -1,22 +1,9 @@
/*
* This file is part of the zfcp device driver for
* FCP adapters for IBM System z9 and zSeries.
* zfcp device driver
*
* (C) Copyright IBM Corp. 2002, 2006
* External function declarations.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* Copyright IBM Corporation 2002, 2008
*/
#ifndef ZFCP_EXT_H
@ -24,172 +11,51 @@
#include "zfcp_def.h"
extern struct zfcp_data zfcp_data;
/******************************** SYSFS *************************************/
extern struct attribute_group *zfcp_driver_attr_groups[];
extern int zfcp_sysfs_adapter_create_files(struct device *);
extern void zfcp_sysfs_adapter_remove_files(struct device *);
extern int zfcp_sysfs_port_create_files(struct device *, u32);
extern void zfcp_sysfs_port_remove_files(struct device *, u32);
extern int zfcp_sysfs_unit_create_files(struct device *);
extern void zfcp_sysfs_unit_remove_files(struct device *);
extern void zfcp_sysfs_port_release(struct device *);
extern void zfcp_sysfs_unit_release(struct device *);
/**************************** CONFIGURATION *********************************/
extern struct zfcp_unit *zfcp_get_unit_by_lun(struct zfcp_port *, fcp_lun_t);
extern struct zfcp_port *zfcp_get_port_by_wwpn(struct zfcp_adapter *, wwn_t);
extern struct zfcp_port *zfcp_get_port_by_did(struct zfcp_adapter *, u32);
struct zfcp_adapter *zfcp_get_adapter_by_busid(char *);
extern struct zfcp_adapter *zfcp_adapter_enqueue(struct ccw_device *);
extern int zfcp_adapter_debug_register(struct zfcp_adapter *);
extern void zfcp_adapter_dequeue(struct zfcp_adapter *);
extern void zfcp_adapter_debug_unregister(struct zfcp_adapter *);
extern struct zfcp_port *zfcp_port_enqueue(struct zfcp_adapter *, wwn_t,
u32, u32);
extern void zfcp_port_dequeue(struct zfcp_port *);
/* zfcp_aux.c */
extern struct zfcp_unit *zfcp_get_unit_by_lun(struct zfcp_port *,
fcp_lun_t);
extern struct zfcp_port *zfcp_get_port_by_wwpn(struct zfcp_adapter *,
wwn_t);
extern int zfcp_adapter_enqueue(struct ccw_device *);
extern void zfcp_adapter_dequeue(struct zfcp_adapter *);
extern struct zfcp_port *zfcp_port_enqueue(struct zfcp_adapter *, wwn_t, u32,
u32);
extern void zfcp_port_dequeue(struct zfcp_port *);
extern struct zfcp_unit *zfcp_unit_enqueue(struct zfcp_port *, fcp_lun_t);
extern void zfcp_unit_dequeue(struct zfcp_unit *);
extern void zfcp_unit_dequeue(struct zfcp_unit *);
extern int zfcp_reqlist_isempty(struct zfcp_adapter *);
extern void zfcp_sg_free_table(struct scatterlist *, int);
extern int zfcp_sg_setup_table(struct scatterlist *, int);
/******************************* S/390 IO ************************************/
extern int zfcp_ccw_register(void);
/* zfcp_ccw.c */
extern int zfcp_ccw_register(void);
extern void zfcp_qdio_zero_sbals(struct qdio_buffer **, int, int);
extern int zfcp_qdio_allocate(struct zfcp_adapter *);
extern int zfcp_qdio_allocate_queues(struct zfcp_adapter *);
extern void zfcp_qdio_free_queues(struct zfcp_adapter *);
extern int zfcp_qdio_determine_pci(struct zfcp_qdio_queue *,
struct zfcp_fsf_req *);
/* zfcp_cfdc.c */
extern struct miscdevice zfcp_cfdc_misc;
extern volatile struct qdio_buffer_element *zfcp_qdio_sbale_req
(struct zfcp_fsf_req *, int, int);
extern volatile struct qdio_buffer_element *zfcp_qdio_sbale_curr
(struct zfcp_fsf_req *);
extern int zfcp_qdio_sbals_from_sg
(struct zfcp_fsf_req *, unsigned long, struct scatterlist *, int, int);
extern int zfcp_qdio_sbals_from_scsicmnd
(struct zfcp_fsf_req *, unsigned long, struct scsi_cmnd *);
/******************************** FSF ****************************************/
extern int zfcp_fsf_open_port(struct zfcp_erp_action *);
extern int zfcp_fsf_close_port(struct zfcp_erp_action *);
extern int zfcp_fsf_close_physical_port(struct zfcp_erp_action *);
extern int zfcp_fsf_open_unit(struct zfcp_erp_action *);
extern int zfcp_fsf_close_unit(struct zfcp_erp_action *);
extern int zfcp_fsf_exchange_config_data(struct zfcp_erp_action *);
extern int zfcp_fsf_exchange_config_data_sync(struct zfcp_adapter *,
struct fsf_qtcb_bottom_config *);
extern int zfcp_fsf_exchange_port_data(struct zfcp_erp_action *);
extern int zfcp_fsf_exchange_port_data_sync(struct zfcp_adapter *,
struct fsf_qtcb_bottom_port *);
extern int zfcp_fsf_control_file(struct zfcp_adapter *, struct zfcp_fsf_req **,
u32, u32, struct zfcp_sg_list *);
extern void zfcp_fsf_start_timer(struct zfcp_fsf_req *, unsigned long);
extern void zfcp_erp_start_timer(struct zfcp_fsf_req *);
extern void zfcp_fsf_req_dismiss_all(struct zfcp_adapter *);
extern int zfcp_fsf_status_read(struct zfcp_adapter *, int);
extern int zfcp_fsf_req_create(struct zfcp_adapter *, u32, int, mempool_t *,
unsigned long *, struct zfcp_fsf_req **);
extern int zfcp_fsf_send_ct(struct zfcp_send_ct *, mempool_t *,
struct zfcp_erp_action *);
extern int zfcp_fsf_send_els(struct zfcp_send_els *);
extern int zfcp_fsf_send_fcp_command_task(struct zfcp_adapter *,
struct zfcp_unit *,
struct scsi_cmnd *, int, int);
extern int zfcp_fsf_req_complete(struct zfcp_fsf_req *);
extern void zfcp_fsf_incoming_els(struct zfcp_fsf_req *);
extern void zfcp_fsf_req_free(struct zfcp_fsf_req *);
extern struct zfcp_fsf_req *zfcp_fsf_send_fcp_command_task_management(
struct zfcp_adapter *, struct zfcp_unit *, u8, int);
extern struct zfcp_fsf_req *zfcp_fsf_abort_fcp_command(
unsigned long, struct zfcp_adapter *, struct zfcp_unit *, int);
/******************************* FC/FCP **************************************/
extern int zfcp_nameserver_enqueue(struct zfcp_adapter *);
extern int zfcp_ns_gid_pn_request(struct zfcp_erp_action *);
extern int zfcp_check_ct_response(struct ct_hdr *);
extern int zfcp_handle_els_rjt(u32, struct zfcp_ls_rjt_par *);
extern void zfcp_plogi_evaluate(struct zfcp_port *, struct fsf_plogi *);
/******************************* SCSI ****************************************/
extern int zfcp_adapter_scsi_register(struct zfcp_adapter *);
extern void zfcp_adapter_scsi_unregister(struct zfcp_adapter *);
extern void zfcp_set_fcp_dl(struct fcp_cmnd_iu *, fcp_dl_t);
extern char *zfcp_get_fcp_rsp_info_ptr(struct fcp_rsp_iu *);
extern void set_host_byte(int *, char);
extern void set_driver_byte(int *, char);
extern char *zfcp_get_fcp_sns_info_ptr(struct fcp_rsp_iu *);
extern fcp_dl_t zfcp_get_fcp_dl(struct fcp_cmnd_iu *);
extern int zfcp_scsi_command_async(struct zfcp_adapter *,struct zfcp_unit *,
struct scsi_cmnd *, int);
extern int zfcp_scsi_command_sync(struct zfcp_unit *, struct scsi_cmnd *, int);
extern struct fc_function_template zfcp_transport_functions;
/******************************** ERP ****************************************/
extern void zfcp_erp_modify_adapter_status(struct zfcp_adapter *, u8, void *,
u32, int);
extern int zfcp_erp_adapter_reopen(struct zfcp_adapter *, int, u8, void *);
extern int zfcp_erp_adapter_shutdown(struct zfcp_adapter *, int, u8, void *);
extern void zfcp_erp_adapter_failed(struct zfcp_adapter *, u8, void *);
extern void zfcp_erp_modify_port_status(struct zfcp_port *, u8, void *, u32,
int);
extern int zfcp_erp_port_reopen(struct zfcp_port *, int, u8, void *);
extern int zfcp_erp_port_shutdown(struct zfcp_port *, int, u8, void *);
extern int zfcp_erp_port_forced_reopen(struct zfcp_port *, int, u8, void *);
extern void zfcp_erp_port_failed(struct zfcp_port *, u8, void *);
extern int zfcp_erp_port_reopen_all(struct zfcp_adapter *, int, u8, void *);
extern void zfcp_erp_modify_unit_status(struct zfcp_unit *, u8, void *, u32,
int);
extern int zfcp_erp_unit_reopen(struct zfcp_unit *, int, u8, void *);
extern int zfcp_erp_unit_shutdown(struct zfcp_unit *, int, u8, void *);
extern void zfcp_erp_unit_failed(struct zfcp_unit *, u8, void *);
extern int zfcp_erp_thread_setup(struct zfcp_adapter *);
extern int zfcp_erp_thread_kill(struct zfcp_adapter *);
extern int zfcp_erp_wait(struct zfcp_adapter *);
extern void zfcp_erp_async_handler(struct zfcp_erp_action *, unsigned long);
extern int zfcp_test_link(struct zfcp_port *);
extern void zfcp_erp_port_boxed(struct zfcp_port *, u8 id, void *ref);
extern void zfcp_erp_unit_boxed(struct zfcp_unit *, u8 id, void *ref);
extern void zfcp_erp_port_access_denied(struct zfcp_port *, u8 id, void *ref);
extern void zfcp_erp_unit_access_denied(struct zfcp_unit *, u8 id, void *ref);
extern void zfcp_erp_adapter_access_changed(struct zfcp_adapter *, u8, void *);
extern void zfcp_erp_port_access_changed(struct zfcp_port *, u8, void *);
extern void zfcp_erp_unit_access_changed(struct zfcp_unit *, u8, void *);
/******************************** AUX ****************************************/
extern void zfcp_rec_dbf_event_thread(u8 id, struct zfcp_adapter *adapter,
int lock);
extern void zfcp_rec_dbf_event_adapter(u8 id, void *ref, struct zfcp_adapter *);
extern void zfcp_rec_dbf_event_port(u8 id, void *ref, struct zfcp_port *port);
extern void zfcp_rec_dbf_event_unit(u8 id, void *ref, struct zfcp_unit *unit);
extern void zfcp_rec_dbf_event_trigger(u8 id, void *ref, u8 want, u8 need,
void *action, struct zfcp_adapter *,
/* zfcp_dbf.c */
extern int zfcp_adapter_debug_register(struct zfcp_adapter *);
extern void zfcp_adapter_debug_unregister(struct zfcp_adapter *);
extern void zfcp_rec_dbf_event_thread(u8, struct zfcp_adapter *);
extern void zfcp_rec_dbf_event_thread_lock(u8, struct zfcp_adapter *);
extern void zfcp_rec_dbf_event_adapter(u8, void *, struct zfcp_adapter *);
extern void zfcp_rec_dbf_event_port(u8, void *, struct zfcp_port *);
extern void zfcp_rec_dbf_event_unit(u8, void *, struct zfcp_unit *);
extern void zfcp_rec_dbf_event_trigger(u8, void *, u8, u8, void *,
struct zfcp_adapter *,
struct zfcp_port *, struct zfcp_unit *);
extern void zfcp_rec_dbf_event_action(u8 id, struct zfcp_erp_action *);
extern void zfcp_rec_dbf_event_action(u8, struct zfcp_erp_action *);
extern void zfcp_hba_dbf_event_fsf_response(struct zfcp_fsf_req *);
extern void zfcp_hba_dbf_event_fsf_unsol(const char *, struct zfcp_adapter *,
struct fsf_status_read_buffer *);
extern void zfcp_hba_dbf_event_qdio(struct zfcp_adapter *,
unsigned int, unsigned int, unsigned int,
int, int);
extern void zfcp_san_dbf_event_ct_request(struct zfcp_fsf_req *);
extern void zfcp_san_dbf_event_ct_response(struct zfcp_fsf_req *);
extern void zfcp_san_dbf_event_els_request(struct zfcp_fsf_req *);
extern void zfcp_san_dbf_event_els_response(struct zfcp_fsf_req *);
extern void zfcp_san_dbf_event_incoming_els(struct zfcp_fsf_req *);
extern void zfcp_scsi_dbf_event_result(const char *, int, struct zfcp_adapter *,
struct scsi_cmnd *,
struct zfcp_fsf_req *);
@ -198,6 +64,101 @@ extern void zfcp_scsi_dbf_event_abort(const char *, struct zfcp_adapter *,
unsigned long);
extern void zfcp_scsi_dbf_event_devreset(const char *, u8, struct zfcp_unit *,
struct scsi_cmnd *);
extern int zfcp_reqlist_isempty(struct zfcp_adapter *);
/* zfcp_erp.c */
extern void zfcp_erp_modify_adapter_status(struct zfcp_adapter *, u8, void *,
u32, int);
extern void zfcp_erp_adapter_reopen(struct zfcp_adapter *, int, u8, void *);
extern void zfcp_erp_adapter_shutdown(struct zfcp_adapter *, int, u8, void *);
extern void zfcp_erp_adapter_failed(struct zfcp_adapter *, u8, void *);
extern void zfcp_erp_modify_port_status(struct zfcp_port *, u8, void *, u32,
int);
extern int zfcp_erp_port_reopen(struct zfcp_port *, int, u8, void *);
extern void zfcp_erp_port_shutdown(struct zfcp_port *, int, u8, void *);
extern void zfcp_erp_port_forced_reopen(struct zfcp_port *, int, u8, void *);
extern void zfcp_erp_port_failed(struct zfcp_port *, u8, void *);
extern void zfcp_erp_modify_unit_status(struct zfcp_unit *, u8, void *, u32,
int);
extern void zfcp_erp_unit_reopen(struct zfcp_unit *, int, u8, void *);
extern void zfcp_erp_unit_shutdown(struct zfcp_unit *, int, u8, void *);
extern void zfcp_erp_unit_failed(struct zfcp_unit *, u8, void *);
extern int zfcp_erp_thread_setup(struct zfcp_adapter *);
extern void zfcp_erp_thread_kill(struct zfcp_adapter *);
extern void zfcp_erp_wait(struct zfcp_adapter *);
extern void zfcp_erp_notify(struct zfcp_erp_action *, unsigned long);
extern void zfcp_erp_port_boxed(struct zfcp_port *, u8, void *);
extern void zfcp_erp_unit_boxed(struct zfcp_unit *, u8, void *);
extern void zfcp_erp_port_access_denied(struct zfcp_port *, u8, void *);
extern void zfcp_erp_unit_access_denied(struct zfcp_unit *, u8, void *);
extern void zfcp_erp_adapter_access_changed(struct zfcp_adapter *, u8, void *);
extern void zfcp_erp_timeout_handler(unsigned long);
/* zfcp_fc.c */
extern int zfcp_scan_ports(struct zfcp_adapter *);
extern void _zfcp_scan_ports_later(struct work_struct *);
extern void zfcp_fc_incoming_els(struct zfcp_fsf_req *);
extern int zfcp_fc_ns_gid_pn_request(struct zfcp_erp_action *);
extern void zfcp_fc_plogi_evaluate(struct zfcp_port *, struct fsf_plogi *);
extern void zfcp_test_link(struct zfcp_port *);
/* zfcp_fsf.c */
extern int zfcp_fsf_open_port(struct zfcp_erp_action *);
extern int zfcp_fsf_close_port(struct zfcp_erp_action *);
extern int zfcp_fsf_close_physical_port(struct zfcp_erp_action *);
extern int zfcp_fsf_open_unit(struct zfcp_erp_action *);
extern int zfcp_fsf_close_unit(struct zfcp_erp_action *);
extern int zfcp_fsf_exchange_config_data(struct zfcp_erp_action *);
extern int zfcp_fsf_exchange_config_data_sync(struct zfcp_adapter *,
struct fsf_qtcb_bottom_config *);
extern int zfcp_fsf_exchange_port_data(struct zfcp_erp_action *);
extern int zfcp_fsf_exchange_port_data_sync(struct zfcp_adapter *,
struct fsf_qtcb_bottom_port *);
extern struct zfcp_fsf_req *zfcp_fsf_control_file(struct zfcp_adapter *,
struct zfcp_fsf_cfdc *);
extern void zfcp_fsf_req_dismiss_all(struct zfcp_adapter *);
extern int zfcp_fsf_status_read(struct zfcp_adapter *);
extern int zfcp_status_read_refill(struct zfcp_adapter *adapter);
extern int zfcp_fsf_send_ct(struct zfcp_send_ct *, mempool_t *,
struct zfcp_erp_action *);
extern int zfcp_fsf_send_els(struct zfcp_send_els *);
extern int zfcp_fsf_send_fcp_command_task(struct zfcp_adapter *,
struct zfcp_unit *,
struct scsi_cmnd *, int, int);
extern void zfcp_fsf_req_complete(struct zfcp_fsf_req *);
extern void zfcp_fsf_req_free(struct zfcp_fsf_req *);
extern struct zfcp_fsf_req *zfcp_fsf_send_fcp_ctm(struct zfcp_adapter *,
struct zfcp_unit *, u8, int);
extern struct zfcp_fsf_req *zfcp_fsf_abort_fcp_command(unsigned long,
struct zfcp_adapter *,
struct zfcp_unit *, int);
/* zfcp_qdio.c */
extern int zfcp_qdio_allocate(struct zfcp_adapter *);
extern void zfcp_qdio_free(struct zfcp_adapter *);
extern int zfcp_qdio_send(struct zfcp_fsf_req *);
extern volatile struct qdio_buffer_element *zfcp_qdio_sbale_req(
struct zfcp_fsf_req *);
extern volatile struct qdio_buffer_element *zfcp_qdio_sbale_curr(
struct zfcp_fsf_req *);
extern int zfcp_qdio_sbals_from_sg(struct zfcp_fsf_req *, unsigned long,
struct scatterlist *, int);
extern int zfcp_qdio_open(struct zfcp_adapter *);
extern void zfcp_qdio_close(struct zfcp_adapter *);
/* zfcp_scsi.c */
extern struct zfcp_data zfcp_data;
extern int zfcp_adapter_scsi_register(struct zfcp_adapter *);
extern void zfcp_adapter_scsi_unregister(struct zfcp_adapter *);
extern void zfcp_set_fcp_dl(struct fcp_cmnd_iu *, fcp_dl_t);
extern char *zfcp_get_fcp_sns_info_ptr(struct fcp_rsp_iu *);
extern struct fc_function_template zfcp_transport_functions;
/* zfcp_sysfs.c */
extern struct attribute_group zfcp_sysfs_unit_attrs;
extern struct attribute_group zfcp_sysfs_adapter_attrs;
extern struct attribute_group zfcp_sysfs_ns_port_attrs;
extern struct attribute_group zfcp_sysfs_port_attrs;
extern struct device_attribute *zfcp_sysfs_sdev_attrs[];
extern struct device_attribute *zfcp_sysfs_shost_attrs[];
#endif /* ZFCP_EXT_H */

567
drivers/s390/scsi/zfcp_fc.c Normal file
View File

@ -0,0 +1,567 @@
/*
* zfcp device driver
*
* Fibre Channel related functions for the zfcp device driver.
*
* Copyright IBM Corporation 2008
*/
#include "zfcp_ext.h"
struct ct_iu_gpn_ft_req {
struct ct_hdr header;
u8 flags;
u8 domain_id_scope;
u8 area_id_scope;
u8 fc4_type;
} __attribute__ ((packed));
struct gpn_ft_resp_acc {
u8 control;
u8 port_id[3];
u8 reserved[4];
u64 wwpn;
} __attribute__ ((packed));
#define ZFCP_GPN_FT_ENTRIES ((PAGE_SIZE - sizeof(struct ct_hdr)) \
/ sizeof(struct gpn_ft_resp_acc))
#define ZFCP_GPN_FT_BUFFERS 4
#define ZFCP_GPN_FT_MAX_ENTRIES ZFCP_GPN_FT_BUFFERS * (ZFCP_GPN_FT_ENTRIES + 1)
struct ct_iu_gpn_ft_resp {
struct ct_hdr header;
struct gpn_ft_resp_acc accept[ZFCP_GPN_FT_ENTRIES];
} __attribute__ ((packed));
struct zfcp_gpn_ft {
struct zfcp_send_ct ct;
struct scatterlist sg_req;
struct scatterlist sg_resp[ZFCP_GPN_FT_BUFFERS];
};
static struct zfcp_port *zfcp_get_port_by_did(struct zfcp_adapter *adapter,
u32 d_id)
{
struct zfcp_port *port;
list_for_each_entry(port, &adapter->port_list_head, list)
if ((port->d_id == d_id) &&
!atomic_test_mask(ZFCP_STATUS_COMMON_REMOVE, &port->status))
return port;
return NULL;
}
static void _zfcp_fc_incoming_rscn(struct zfcp_fsf_req *fsf_req, u32 range,
struct fcp_rscn_element *elem)
{
unsigned long flags;
struct zfcp_port *port;
read_lock_irqsave(&zfcp_data.config_lock, flags);
list_for_each_entry(port, &fsf_req->adapter->port_list_head, list) {
if (atomic_test_mask(ZFCP_STATUS_PORT_WKA, &port->status))
continue;
/* FIXME: ZFCP_STATUS_PORT_DID_DID check is racy */
if (!atomic_test_mask(ZFCP_STATUS_PORT_DID_DID, &port->status))
/* Try to connect to unused ports anyway. */
zfcp_erp_port_reopen(port,
ZFCP_STATUS_COMMON_ERP_FAILED,
82, fsf_req);
else if ((port->d_id & range) == (elem->nport_did & range))
/* Check connection status for connected ports */
zfcp_test_link(port);
}
read_unlock_irqrestore(&zfcp_data.config_lock, flags);
}
static void zfcp_fc_incoming_rscn(struct zfcp_fsf_req *fsf_req)
{
struct fsf_status_read_buffer *status_buffer = (void *)fsf_req->data;
struct fcp_rscn_head *fcp_rscn_head;
struct fcp_rscn_element *fcp_rscn_element;
u16 i;
u16 no_entries;
u32 range_mask;
fcp_rscn_head = (struct fcp_rscn_head *) status_buffer->payload.data;
fcp_rscn_element = (struct fcp_rscn_element *) fcp_rscn_head;
/* see FC-FS */
no_entries = fcp_rscn_head->payload_len /
sizeof(struct fcp_rscn_element);
for (i = 1; i < no_entries; i++) {
/* skip head and start with 1st element */
fcp_rscn_element++;
switch (fcp_rscn_element->addr_format) {
case ZFCP_PORT_ADDRESS:
range_mask = ZFCP_PORTS_RANGE_PORT;
break;
case ZFCP_AREA_ADDRESS:
range_mask = ZFCP_PORTS_RANGE_AREA;
break;
case ZFCP_DOMAIN_ADDRESS:
range_mask = ZFCP_PORTS_RANGE_DOMAIN;
break;
case ZFCP_FABRIC_ADDRESS:
range_mask = ZFCP_PORTS_RANGE_FABRIC;
break;
default:
continue;
}
_zfcp_fc_incoming_rscn(fsf_req, range_mask, fcp_rscn_element);
}
schedule_work(&fsf_req->adapter->scan_work);
}
static void zfcp_fc_incoming_wwpn(struct zfcp_fsf_req *req, wwn_t wwpn)
{
struct zfcp_adapter *adapter = req->adapter;
struct zfcp_port *port;
unsigned long flags;
read_lock_irqsave(&zfcp_data.config_lock, flags);
list_for_each_entry(port, &adapter->port_list_head, list)
if (port->wwpn == wwpn)
break;
read_unlock_irqrestore(&zfcp_data.config_lock, flags);
if (port && (port->wwpn == wwpn))
zfcp_erp_port_forced_reopen(port, 0, 83, req);
}
static void zfcp_fc_incoming_plogi(struct zfcp_fsf_req *req)
{
struct fsf_status_read_buffer *status_buffer =
(struct fsf_status_read_buffer *)req->data;
struct fsf_plogi *els_plogi =
(struct fsf_plogi *) status_buffer->payload.data;
zfcp_fc_incoming_wwpn(req, els_plogi->serv_param.wwpn);
}
static void zfcp_fc_incoming_logo(struct zfcp_fsf_req *req)
{
struct fsf_status_read_buffer *status_buffer =
(struct fsf_status_read_buffer *)req->data;
struct fcp_logo *els_logo =
(struct fcp_logo *) status_buffer->payload.data;
zfcp_fc_incoming_wwpn(req, els_logo->nport_wwpn);
}
/**
* zfcp_fc_incoming_els - handle incoming ELS
* @fsf_req - request which contains incoming ELS
*/
void zfcp_fc_incoming_els(struct zfcp_fsf_req *fsf_req)
{
struct fsf_status_read_buffer *status_buffer =
(struct fsf_status_read_buffer *) fsf_req->data;
unsigned int els_type = status_buffer->payload.data[0];
zfcp_san_dbf_event_incoming_els(fsf_req);
if (els_type == LS_PLOGI)
zfcp_fc_incoming_plogi(fsf_req);
else if (els_type == LS_LOGO)
zfcp_fc_incoming_logo(fsf_req);
else if (els_type == LS_RSCN)
zfcp_fc_incoming_rscn(fsf_req);
}
static void zfcp_ns_gid_pn_handler(unsigned long data)
{
struct zfcp_gid_pn_data *gid_pn = (struct zfcp_gid_pn_data *) data;
struct zfcp_send_ct *ct = &gid_pn->ct;
struct ct_iu_gid_pn_req *ct_iu_req = sg_virt(ct->req);
struct ct_iu_gid_pn_resp *ct_iu_resp = sg_virt(ct->resp);
struct zfcp_port *port = gid_pn->port;
if (ct->status)
goto out;
if (ct_iu_resp->header.cmd_rsp_code != ZFCP_CT_ACCEPT) {
atomic_set_mask(ZFCP_STATUS_PORT_INVALID_WWPN, &port->status);
goto out;
}
/* paranoia */
if (ct_iu_req->wwpn != port->wwpn)
goto out;
/* looks like a valid d_id */
port->d_id = ct_iu_resp->d_id & ZFCP_DID_MASK;
atomic_set_mask(ZFCP_STATUS_PORT_DID_DID, &port->status);
out:
mempool_free(gid_pn, port->adapter->pool.data_gid_pn);
}
/**
* zfcp_fc_ns_gid_pn_request - initiate GID_PN nameserver request
* @erp_action: pointer to zfcp_erp_action where GID_PN request is needed
* return: -ENOMEM on error, 0 otherwise
*/
int zfcp_fc_ns_gid_pn_request(struct zfcp_erp_action *erp_action)
{
int ret;
struct zfcp_gid_pn_data *gid_pn;
struct zfcp_adapter *adapter = erp_action->adapter;
gid_pn = mempool_alloc(adapter->pool.data_gid_pn, GFP_ATOMIC);
if (!gid_pn)
return -ENOMEM;
memset(gid_pn, 0, sizeof(*gid_pn));
/* setup parameters for send generic command */
gid_pn->port = erp_action->port;
gid_pn->ct.port = adapter->nameserver_port;
gid_pn->ct.handler = zfcp_ns_gid_pn_handler;
gid_pn->ct.handler_data = (unsigned long) gid_pn;
gid_pn->ct.timeout = ZFCP_NS_GID_PN_TIMEOUT;
gid_pn->ct.req = &gid_pn->req;
gid_pn->ct.resp = &gid_pn->resp;
gid_pn->ct.req_count = 1;
gid_pn->ct.resp_count = 1;
sg_init_one(&gid_pn->req, &gid_pn->ct_iu_req,
sizeof(struct ct_iu_gid_pn_req));
sg_init_one(&gid_pn->resp, &gid_pn->ct_iu_resp,
sizeof(struct ct_iu_gid_pn_resp));
/* setup nameserver request */
gid_pn->ct_iu_req.header.revision = ZFCP_CT_REVISION;
gid_pn->ct_iu_req.header.gs_type = ZFCP_CT_DIRECTORY_SERVICE;
gid_pn->ct_iu_req.header.gs_subtype = ZFCP_CT_NAME_SERVER;
gid_pn->ct_iu_req.header.options = ZFCP_CT_SYNCHRONOUS;
gid_pn->ct_iu_req.header.cmd_rsp_code = ZFCP_CT_GID_PN;
gid_pn->ct_iu_req.header.max_res_size = ZFCP_CT_MAX_SIZE;
gid_pn->ct_iu_req.wwpn = erp_action->port->wwpn;
ret = zfcp_fsf_send_ct(&gid_pn->ct, adapter->pool.fsf_req_erp,
erp_action);
if (ret)
mempool_free(gid_pn, adapter->pool.data_gid_pn);
return ret;
}
/**
* zfcp_fc_plogi_evaluate - evaluate PLOGI playload
* @port: zfcp_port structure
* @plogi: plogi payload
*
* Evaluate PLOGI playload and copy important fields into zfcp_port structure
*/
void zfcp_fc_plogi_evaluate(struct zfcp_port *port, struct fsf_plogi *plogi)
{
port->maxframe_size = plogi->serv_param.common_serv_param[7] |
((plogi->serv_param.common_serv_param[6] & 0x0F) << 8);
if (plogi->serv_param.class1_serv_param[0] & 0x80)
port->supported_classes |= FC_COS_CLASS1;
if (plogi->serv_param.class2_serv_param[0] & 0x80)
port->supported_classes |= FC_COS_CLASS2;
if (plogi->serv_param.class3_serv_param[0] & 0x80)
port->supported_classes |= FC_COS_CLASS3;
if (plogi->serv_param.class4_serv_param[0] & 0x80)
port->supported_classes |= FC_COS_CLASS4;
}
struct zfcp_els_adisc {
struct zfcp_send_els els;
struct scatterlist req;
struct scatterlist resp;
struct zfcp_ls_adisc ls_adisc;
struct zfcp_ls_adisc_acc ls_adisc_acc;
};
static void zfcp_fc_adisc_handler(unsigned long data)
{
struct zfcp_els_adisc *adisc = (struct zfcp_els_adisc *) data;
struct zfcp_port *port = adisc->els.port;
struct zfcp_ls_adisc_acc *ls_adisc = &adisc->ls_adisc_acc;
if (adisc->els.status) {
/* request rejected or timed out */
zfcp_erp_port_forced_reopen(port, 0, 63, NULL);
goto out;
}
if (!port->wwnn)
port->wwnn = ls_adisc->wwnn;
if (port->wwpn != ls_adisc->wwpn)
zfcp_erp_port_reopen(port, 0, 64, NULL);
out:
zfcp_port_put(port);
kfree(adisc);
}
static int zfcp_fc_adisc(struct zfcp_port *port)
{
struct zfcp_els_adisc *adisc;
struct zfcp_adapter *adapter = port->adapter;
adisc = kzalloc(sizeof(struct zfcp_els_adisc), GFP_ATOMIC);
if (!adisc)
return -ENOMEM;
adisc->els.req = &adisc->req;
adisc->els.resp = &adisc->resp;
sg_init_one(adisc->els.req, &adisc->ls_adisc,
sizeof(struct zfcp_ls_adisc));
sg_init_one(adisc->els.resp, &adisc->ls_adisc_acc,
sizeof(struct zfcp_ls_adisc_acc));
adisc->els.req_count = 1;
adisc->els.resp_count = 1;
adisc->els.adapter = adapter;
adisc->els.port = port;
adisc->els.d_id = port->d_id;
adisc->els.handler = zfcp_fc_adisc_handler;
adisc->els.handler_data = (unsigned long) adisc;
adisc->els.ls_code = adisc->ls_adisc.code = ZFCP_LS_ADISC;
/* acc. to FC-FS, hard_nport_id in ADISC should not be set for ports
without FC-AL-2 capability, so we don't set it */
adisc->ls_adisc.wwpn = fc_host_port_name(adapter->scsi_host);
adisc->ls_adisc.wwnn = fc_host_node_name(adapter->scsi_host);
adisc->ls_adisc.nport_id = fc_host_port_id(adapter->scsi_host);
return zfcp_fsf_send_els(&adisc->els);
}
/**
* zfcp_test_link - lightweight link test procedure
* @port: port to be tested
*
* Test status of a link to a remote port using the ELS command ADISC.
* If there is a problem with the remote port, error recovery steps
* will be triggered.
*/
void zfcp_test_link(struct zfcp_port *port)
{
int retval;
zfcp_port_get(port);
retval = zfcp_fc_adisc(port);
if (retval == 0 || retval == -EBUSY)
return;
/* send of ADISC was not possible */
zfcp_port_put(port);
zfcp_erp_port_forced_reopen(port, 0, 65, NULL);
}
static int zfcp_scan_get_nameserver(struct zfcp_adapter *adapter)
{
int ret;
if (!adapter->nameserver_port)
return -EINTR;
if (!atomic_test_mask(ZFCP_STATUS_COMMON_UNBLOCKED,
&adapter->nameserver_port->status)) {
ret = zfcp_erp_port_reopen(adapter->nameserver_port, 0, 148,
NULL);
if (ret)
return ret;
zfcp_erp_wait(adapter);
zfcp_port_put(adapter->nameserver_port);
}
return !atomic_test_mask(ZFCP_STATUS_COMMON_UNBLOCKED,
&adapter->nameserver_port->status);
}
static void zfcp_gpn_ft_handler(unsigned long _done)
{
complete((struct completion *)_done);
}
static void zfcp_free_sg_env(struct zfcp_gpn_ft *gpn_ft)
{
struct scatterlist *sg = &gpn_ft->sg_req;
kfree(sg_virt(sg)); /* free request buffer */
zfcp_sg_free_table(gpn_ft->sg_resp, ZFCP_GPN_FT_BUFFERS);
kfree(gpn_ft);
}
static struct zfcp_gpn_ft *zfcp_alloc_sg_env(void)
{
struct zfcp_gpn_ft *gpn_ft;
struct ct_iu_gpn_ft_req *req;
gpn_ft = kzalloc(sizeof(*gpn_ft), GFP_KERNEL);
if (!gpn_ft)
return NULL;
req = kzalloc(sizeof(struct ct_iu_gpn_ft_req), GFP_KERNEL);
if (!req) {
kfree(gpn_ft);
gpn_ft = NULL;
goto out;
}
sg_init_one(&gpn_ft->sg_req, req, sizeof(*req));
if (zfcp_sg_setup_table(gpn_ft->sg_resp, ZFCP_GPN_FT_BUFFERS)) {
zfcp_free_sg_env(gpn_ft);
gpn_ft = NULL;
}
out:
return gpn_ft;
}
static int zfcp_scan_issue_gpn_ft(struct zfcp_gpn_ft *gpn_ft,
struct zfcp_adapter *adapter)
{
struct zfcp_send_ct *ct = &gpn_ft->ct;
struct ct_iu_gpn_ft_req *req = sg_virt(&gpn_ft->sg_req);
struct completion done;
int ret;
/* prepare CT IU for GPN_FT */
req->header.revision = ZFCP_CT_REVISION;
req->header.gs_type = ZFCP_CT_DIRECTORY_SERVICE;
req->header.gs_subtype = ZFCP_CT_NAME_SERVER;
req->header.options = ZFCP_CT_SYNCHRONOUS;
req->header.cmd_rsp_code = ZFCP_CT_GPN_FT;
req->header.max_res_size = (sizeof(struct gpn_ft_resp_acc) *
(ZFCP_GPN_FT_MAX_ENTRIES - 1)) >> 2;
req->flags = 0;
req->domain_id_scope = 0;
req->area_id_scope = 0;
req->fc4_type = ZFCP_CT_SCSI_FCP;
/* prepare zfcp_send_ct */
ct->port = adapter->nameserver_port;
ct->handler = zfcp_gpn_ft_handler;
ct->handler_data = (unsigned long)&done;
ct->timeout = 10;
ct->req = &gpn_ft->sg_req;
ct->resp = gpn_ft->sg_resp;
ct->req_count = 1;
ct->resp_count = ZFCP_GPN_FT_BUFFERS;
init_completion(&done);
ret = zfcp_fsf_send_ct(ct, NULL, NULL);
if (!ret)
wait_for_completion(&done);
return ret;
}
static void zfcp_validate_port(struct zfcp_port *port)
{
struct zfcp_adapter *adapter = port->adapter;
atomic_clear_mask(ZFCP_STATUS_COMMON_NOESC, &port->status);
if (port == adapter->nameserver_port)
return;
if ((port->supported_classes != 0) || (port->units != 0)) {
zfcp_port_put(port);
return;
}
zfcp_erp_port_shutdown(port, 0, 151, NULL);
zfcp_erp_wait(adapter);
zfcp_port_put(port);
zfcp_port_dequeue(port);
}
static int zfcp_scan_eval_gpn_ft(struct zfcp_gpn_ft *gpn_ft)
{
struct zfcp_send_ct *ct = &gpn_ft->ct;
struct scatterlist *sg = gpn_ft->sg_resp;
struct ct_hdr *hdr = sg_virt(sg);
struct gpn_ft_resp_acc *acc = sg_virt(sg);
struct zfcp_adapter *adapter = ct->port->adapter;
struct zfcp_port *port, *tmp;
u32 d_id;
int ret = 0, x;
if (ct->status)
return -EIO;
if (hdr->cmd_rsp_code != ZFCP_CT_ACCEPT) {
if (hdr->reason_code == ZFCP_CT_UNABLE_TO_PERFORM_CMD)
return -EAGAIN; /* might be a temporary condition */
return -EIO;
}
if (hdr->max_res_size)
return -E2BIG;
down(&zfcp_data.config_sema);
/* first entry is the header */
for (x = 1; x < ZFCP_GPN_FT_MAX_ENTRIES; x++) {
if (x % (ZFCP_GPN_FT_ENTRIES + 1))
acc++;
else
acc = sg_virt(++sg);
d_id = acc->port_id[0] << 16 | acc->port_id[1] << 8 |
acc->port_id[2];
/* skip the adapter's port and known remote ports */
if (acc->wwpn == fc_host_port_name(adapter->scsi_host) ||
zfcp_get_port_by_did(adapter, d_id))
continue;
port = zfcp_port_enqueue(adapter, acc->wwpn,
ZFCP_STATUS_PORT_DID_DID |
ZFCP_STATUS_COMMON_NOESC, d_id);
if (IS_ERR(port))
ret = PTR_ERR(port);
else
zfcp_erp_port_reopen(port, 0, 149, NULL);
if (acc->control & 0x80) /* last entry */
break;
}
zfcp_erp_wait(adapter);
list_for_each_entry_safe(port, tmp, &adapter->port_list_head, list)
zfcp_validate_port(port);
up(&zfcp_data.config_sema);
return ret;
}
/**
* zfcp_scan_ports - scan remote ports and attach new ports
* @adapter: pointer to struct zfcp_adapter
*/
int zfcp_scan_ports(struct zfcp_adapter *adapter)
{
int ret, i;
struct zfcp_gpn_ft *gpn_ft;
zfcp_erp_wait(adapter); /* wait until adapter is finished with ERP */
if (fc_host_port_type(adapter->scsi_host) != FC_PORTTYPE_NPORT)
return 0;
ret = zfcp_scan_get_nameserver(adapter);
if (ret)
return ret;
gpn_ft = zfcp_alloc_sg_env();
if (!gpn_ft)
return -ENOMEM;
for (i = 0; i < 3; i++) {
ret = zfcp_scan_issue_gpn_ft(gpn_ft, adapter);
if (!ret) {
ret = zfcp_scan_eval_gpn_ft(gpn_ft);
if (ret == -EAGAIN)
ssleep(1);
else
break;
}
}
zfcp_free_sg_env(gpn_ft);
return ret;
}
void _zfcp_scan_ports_later(struct work_struct *work)
{
zfcp_scan_ports(container_of(work, struct zfcp_adapter, scan_work));
}

File diff suppressed because it is too large Load Diff

View File

@ -1,27 +1,16 @@
/*
* This file is part of the zfcp device driver for
* FCP adapters for IBM System z9 and zSeries.
* zfcp device driver
*
* (C) Copyright IBM Corp. 2002, 2006
* Interface to the FSF support functions.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* Copyright IBM Corporation 2002, 2008
*/
#ifndef FSF_H
#define FSF_H
#include <linux/pfn.h>
#define FSF_QTCB_CURRENT_VERSION 0x00000001
/* FSF commands */
@ -258,6 +247,16 @@
#define FSF_UNIT_ACCESS_EXCLUSIVE 0x02000000
#define FSF_UNIT_ACCESS_OUTBOUND_TRANSFER 0x10000000
/* FSF interface for CFDC */
#define ZFCP_CFDC_MAX_SIZE 127 * 1024
#define ZFCP_CFDC_PAGES PFN_UP(ZFCP_CFDC_MAX_SIZE)
struct zfcp_fsf_cfdc {
struct scatterlist sg[ZFCP_CFDC_PAGES];
u32 command;
u32 option;
};
struct fsf_queue_designator {
u8 cssid;
u8 chpid;
@ -288,29 +287,6 @@ struct fsf_bit_error_payload {
u32 current_transmit_b2b_credit;
} __attribute__ ((packed));
struct fsf_status_read_buffer {
u32 status_type;
u32 status_subtype;
u32 length;
u32 res1;
struct fsf_queue_designator queue_designator;
u32 d_id;
u32 class;
u64 fcp_lun;
u8 res3[24];
u8 payload[FSF_STATUS_READ_PAYLOAD_SIZE];
} __attribute__ ((packed));
struct fsf_qual_version_error {
u32 fsf_version;
u32 res1[3];
} __attribute__ ((packed));
struct fsf_qual_sequence_error {
u32 exp_req_seq_no;
u32 res1[3];
} __attribute__ ((packed));
struct fsf_link_down_info {
u32 error_code;
u32 res1;
@ -323,11 +299,47 @@ struct fsf_link_down_info {
u8 vendor_specific_code;
} __attribute__ ((packed));
struct fsf_status_read_buffer {
u32 status_type;
u32 status_subtype;
u32 length;
u32 res1;
struct fsf_queue_designator queue_designator;
u32 d_id;
u32 class;
u64 fcp_lun;
u8 res3[24];
union {
u8 data[FSF_STATUS_READ_PAYLOAD_SIZE];
u32 word[FSF_STATUS_READ_PAYLOAD_SIZE/sizeof(u32)];
struct fsf_link_down_info link_down_info;
struct fsf_bit_error_payload bit_error;
} payload;
} __attribute__ ((packed));
struct fsf_qual_version_error {
u32 fsf_version;
u32 res1[3];
} __attribute__ ((packed));
struct fsf_qual_sequence_error {
u32 exp_req_seq_no;
u32 res1[3];
} __attribute__ ((packed));
struct fsf_qual_latency_info {
u32 channel_lat;
u32 fabric_lat;
u8 res1[8];
} __attribute__ ((packed));
union fsf_prot_status_qual {
u32 word[FSF_PROT_STATUS_QUAL_SIZE / sizeof(u32)];
u64 doubleword[FSF_PROT_STATUS_QUAL_SIZE / sizeof(u64)];
struct fsf_qual_version_error version_error;
struct fsf_qual_sequence_error sequence_error;
struct fsf_link_down_info link_down_info;
struct fsf_qual_latency_info latency_info;
} __attribute__ ((packed));
struct fsf_qtcb_prefix {
@ -437,7 +449,9 @@ struct fsf_qtcb_bottom_config {
u32 fc_link_speed;
u32 adapter_type;
u32 peer_d_id;
u8 res2[12];
u8 res1[2];
u16 timer_interval;
u8 res2[8];
u32 s_id;
struct fsf_nport_serv_param nport_serv_param;
u8 reserved_nport_serv_param[16];

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,496 @@
/*
* zfcp device driver
*
* sysfs attributes.
*
* Copyright IBM Corporation 2008
*/
#include "zfcp_ext.h"
#define ZFCP_DEV_ATTR(_feat, _name, _mode, _show, _store) \
struct device_attribute dev_attr_##_feat##_##_name = __ATTR(_name, _mode,\
_show, _store)
#define ZFCP_DEFINE_ATTR(_feat_def, _feat, _name, _format, _value) \
static ssize_t zfcp_sysfs_##_feat##_##_name##_show(struct device *dev, \
struct device_attribute *at,\
char *buf) \
{ \
struct _feat_def *_feat = dev_get_drvdata(dev); \
\
return sprintf(buf, _format, _value); \
} \
static ZFCP_DEV_ATTR(_feat, _name, S_IRUGO, \
zfcp_sysfs_##_feat##_##_name##_show, NULL);
ZFCP_DEFINE_ATTR(zfcp_adapter, adapter, status, "0x%08x\n",
atomic_read(&adapter->status));
ZFCP_DEFINE_ATTR(zfcp_adapter, adapter, peer_wwnn, "0x%016llx\n",
adapter->peer_wwnn);
ZFCP_DEFINE_ATTR(zfcp_adapter, adapter, peer_wwpn, "0x%016llx\n",
adapter->peer_wwpn);
ZFCP_DEFINE_ATTR(zfcp_adapter, adapter, peer_d_id, "0x%06x\n",
adapter->peer_d_id);
ZFCP_DEFINE_ATTR(zfcp_adapter, adapter, card_version, "0x%04x\n",
adapter->hydra_version);
ZFCP_DEFINE_ATTR(zfcp_adapter, adapter, lic_version, "0x%08x\n",
adapter->fsf_lic_version);
ZFCP_DEFINE_ATTR(zfcp_adapter, adapter, hardware_version, "0x%08x\n",
adapter->hardware_version);
ZFCP_DEFINE_ATTR(zfcp_adapter, adapter, in_recovery, "%d\n",
(atomic_read(&adapter->status) &
ZFCP_STATUS_COMMON_ERP_INUSE) != 0);
ZFCP_DEFINE_ATTR(zfcp_port, port, status, "0x%08x\n",
atomic_read(&port->status));
ZFCP_DEFINE_ATTR(zfcp_port, port, in_recovery, "%d\n",
(atomic_read(&port->status) &
ZFCP_STATUS_COMMON_ERP_INUSE) != 0);
ZFCP_DEFINE_ATTR(zfcp_port, port, access_denied, "%d\n",
(atomic_read(&port->status) &
ZFCP_STATUS_COMMON_ACCESS_DENIED) != 0);
ZFCP_DEFINE_ATTR(zfcp_unit, unit, status, "0x%08x\n",
atomic_read(&unit->status));
ZFCP_DEFINE_ATTR(zfcp_unit, unit, in_recovery, "%d\n",
(atomic_read(&unit->status) &
ZFCP_STATUS_COMMON_ERP_INUSE) != 0);
ZFCP_DEFINE_ATTR(zfcp_unit, unit, access_denied, "%d\n",
(atomic_read(&unit->status) &
ZFCP_STATUS_COMMON_ACCESS_DENIED) != 0);
ZFCP_DEFINE_ATTR(zfcp_unit, unit, access_shared, "%d\n",
(atomic_read(&unit->status) &
ZFCP_STATUS_UNIT_SHARED) != 0);
ZFCP_DEFINE_ATTR(zfcp_unit, unit, access_readonly, "%d\n",
(atomic_read(&unit->status) &
ZFCP_STATUS_UNIT_READONLY) != 0);
#define ZFCP_SYSFS_FAILED(_feat_def, _feat, _adapter, _mod_id, _reopen_id) \
static ssize_t zfcp_sysfs_##_feat##_failed_show(struct device *dev, \
struct device_attribute *attr, \
char *buf) \
{ \
struct _feat_def *_feat = dev_get_drvdata(dev); \
\
if (atomic_read(&_feat->status) & ZFCP_STATUS_COMMON_ERP_FAILED) \
return sprintf(buf, "1\n"); \
else \
return sprintf(buf, "0\n"); \
} \
static ssize_t zfcp_sysfs_##_feat##_failed_store(struct device *dev, \
struct device_attribute *attr,\
const char *buf, size_t count)\
{ \
struct _feat_def *_feat = dev_get_drvdata(dev); \
unsigned long val; \
int retval = 0; \
\
down(&zfcp_data.config_sema); \
if (atomic_read(&_feat->status) & ZFCP_STATUS_COMMON_REMOVE) { \
retval = -EBUSY; \
goto out; \
} \
\
if (strict_strtoul(buf, 0, &val) || val != 0) { \
retval = -EINVAL; \
goto out; \
} \
\
zfcp_erp_modify_##_feat##_status(_feat, _mod_id, NULL, \
ZFCP_STATUS_COMMON_RUNNING, ZFCP_SET);\
zfcp_erp_##_feat##_reopen(_feat, ZFCP_STATUS_COMMON_ERP_FAILED, \
_reopen_id, NULL); \
zfcp_erp_wait(_adapter); \
out: \
up(&zfcp_data.config_sema); \
return retval ? retval : (ssize_t) count; \
} \
static ZFCP_DEV_ATTR(_feat, failed, S_IWUSR | S_IRUGO, \
zfcp_sysfs_##_feat##_failed_show, \
zfcp_sysfs_##_feat##_failed_store);
ZFCP_SYSFS_FAILED(zfcp_adapter, adapter, adapter, 44, 93);
ZFCP_SYSFS_FAILED(zfcp_port, port, port->adapter, 45, 96);
ZFCP_SYSFS_FAILED(zfcp_unit, unit, unit->port->adapter, 46, 97);
static ssize_t zfcp_sysfs_port_rescan_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct zfcp_adapter *adapter = dev_get_drvdata(dev);
int ret;
if (atomic_read(&adapter->status) & ZFCP_STATUS_COMMON_REMOVE)
return -EBUSY;
ret = zfcp_scan_ports(adapter);
return ret ? ret : (ssize_t) count;
}
static ZFCP_DEV_ATTR(adapter, port_rescan, S_IWUSR, NULL,
zfcp_sysfs_port_rescan_store);
static ssize_t zfcp_sysfs_port_remove_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct zfcp_adapter *adapter = dev_get_drvdata(dev);
struct zfcp_port *port;
wwn_t wwpn;
int retval = 0;
down(&zfcp_data.config_sema);
if (atomic_read(&adapter->status) & ZFCP_STATUS_COMMON_REMOVE) {
retval = -EBUSY;
goto out;
}
if (strict_strtoull(buf, 0, &wwpn)) {
retval = -EINVAL;
goto out;
}
write_lock_irq(&zfcp_data.config_lock);
port = zfcp_get_port_by_wwpn(adapter, wwpn);
if (port && (atomic_read(&port->refcount) == 0)) {
zfcp_port_get(port);
atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, &port->status);
list_move(&port->list, &adapter->port_remove_lh);
} else
port = NULL;
write_unlock_irq(&zfcp_data.config_lock);
if (!port) {
retval = -ENXIO;
goto out;
}
zfcp_erp_port_shutdown(port, 0, 92, NULL);
zfcp_erp_wait(adapter);
zfcp_port_put(port);
zfcp_port_dequeue(port);
out:
up(&zfcp_data.config_sema);
return retval ? retval : (ssize_t) count;
}
static ZFCP_DEV_ATTR(adapter, port_remove, S_IWUSR, NULL,
zfcp_sysfs_port_remove_store);
static struct attribute *zfcp_adapter_attrs[] = {
&dev_attr_adapter_failed.attr,
&dev_attr_adapter_in_recovery.attr,
&dev_attr_adapter_port_remove.attr,
&dev_attr_adapter_port_rescan.attr,
&dev_attr_adapter_peer_wwnn.attr,
&dev_attr_adapter_peer_wwpn.attr,
&dev_attr_adapter_peer_d_id.attr,
&dev_attr_adapter_card_version.attr,
&dev_attr_adapter_lic_version.attr,
&dev_attr_adapter_status.attr,
&dev_attr_adapter_hardware_version.attr,
NULL
};
struct attribute_group zfcp_sysfs_adapter_attrs = {
.attrs = zfcp_adapter_attrs,
};
static ssize_t zfcp_sysfs_unit_add_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct zfcp_port *port = dev_get_drvdata(dev);
struct zfcp_unit *unit;
fcp_lun_t fcp_lun;
int retval = -EINVAL;
down(&zfcp_data.config_sema);
if (atomic_read(&port->status) & ZFCP_STATUS_COMMON_REMOVE) {
retval = -EBUSY;
goto out;
}
if (strict_strtoull(buf, 0, &fcp_lun))
goto out;
unit = zfcp_unit_enqueue(port, fcp_lun);
if (IS_ERR(unit))
goto out;
retval = 0;
zfcp_erp_unit_reopen(unit, 0, 94, NULL);
zfcp_erp_wait(unit->port->adapter);
zfcp_unit_put(unit);
out:
up(&zfcp_data.config_sema);
return retval ? retval : (ssize_t) count;
}
static DEVICE_ATTR(unit_add, S_IWUSR, NULL, zfcp_sysfs_unit_add_store);
static ssize_t zfcp_sysfs_unit_remove_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct zfcp_port *port = dev_get_drvdata(dev);
struct zfcp_unit *unit;
fcp_lun_t fcp_lun;
int retval = 0;
down(&zfcp_data.config_sema);
if (atomic_read(&port->status) & ZFCP_STATUS_COMMON_REMOVE) {
retval = -EBUSY;
goto out;
}
if (strict_strtoull(buf, 0, &fcp_lun)) {
retval = -EINVAL;
goto out;
}
write_lock_irq(&zfcp_data.config_lock);
unit = zfcp_get_unit_by_lun(port, fcp_lun);
if (unit && (atomic_read(&unit->refcount) == 0)) {
zfcp_unit_get(unit);
atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, &unit->status);
list_move(&unit->list, &port->unit_remove_lh);
} else
unit = NULL;
write_unlock_irq(&zfcp_data.config_lock);
if (!unit) {
retval = -ENXIO;
goto out;
}
zfcp_erp_unit_shutdown(unit, 0, 95, NULL);
zfcp_erp_wait(unit->port->adapter);
zfcp_unit_put(unit);
zfcp_unit_dequeue(unit);
out:
up(&zfcp_data.config_sema);
return retval ? retval : (ssize_t) count;
}
static DEVICE_ATTR(unit_remove, S_IWUSR, NULL, zfcp_sysfs_unit_remove_store);
static struct attribute *zfcp_port_ns_attrs[] = {
&dev_attr_port_failed.attr,
&dev_attr_port_in_recovery.attr,
&dev_attr_port_status.attr,
&dev_attr_port_access_denied.attr,
NULL
};
/**
* zfcp_sysfs_ns_port_attrs - sysfs attributes for nameserver
*/
struct attribute_group zfcp_sysfs_ns_port_attrs = {
.attrs = zfcp_port_ns_attrs,
};
static struct attribute *zfcp_port_no_ns_attrs[] = {
&dev_attr_unit_add.attr,
&dev_attr_unit_remove.attr,
&dev_attr_port_failed.attr,
&dev_attr_port_in_recovery.attr,
&dev_attr_port_status.attr,
&dev_attr_port_access_denied.attr,
NULL
};
/**
* zfcp_sysfs_port_attrs - sysfs attributes for all other ports
*/
struct attribute_group zfcp_sysfs_port_attrs = {
.attrs = zfcp_port_no_ns_attrs,
};
static struct attribute *zfcp_unit_attrs[] = {
&dev_attr_unit_failed.attr,
&dev_attr_unit_in_recovery.attr,
&dev_attr_unit_status.attr,
&dev_attr_unit_access_denied.attr,
&dev_attr_unit_access_shared.attr,
&dev_attr_unit_access_readonly.attr,
NULL
};
struct attribute_group zfcp_sysfs_unit_attrs = {
.attrs = zfcp_unit_attrs,
};
#define ZFCP_DEFINE_LATENCY_ATTR(_name) \
static ssize_t \
zfcp_sysfs_unit_##_name##_latency_show(struct device *dev, \
struct device_attribute *attr, \
char *buf) { \
struct scsi_device *sdev = to_scsi_device(dev); \
struct zfcp_unit *unit = sdev->hostdata; \
struct zfcp_latencies *lat = &unit->latencies; \
struct zfcp_adapter *adapter = unit->port->adapter; \
unsigned long flags; \
unsigned long long fsum, fmin, fmax, csum, cmin, cmax, cc; \
\
spin_lock_irqsave(&lat->lock, flags); \
fsum = lat->_name.fabric.sum * adapter->timer_ticks; \
fmin = lat->_name.fabric.min * adapter->timer_ticks; \
fmax = lat->_name.fabric.max * adapter->timer_ticks; \
csum = lat->_name.channel.sum * adapter->timer_ticks; \
cmin = lat->_name.channel.min * adapter->timer_ticks; \
cmax = lat->_name.channel.max * adapter->timer_ticks; \
cc = lat->_name.counter; \
spin_unlock_irqrestore(&lat->lock, flags); \
\
do_div(fsum, 1000); \
do_div(fmin, 1000); \
do_div(fmax, 1000); \
do_div(csum, 1000); \
do_div(cmin, 1000); \
do_div(cmax, 1000); \
\
return sprintf(buf, "%llu %llu %llu %llu %llu %llu %llu\n", \
fmin, fmax, fsum, cmin, cmax, csum, cc); \
} \
static ssize_t \
zfcp_sysfs_unit_##_name##_latency_store(struct device *dev, \
struct device_attribute *attr, \
const char *buf, size_t count) \
{ \
struct scsi_device *sdev = to_scsi_device(dev); \
struct zfcp_unit *unit = sdev->hostdata; \
struct zfcp_latencies *lat = &unit->latencies; \
unsigned long flags; \
\
spin_lock_irqsave(&lat->lock, flags); \
lat->_name.fabric.sum = 0; \
lat->_name.fabric.min = 0xFFFFFFFF; \
lat->_name.fabric.max = 0; \
lat->_name.channel.sum = 0; \
lat->_name.channel.min = 0xFFFFFFFF; \
lat->_name.channel.max = 0; \
lat->_name.counter = 0; \
spin_unlock_irqrestore(&lat->lock, flags); \
\
return (ssize_t) count; \
} \
static DEVICE_ATTR(_name##_latency, S_IWUSR | S_IRUGO, \
zfcp_sysfs_unit_##_name##_latency_show, \
zfcp_sysfs_unit_##_name##_latency_store);
ZFCP_DEFINE_LATENCY_ATTR(read);
ZFCP_DEFINE_LATENCY_ATTR(write);
ZFCP_DEFINE_LATENCY_ATTR(cmd);
#define ZFCP_DEFINE_SCSI_ATTR(_name, _format, _value) \
static ssize_t zfcp_sysfs_scsi_##_name##_show(struct device *dev, \
struct device_attribute *attr,\
char *buf) \
{ \
struct scsi_device *sdev = to_scsi_device(dev); \
struct zfcp_unit *unit = sdev->hostdata; \
\
return sprintf(buf, _format, _value); \
} \
static DEVICE_ATTR(_name, S_IRUGO, zfcp_sysfs_scsi_##_name##_show, NULL);
ZFCP_DEFINE_SCSI_ATTR(hba_id, "%s\n",
unit->port->adapter->ccw_device->dev.bus_id);
ZFCP_DEFINE_SCSI_ATTR(wwpn, "0x%016llx\n", unit->port->wwpn);
ZFCP_DEFINE_SCSI_ATTR(fcp_lun, "0x%016llx\n", unit->fcp_lun);
struct device_attribute *zfcp_sysfs_sdev_attrs[] = {
&dev_attr_fcp_lun,
&dev_attr_wwpn,
&dev_attr_hba_id,
&dev_attr_read_latency,
&dev_attr_write_latency,
&dev_attr_cmd_latency,
NULL
};
static ssize_t zfcp_sysfs_adapter_util_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct Scsi_Host *scsi_host = dev_to_shost(dev);
struct fsf_qtcb_bottom_port *qtcb_port;
struct zfcp_adapter *adapter;
int retval;
adapter = (struct zfcp_adapter *) scsi_host->hostdata[0];
if (!(adapter->adapter_features & FSF_FEATURE_MEASUREMENT_DATA))
return -EOPNOTSUPP;
qtcb_port = kzalloc(sizeof(struct fsf_qtcb_bottom_port), GFP_KERNEL);
if (!qtcb_port)
return -ENOMEM;
retval = zfcp_fsf_exchange_port_data_sync(adapter, qtcb_port);
if (!retval)
retval = sprintf(buf, "%u %u %u\n", qtcb_port->cp_util,
qtcb_port->cb_util, qtcb_port->a_util);
kfree(qtcb_port);
return retval;
}
static DEVICE_ATTR(utilization, S_IRUGO, zfcp_sysfs_adapter_util_show, NULL);
static int zfcp_sysfs_adapter_ex_config(struct device *dev,
struct fsf_statistics_info *stat_inf)
{
struct Scsi_Host *scsi_host = dev_to_shost(dev);
struct fsf_qtcb_bottom_config *qtcb_config;
struct zfcp_adapter *adapter;
int retval;
adapter = (struct zfcp_adapter *) scsi_host->hostdata[0];
if (!(adapter->adapter_features & FSF_FEATURE_MEASUREMENT_DATA))
return -EOPNOTSUPP;
qtcb_config = kzalloc(sizeof(struct fsf_qtcb_bottom_config),
GFP_KERNEL);
if (!qtcb_config)
return -ENOMEM;
retval = zfcp_fsf_exchange_config_data_sync(adapter, qtcb_config);
if (!retval)
*stat_inf = qtcb_config->stat_info;
kfree(qtcb_config);
return retval;
}
#define ZFCP_SHOST_ATTR(_name, _format, _arg...) \
static ssize_t zfcp_sysfs_adapter_##_name##_show(struct device *dev, \
struct device_attribute *attr,\
char *buf) \
{ \
struct fsf_statistics_info stat_info; \
int retval; \
\
retval = zfcp_sysfs_adapter_ex_config(dev, &stat_info); \
if (retval) \
return retval; \
\
return sprintf(buf, _format, ## _arg); \
} \
static DEVICE_ATTR(_name, S_IRUGO, zfcp_sysfs_adapter_##_name##_show, NULL);
ZFCP_SHOST_ATTR(requests, "%llu %llu %llu\n",
(unsigned long long) stat_info.input_req,
(unsigned long long) stat_info.output_req,
(unsigned long long) stat_info.control_req);
ZFCP_SHOST_ATTR(megabytes, "%llu %llu\n",
(unsigned long long) stat_info.input_mb,
(unsigned long long) stat_info.output_mb);
ZFCP_SHOST_ATTR(seconds_active, "%llu\n",
(unsigned long long) stat_info.seconds_act);
struct device_attribute *zfcp_sysfs_shost_attrs[] = {
&dev_attr_utilization,
&dev_attr_requests,
&dev_attr_megabytes,
&dev_attr_seconds_active,
NULL
};

View File

@ -1,270 +0,0 @@
/*
* This file is part of the zfcp device driver for
* FCP adapters for IBM System z9 and zSeries.
*
* (C) Copyright IBM Corp. 2002, 2006
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "zfcp_ext.h"
#define ZFCP_LOG_AREA ZFCP_LOG_AREA_CONFIG
/**
* ZFCP_DEFINE_ADAPTER_ATTR
* @_name: name of show attribute
* @_format: format string
* @_value: value to print
*
* Generates attributes for an adapter.
*/
#define ZFCP_DEFINE_ADAPTER_ATTR(_name, _format, _value) \
static ssize_t zfcp_sysfs_adapter_##_name##_show(struct device *dev, struct device_attribute *attr, \
char *buf) \
{ \
struct zfcp_adapter *adapter; \
\
adapter = dev_get_drvdata(dev); \
return sprintf(buf, _format, _value); \
} \
\
static DEVICE_ATTR(_name, S_IRUGO, zfcp_sysfs_adapter_##_name##_show, NULL);
ZFCP_DEFINE_ADAPTER_ATTR(status, "0x%08x\n", atomic_read(&adapter->status));
ZFCP_DEFINE_ADAPTER_ATTR(peer_wwnn, "0x%016llx\n", adapter->peer_wwnn);
ZFCP_DEFINE_ADAPTER_ATTR(peer_wwpn, "0x%016llx\n", adapter->peer_wwpn);
ZFCP_DEFINE_ADAPTER_ATTR(peer_d_id, "0x%06x\n", adapter->peer_d_id);
ZFCP_DEFINE_ADAPTER_ATTR(card_version, "0x%04x\n", adapter->hydra_version);
ZFCP_DEFINE_ADAPTER_ATTR(lic_version, "0x%08x\n", adapter->fsf_lic_version);
ZFCP_DEFINE_ADAPTER_ATTR(hardware_version, "0x%08x\n",
adapter->hardware_version);
ZFCP_DEFINE_ADAPTER_ATTR(in_recovery, "%d\n", atomic_test_mask
(ZFCP_STATUS_COMMON_ERP_INUSE, &adapter->status));
/**
* zfcp_sysfs_port_add_store - add a port to sysfs tree
* @dev: pointer to belonging device
* @buf: pointer to input buffer
* @count: number of bytes in buffer
*
* Store function of the "port_add" attribute of an adapter.
*/
static ssize_t
zfcp_sysfs_port_add_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
wwn_t wwpn;
char *endp;
struct zfcp_adapter *adapter;
struct zfcp_port *port;
int retval = -EINVAL;
down(&zfcp_data.config_sema);
adapter = dev_get_drvdata(dev);
if (atomic_test_mask(ZFCP_STATUS_COMMON_REMOVE, &adapter->status)) {
retval = -EBUSY;
goto out;
}
wwpn = simple_strtoull(buf, &endp, 0);
if ((endp + 1) < (buf + count))
goto out;
port = zfcp_port_enqueue(adapter, wwpn, 0, 0);
if (!port)
goto out;
retval = 0;
zfcp_erp_port_reopen(port, 0, 91, NULL);
zfcp_erp_wait(port->adapter);
zfcp_port_put(port);
out:
up(&zfcp_data.config_sema);
return retval ? retval : (ssize_t) count;
}
static DEVICE_ATTR(port_add, S_IWUSR, NULL, zfcp_sysfs_port_add_store);
/**
* zfcp_sysfs_port_remove_store - remove a port from sysfs tree
* @dev: pointer to belonging device
* @buf: pointer to input buffer
* @count: number of bytes in buffer
*
* Store function of the "port_remove" attribute of an adapter.
*/
static ssize_t
zfcp_sysfs_port_remove_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
struct zfcp_adapter *adapter;
struct zfcp_port *port;
wwn_t wwpn;
char *endp;
int retval = 0;
down(&zfcp_data.config_sema);
adapter = dev_get_drvdata(dev);
if (atomic_test_mask(ZFCP_STATUS_COMMON_REMOVE, &adapter->status)) {
retval = -EBUSY;
goto out;
}
wwpn = simple_strtoull(buf, &endp, 0);
if ((endp + 1) < (buf + count)) {
retval = -EINVAL;
goto out;
}
write_lock_irq(&zfcp_data.config_lock);
port = zfcp_get_port_by_wwpn(adapter, wwpn);
if (port && (atomic_read(&port->refcount) == 0)) {
zfcp_port_get(port);
atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, &port->status);
list_move(&port->list, &adapter->port_remove_lh);
}
else {
port = NULL;
}
write_unlock_irq(&zfcp_data.config_lock);
if (!port) {
retval = -ENXIO;
goto out;
}
zfcp_erp_port_shutdown(port, 0, 92, NULL);
zfcp_erp_wait(adapter);
zfcp_port_put(port);
zfcp_port_dequeue(port);
out:
up(&zfcp_data.config_sema);
return retval ? retval : (ssize_t) count;
}
static DEVICE_ATTR(port_remove, S_IWUSR, NULL, zfcp_sysfs_port_remove_store);
/**
* zfcp_sysfs_adapter_failed_store - failed state of adapter
* @dev: pointer to belonging device
* @buf: pointer to input buffer
* @count: number of bytes in buffer
*
* Store function of the "failed" attribute of an adapter.
* If a "0" gets written to "failed", error recovery will be
* started for the belonging adapter.
*/
static ssize_t
zfcp_sysfs_adapter_failed_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct zfcp_adapter *adapter;
unsigned int val;
char *endp;
int retval = 0;
down(&zfcp_data.config_sema);
adapter = dev_get_drvdata(dev);
if (atomic_test_mask(ZFCP_STATUS_COMMON_REMOVE, &adapter->status)) {
retval = -EBUSY;
goto out;
}
val = simple_strtoul(buf, &endp, 0);
if (((endp + 1) < (buf + count)) || (val != 0)) {
retval = -EINVAL;
goto out;
}
zfcp_erp_modify_adapter_status(adapter, 44, NULL,
ZFCP_STATUS_COMMON_RUNNING, ZFCP_SET);
zfcp_erp_adapter_reopen(adapter, ZFCP_STATUS_COMMON_ERP_FAILED, 93,
NULL);
zfcp_erp_wait(adapter);
out:
up(&zfcp_data.config_sema);
return retval ? retval : (ssize_t) count;
}
/**
* zfcp_sysfs_adapter_failed_show - failed state of adapter
* @dev: pointer to belonging device
* @buf: pointer to input buffer
*
* Show function of "failed" attribute of adapter. Will be
* "0" if adapter is working, otherwise "1".
*/
static ssize_t
zfcp_sysfs_adapter_failed_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct zfcp_adapter *adapter;
adapter = dev_get_drvdata(dev);
if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_FAILED, &adapter->status))
return sprintf(buf, "1\n");
else
return sprintf(buf, "0\n");
}
static DEVICE_ATTR(failed, S_IWUSR | S_IRUGO, zfcp_sysfs_adapter_failed_show,
zfcp_sysfs_adapter_failed_store);
static struct attribute *zfcp_adapter_attrs[] = {
&dev_attr_failed.attr,
&dev_attr_in_recovery.attr,
&dev_attr_port_remove.attr,
&dev_attr_port_add.attr,
&dev_attr_peer_wwnn.attr,
&dev_attr_peer_wwpn.attr,
&dev_attr_peer_d_id.attr,
&dev_attr_card_version.attr,
&dev_attr_lic_version.attr,
&dev_attr_status.attr,
&dev_attr_hardware_version.attr,
NULL
};
static struct attribute_group zfcp_adapter_attr_group = {
.attrs = zfcp_adapter_attrs,
};
/**
* zfcp_sysfs_create_adapter_files - create sysfs adapter files
* @dev: pointer to belonging device
*
* Create all attributes of the sysfs representation of an adapter.
*/
int
zfcp_sysfs_adapter_create_files(struct device *dev)
{
return sysfs_create_group(&dev->kobj, &zfcp_adapter_attr_group);
}
/**
* zfcp_sysfs_remove_adapter_files - remove sysfs adapter files
* @dev: pointer to belonging device
*
* Remove all attributes of the sysfs representation of an adapter.
*/
void
zfcp_sysfs_adapter_remove_files(struct device *dev)
{
sysfs_remove_group(&dev->kobj, &zfcp_adapter_attr_group);
}
#undef ZFCP_LOG_AREA

View File

@ -1,106 +0,0 @@
/*
* This file is part of the zfcp device driver for
* FCP adapters for IBM System z9 and zSeries.
*
* (C) Copyright IBM Corp. 2002, 2006
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "zfcp_ext.h"
#define ZFCP_LOG_AREA ZFCP_LOG_AREA_CONFIG
/**
* ZFCP_DEFINE_DRIVER_ATTR - define for all loglevels sysfs attributes
* @_name: name of attribute
* @_define: name of ZFCP loglevel define
*
* Generates store function for a sysfs loglevel attribute of zfcp driver.
*/
#define ZFCP_DEFINE_DRIVER_ATTR(_name, _define) \
static ssize_t zfcp_sysfs_loglevel_##_name##_store(struct device_driver *drv, \
const char *buf, \
size_t count) \
{ \
unsigned int loglevel; \
unsigned int new_loglevel; \
char *endp; \
\
new_loglevel = simple_strtoul(buf, &endp, 0); \
if ((endp + 1) < (buf + count)) \
return -EINVAL; \
if (new_loglevel > 3) \
return -EINVAL; \
down(&zfcp_data.config_sema); \
loglevel = atomic_read(&zfcp_data.loglevel); \
loglevel &= ~((unsigned int) 0xf << (ZFCP_LOG_AREA_##_define << 2)); \
loglevel |= new_loglevel << (ZFCP_LOG_AREA_##_define << 2); \
atomic_set(&zfcp_data.loglevel, loglevel); \
up(&zfcp_data.config_sema); \
return count; \
} \
\
static ssize_t zfcp_sysfs_loglevel_##_name##_show(struct device_driver *dev, \
char *buf) \
{ \
return sprintf(buf,"%d\n", (unsigned int) \
ZFCP_GET_LOG_VALUE(ZFCP_LOG_AREA_##_define)); \
} \
\
static DRIVER_ATTR(loglevel_##_name, S_IWUSR | S_IRUGO, \
zfcp_sysfs_loglevel_##_name##_show, \
zfcp_sysfs_loglevel_##_name##_store);
ZFCP_DEFINE_DRIVER_ATTR(other, OTHER);
ZFCP_DEFINE_DRIVER_ATTR(scsi, SCSI);
ZFCP_DEFINE_DRIVER_ATTR(fsf, FSF);
ZFCP_DEFINE_DRIVER_ATTR(config, CONFIG);
ZFCP_DEFINE_DRIVER_ATTR(cio, CIO);
ZFCP_DEFINE_DRIVER_ATTR(qdio, QDIO);
ZFCP_DEFINE_DRIVER_ATTR(erp, ERP);
ZFCP_DEFINE_DRIVER_ATTR(fc, FC);
static ssize_t zfcp_sysfs_version_show(struct device_driver *dev,
char *buf)
{
return sprintf(buf, "%s\n", zfcp_data.driver_version);
}
static DRIVER_ATTR(version, S_IRUGO, zfcp_sysfs_version_show, NULL);
static struct attribute *zfcp_driver_attrs[] = {
&driver_attr_loglevel_other.attr,
&driver_attr_loglevel_scsi.attr,
&driver_attr_loglevel_fsf.attr,
&driver_attr_loglevel_config.attr,
&driver_attr_loglevel_cio.attr,
&driver_attr_loglevel_qdio.attr,
&driver_attr_loglevel_erp.attr,
&driver_attr_loglevel_fc.attr,
&driver_attr_version.attr,
NULL
};
static struct attribute_group zfcp_driver_attr_group = {
.attrs = zfcp_driver_attrs,
};
struct attribute_group *zfcp_driver_attr_groups[] = {
&zfcp_driver_attr_group,
NULL,
};
#undef ZFCP_LOG_AREA

View File

@ -1,295 +0,0 @@
/*
* This file is part of the zfcp device driver for
* FCP adapters for IBM System z9 and zSeries.
*
* (C) Copyright IBM Corp. 2002, 2006
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "zfcp_ext.h"
#define ZFCP_LOG_AREA ZFCP_LOG_AREA_CONFIG
/**
* zfcp_sysfs_port_release - gets called when a struct device port is released
* @dev: pointer to belonging device
*/
void
zfcp_sysfs_port_release(struct device *dev)
{
kfree(dev);
}
/**
* ZFCP_DEFINE_PORT_ATTR
* @_name: name of show attribute
* @_format: format string
* @_value: value to print
*
* Generates attributes for a port.
*/
#define ZFCP_DEFINE_PORT_ATTR(_name, _format, _value) \
static ssize_t zfcp_sysfs_port_##_name##_show(struct device *dev, struct device_attribute *attr, \
char *buf) \
{ \
struct zfcp_port *port; \
\
port = dev_get_drvdata(dev); \
return sprintf(buf, _format, _value); \
} \
\
static DEVICE_ATTR(_name, S_IRUGO, zfcp_sysfs_port_##_name##_show, NULL);
ZFCP_DEFINE_PORT_ATTR(status, "0x%08x\n", atomic_read(&port->status));
ZFCP_DEFINE_PORT_ATTR(in_recovery, "%d\n", atomic_test_mask
(ZFCP_STATUS_COMMON_ERP_INUSE, &port->status));
ZFCP_DEFINE_PORT_ATTR(access_denied, "%d\n", atomic_test_mask
(ZFCP_STATUS_COMMON_ACCESS_DENIED, &port->status));
/**
* zfcp_sysfs_unit_add_store - add a unit to sysfs tree
* @dev: pointer to belonging device
* @buf: pointer to input buffer
* @count: number of bytes in buffer
*
* Store function of the "unit_add" attribute of a port.
*/
static ssize_t
zfcp_sysfs_unit_add_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
fcp_lun_t fcp_lun;
char *endp;
struct zfcp_port *port;
struct zfcp_unit *unit;
int retval = -EINVAL;
down(&zfcp_data.config_sema);
port = dev_get_drvdata(dev);
if (atomic_test_mask(ZFCP_STATUS_COMMON_REMOVE, &port->status)) {
retval = -EBUSY;
goto out;
}
fcp_lun = simple_strtoull(buf, &endp, 0);
if ((endp + 1) < (buf + count))
goto out;
unit = zfcp_unit_enqueue(port, fcp_lun);
if (!unit)
goto out;
retval = 0;
zfcp_erp_unit_reopen(unit, 0, 94, NULL);
zfcp_erp_wait(unit->port->adapter);
zfcp_unit_put(unit);
out:
up(&zfcp_data.config_sema);
return retval ? retval : (ssize_t) count;
}
static DEVICE_ATTR(unit_add, S_IWUSR, NULL, zfcp_sysfs_unit_add_store);
/**
* zfcp_sysfs_unit_remove_store - remove a unit from sysfs tree
* @dev: pointer to belonging device
* @buf: pointer to input buffer
* @count: number of bytes in buffer
*/
static ssize_t
zfcp_sysfs_unit_remove_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
struct zfcp_port *port;
struct zfcp_unit *unit;
fcp_lun_t fcp_lun;
char *endp;
int retval = 0;
down(&zfcp_data.config_sema);
port = dev_get_drvdata(dev);
if (atomic_test_mask(ZFCP_STATUS_COMMON_REMOVE, &port->status)) {
retval = -EBUSY;
goto out;
}
fcp_lun = simple_strtoull(buf, &endp, 0);
if ((endp + 1) < (buf + count)) {
retval = -EINVAL;
goto out;
}
write_lock_irq(&zfcp_data.config_lock);
unit = zfcp_get_unit_by_lun(port, fcp_lun);
if (unit && (atomic_read(&unit->refcount) == 0)) {
zfcp_unit_get(unit);
atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, &unit->status);
list_move(&unit->list, &port->unit_remove_lh);
}
else {
unit = NULL;
}
write_unlock_irq(&zfcp_data.config_lock);
if (!unit) {
retval = -ENXIO;
goto out;
}
zfcp_erp_unit_shutdown(unit, 0, 95, NULL);
zfcp_erp_wait(unit->port->adapter);
zfcp_unit_put(unit);
zfcp_unit_dequeue(unit);
out:
up(&zfcp_data.config_sema);
return retval ? retval : (ssize_t) count;
}
static DEVICE_ATTR(unit_remove, S_IWUSR, NULL, zfcp_sysfs_unit_remove_store);
/**
* zfcp_sysfs_port_failed_store - failed state of port
* @dev: pointer to belonging device
* @buf: pointer to input buffer
* @count: number of bytes in buffer
*
* Store function of the "failed" attribute of a port.
* If a "0" gets written to "failed", error recovery will be
* started for the belonging port.
*/
static ssize_t
zfcp_sysfs_port_failed_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
struct zfcp_port *port;
unsigned int val;
char *endp;
int retval = 0;
down(&zfcp_data.config_sema);
port = dev_get_drvdata(dev);
if (atomic_test_mask(ZFCP_STATUS_COMMON_REMOVE, &port->status)) {
retval = -EBUSY;
goto out;
}
val = simple_strtoul(buf, &endp, 0);
if (((endp + 1) < (buf + count)) || (val != 0)) {
retval = -EINVAL;
goto out;
}
zfcp_erp_modify_port_status(port, 45, NULL,
ZFCP_STATUS_COMMON_RUNNING, ZFCP_SET);
zfcp_erp_port_reopen(port, ZFCP_STATUS_COMMON_ERP_FAILED, 96, NULL);
zfcp_erp_wait(port->adapter);
out:
up(&zfcp_data.config_sema);
return retval ? retval : (ssize_t) count;
}
/**
* zfcp_sysfs_port_failed_show - failed state of port
* @dev: pointer to belonging device
* @buf: pointer to input buffer
*
* Show function of "failed" attribute of port. Will be
* "0" if port is working, otherwise "1".
*/
static ssize_t
zfcp_sysfs_port_failed_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct zfcp_port *port;
port = dev_get_drvdata(dev);
if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_FAILED, &port->status))
return sprintf(buf, "1\n");
else
return sprintf(buf, "0\n");
}
static DEVICE_ATTR(failed, S_IWUSR | S_IRUGO, zfcp_sysfs_port_failed_show,
zfcp_sysfs_port_failed_store);
/**
* zfcp_port_common_attrs
* sysfs attributes that are common for all kind of fc ports.
*/
static struct attribute *zfcp_port_common_attrs[] = {
&dev_attr_failed.attr,
&dev_attr_in_recovery.attr,
&dev_attr_status.attr,
&dev_attr_access_denied.attr,
NULL
};
static struct attribute_group zfcp_port_common_attr_group = {
.attrs = zfcp_port_common_attrs,
};
/**
* zfcp_port_no_ns_attrs
* sysfs attributes not to be used for nameserver ports.
*/
static struct attribute *zfcp_port_no_ns_attrs[] = {
&dev_attr_unit_add.attr,
&dev_attr_unit_remove.attr,
NULL
};
static struct attribute_group zfcp_port_no_ns_attr_group = {
.attrs = zfcp_port_no_ns_attrs,
};
/**
* zfcp_sysfs_port_create_files - create sysfs port files
* @dev: pointer to belonging device
*
* Create all attributes of the sysfs representation of a port.
*/
int
zfcp_sysfs_port_create_files(struct device *dev, u32 flags)
{
int retval;
retval = sysfs_create_group(&dev->kobj, &zfcp_port_common_attr_group);
if ((flags & ZFCP_STATUS_PORT_WKA) || retval)
return retval;
retval = sysfs_create_group(&dev->kobj, &zfcp_port_no_ns_attr_group);
if (retval)
sysfs_remove_group(&dev->kobj, &zfcp_port_common_attr_group);
return retval;
}
/**
* zfcp_sysfs_port_remove_files - remove sysfs port files
* @dev: pointer to belonging device
*
* Remove all attributes of the sysfs representation of a port.
*/
void
zfcp_sysfs_port_remove_files(struct device *dev, u32 flags)
{
sysfs_remove_group(&dev->kobj, &zfcp_port_common_attr_group);
if (!(flags & ZFCP_STATUS_PORT_WKA))
sysfs_remove_group(&dev->kobj, &zfcp_port_no_ns_attr_group);
}
#undef ZFCP_LOG_AREA

View File

@ -1,167 +0,0 @@
/*
* This file is part of the zfcp device driver for
* FCP adapters for IBM System z9 and zSeries.
*
* (C) Copyright IBM Corp. 2002, 2006
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "zfcp_ext.h"
#define ZFCP_LOG_AREA ZFCP_LOG_AREA_CONFIG
/**
* zfcp_sysfs_unit_release - gets called when a struct device unit is released
* @dev: pointer to belonging device
*/
void
zfcp_sysfs_unit_release(struct device *dev)
{
kfree(dev);
}
/**
* ZFCP_DEFINE_UNIT_ATTR
* @_name: name of show attribute
* @_format: format string
* @_value: value to print
*
* Generates attribute for a unit.
*/
#define ZFCP_DEFINE_UNIT_ATTR(_name, _format, _value) \
static ssize_t zfcp_sysfs_unit_##_name##_show(struct device *dev, struct device_attribute *attr, \
char *buf) \
{ \
struct zfcp_unit *unit; \
\
unit = dev_get_drvdata(dev); \
return sprintf(buf, _format, _value); \
} \
\
static DEVICE_ATTR(_name, S_IRUGO, zfcp_sysfs_unit_##_name##_show, NULL);
ZFCP_DEFINE_UNIT_ATTR(status, "0x%08x\n", atomic_read(&unit->status));
ZFCP_DEFINE_UNIT_ATTR(in_recovery, "%d\n", atomic_test_mask
(ZFCP_STATUS_COMMON_ERP_INUSE, &unit->status));
ZFCP_DEFINE_UNIT_ATTR(access_denied, "%d\n", atomic_test_mask
(ZFCP_STATUS_COMMON_ACCESS_DENIED, &unit->status));
ZFCP_DEFINE_UNIT_ATTR(access_shared, "%d\n", atomic_test_mask
(ZFCP_STATUS_UNIT_SHARED, &unit->status));
ZFCP_DEFINE_UNIT_ATTR(access_readonly, "%d\n", atomic_test_mask
(ZFCP_STATUS_UNIT_READONLY, &unit->status));
/**
* zfcp_sysfs_unit_failed_store - failed state of unit
* @dev: pointer to belonging device
* @buf: pointer to input buffer
* @count: number of bytes in buffer
*
* Store function of the "failed" attribute of a unit.
* If a "0" gets written to "failed", error recovery will be
* started for the belonging unit.
*/
static ssize_t
zfcp_sysfs_unit_failed_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
struct zfcp_unit *unit;
unsigned int val;
char *endp;
int retval = 0;
down(&zfcp_data.config_sema);
unit = dev_get_drvdata(dev);
if (atomic_test_mask(ZFCP_STATUS_COMMON_REMOVE, &unit->status)) {
retval = -EBUSY;
goto out;
}
val = simple_strtoul(buf, &endp, 0);
if (((endp + 1) < (buf + count)) || (val != 0)) {
retval = -EINVAL;
goto out;
}
zfcp_erp_modify_unit_status(unit, 46, NULL,
ZFCP_STATUS_COMMON_RUNNING, ZFCP_SET);
zfcp_erp_unit_reopen(unit, ZFCP_STATUS_COMMON_ERP_FAILED, 97, NULL);
zfcp_erp_wait(unit->port->adapter);
out:
up(&zfcp_data.config_sema);
return retval ? retval : (ssize_t) count;
}
/**
* zfcp_sysfs_unit_failed_show - failed state of unit
* @dev: pointer to belonging device
* @buf: pointer to input buffer
*
* Show function of "failed" attribute of unit. Will be
* "0" if unit is working, otherwise "1".
*/
static ssize_t
zfcp_sysfs_unit_failed_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct zfcp_unit *unit;
unit = dev_get_drvdata(dev);
if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_FAILED, &unit->status))
return sprintf(buf, "1\n");
else
return sprintf(buf, "0\n");
}
static DEVICE_ATTR(failed, S_IWUSR | S_IRUGO, zfcp_sysfs_unit_failed_show,
zfcp_sysfs_unit_failed_store);
static struct attribute *zfcp_unit_attrs[] = {
&dev_attr_failed.attr,
&dev_attr_in_recovery.attr,
&dev_attr_status.attr,
&dev_attr_access_denied.attr,
&dev_attr_access_shared.attr,
&dev_attr_access_readonly.attr,
NULL
};
static struct attribute_group zfcp_unit_attr_group = {
.attrs = zfcp_unit_attrs,
};
/**
* zfcp_sysfs_create_unit_files - create sysfs unit files
* @dev: pointer to belonging device
*
* Create all attributes of the sysfs representation of a unit.
*/
int
zfcp_sysfs_unit_create_files(struct device *dev)
{
return sysfs_create_group(&dev->kobj, &zfcp_unit_attr_group);
}
/**
* zfcp_sysfs_remove_unit_files - remove sysfs unit files
* @dev: pointer to belonging device
*
* Remove all attributes of the sysfs representation of a unit.
*/
void
zfcp_sysfs_unit_remove_files(struct device *dev)
{
sysfs_remove_group(&dev->kobj, &zfcp_unit_attr_group);
}
#undef ZFCP_LOG_AREA

View File

@ -888,6 +888,25 @@ config SCSI_IBMVSCSIS
To compile this driver as a module, choose M here: the
module will be called ibmvstgt.
config SCSI_IBMVFC
tristate "IBM Virtual FC support"
depends on PPC_PSERIES && SCSI
select SCSI_FC_ATTRS
help
This is the IBM POWER Virtual FC Client
To compile this driver as a module, choose M here: the
module will be called ibmvfc.
config SCSI_IBMVFC_TRACE
bool "enable driver internal trace"
depends on SCSI_IBMVFC
default y
help
If you say Y here, the driver will trace all commands issued
to the adapter. Performance impact is minimal. Trace can be
dumped using /sys/class/scsi_host/hostXX/trace.
config SCSI_INITIO
tristate "Initio 9100U(W) support"
depends on PCI && SCSI
@ -1738,10 +1757,12 @@ config SCSI_SUNESP
select SCSI_SPI_ATTRS
help
This is the driver for the Sun ESP SCSI host adapter. The ESP
chipset is present in most SPARC SBUS-based computers.
chipset is present in most SPARC SBUS-based computers and
supports the Emulex family of ESP SCSI chips (esp100, esp100A,
esp236, fas101, fas236) as well as the Qlogic fas366 SCSI chip.
To compile this driver as a module, choose M here: the
module will be called esp.
module will be called sun_esp.
config ZFCP
tristate "FCP host bus adapter driver for IBM eServer zSeries"
@ -1771,4 +1792,6 @@ endif # SCSI_LOWLEVEL
source "drivers/scsi/pcmcia/Kconfig"
source "drivers/scsi/device_handler/Kconfig"
endmenu

View File

@ -34,6 +34,7 @@ obj-$(CONFIG_SCSI_ISCSI_ATTRS) += scsi_transport_iscsi.o
obj-$(CONFIG_SCSI_SAS_ATTRS) += scsi_transport_sas.o
obj-$(CONFIG_SCSI_SAS_LIBSAS) += libsas/
obj-$(CONFIG_SCSI_SRP_ATTRS) += scsi_transport_srp.o
obj-$(CONFIG_SCSI_DH) += device_handler/
obj-$(CONFIG_ISCSI_TCP) += libiscsi.o iscsi_tcp.o
obj-$(CONFIG_INFINIBAND_ISER) += libiscsi.o
@ -118,6 +119,7 @@ obj-$(CONFIG_SCSI_IPR) += ipr.o
obj-$(CONFIG_SCSI_SRP) += libsrp.o
obj-$(CONFIG_SCSI_IBMVSCSI) += ibmvscsi/
obj-$(CONFIG_SCSI_IBMVSCSIS) += ibmvscsi/
obj-$(CONFIG_SCSI_IBMVFC) += ibmvscsi/
obj-$(CONFIG_SCSI_HPTIOP) += hptiop.o
obj-$(CONFIG_SCSI_STEX) += stex.o
obj-$(CONFIG_SCSI_MVSAS) += mvsas.o

View File

@ -41,6 +41,7 @@
#include <linux/kthread.h>
#include <linux/semaphore.h>
#include <asm/uaccess.h>
#include <scsi/scsi_host.h>
#include "aacraid.h"
@ -581,6 +582,14 @@ static int aac_send_raw_srb(struct aac_dev* dev, void __user * arg)
for (i = 0; i < upsg->count; i++) {
u64 addr;
void* p;
if (upsg->sg[i].count >
(dev->adapter_info.options &
AAC_OPT_NEW_COMM) ?
(dev->scsi_host_ptr->max_sectors << 9) :
65536) {
rcode = -EINVAL;
goto cleanup;
}
/* Does this really need to be GFP_DMA? */
p = kmalloc(upsg->sg[i].count,GFP_KERNEL|__GFP_DMA);
if(!p) {
@ -625,6 +634,14 @@ static int aac_send_raw_srb(struct aac_dev* dev, void __user * arg)
for (i = 0; i < usg->count; i++) {
u64 addr;
void* p;
if (usg->sg[i].count >
(dev->adapter_info.options &
AAC_OPT_NEW_COMM) ?
(dev->scsi_host_ptr->max_sectors << 9) :
65536) {
rcode = -EINVAL;
goto cleanup;
}
/* Does this really need to be GFP_DMA? */
p = kmalloc(usg->sg[i].count,GFP_KERNEL|__GFP_DMA);
if(!p) {
@ -667,6 +684,14 @@ static int aac_send_raw_srb(struct aac_dev* dev, void __user * arg)
for (i = 0; i < upsg->count; i++) {
uintptr_t addr;
void* p;
if (usg->sg[i].count >
(dev->adapter_info.options &
AAC_OPT_NEW_COMM) ?
(dev->scsi_host_ptr->max_sectors << 9) :
65536) {
rcode = -EINVAL;
goto cleanup;
}
/* Does this really need to be GFP_DMA? */
p = kmalloc(usg->sg[i].count,GFP_KERNEL|__GFP_DMA);
if(!p) {
@ -698,6 +723,14 @@ static int aac_send_raw_srb(struct aac_dev* dev, void __user * arg)
for (i = 0; i < upsg->count; i++) {
dma_addr_t addr;
void* p;
if (upsg->sg[i].count >
(dev->adapter_info.options &
AAC_OPT_NEW_COMM) ?
(dev->scsi_host_ptr->max_sectors << 9) :
65536) {
rcode = -EINVAL;
goto cleanup;
}
p = kmalloc(upsg->sg[i].count, GFP_KERNEL);
if (!p) {
dprintk((KERN_DEBUG"aacraid: Could not allocate SG buffer - size = %d buffer number %d of %d\n",

View File

@ -865,7 +865,7 @@ static ssize_t aac_show_bios_version(struct device *device,
return len;
}
ssize_t aac_show_serial_number(struct device *device,
static ssize_t aac_show_serial_number(struct device *device,
struct device_attribute *attr, char *buf)
{
struct aac_dev *dev = (struct aac_dev*)class_to_shost(device)->hostdata;

View File

@ -0,0 +1,32 @@
#
# SCSI Device Handler configuration
#
menuconfig SCSI_DH
tristate "SCSI Device Handlers"
depends on SCSI
default n
help
SCSI Device Handlers provide device specific support for
devices utilized in multipath configurations. Say Y here to
select support for specific hardware.
config SCSI_DH_RDAC
tristate "LSI RDAC Device Handler"
depends on SCSI_DH
help
If you have a LSI RDAC select y. Otherwise, say N.
config SCSI_DH_HP_SW
tristate "HP/COMPAQ MSA Device Handler"
depends on SCSI_DH
help
If you have a HP/COMPAQ MSA device that requires START_STOP to
be sent to start it and cannot upgrade the firmware then select y.
Otherwise, say N.
config SCSI_DH_EMC
tristate "EMC CLARiiON Device Handler"
depends on SCSI_DH
help
If you have a EMC CLARiiON select y. Otherwise, say N.

View File

@ -0,0 +1,7 @@
#
# SCSI Device Handler
#
obj-$(CONFIG_SCSI_DH) += scsi_dh.o
obj-$(CONFIG_SCSI_DH_RDAC) += scsi_dh_rdac.o
obj-$(CONFIG_SCSI_DH_HP_SW) += scsi_dh_hp_sw.o
obj-$(CONFIG_SCSI_DH_EMC) += scsi_dh_emc.o

View File

@ -0,0 +1,162 @@
/*
* SCSI device handler infrastruture.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* Copyright IBM Corporation, 2007
* Authors:
* Chandra Seetharaman <sekharan@us.ibm.com>
* Mike Anderson <andmike@linux.vnet.ibm.com>
*/
#include <scsi/scsi_dh.h>
#include "../scsi_priv.h"
static DEFINE_SPINLOCK(list_lock);
static LIST_HEAD(scsi_dh_list);
static struct scsi_device_handler *get_device_handler(const char *name)
{
struct scsi_device_handler *tmp, *found = NULL;
spin_lock(&list_lock);
list_for_each_entry(tmp, &scsi_dh_list, list) {
if (!strcmp(tmp->name, name)) {
found = tmp;
break;
}
}
spin_unlock(&list_lock);
return found;
}
static int scsi_dh_notifier_add(struct device *dev, void *data)
{
struct scsi_device_handler *scsi_dh = data;
scsi_dh->nb.notifier_call(&scsi_dh->nb, BUS_NOTIFY_ADD_DEVICE, dev);
return 0;
}
/*
* scsi_register_device_handler - register a device handler personality
* module.
* @scsi_dh - device handler to be registered.
*
* Returns 0 on success, -EBUSY if handler already registered.
*/
int scsi_register_device_handler(struct scsi_device_handler *scsi_dh)
{
int ret = -EBUSY;
struct scsi_device_handler *tmp;
tmp = get_device_handler(scsi_dh->name);
if (tmp)
goto done;
ret = bus_register_notifier(&scsi_bus_type, &scsi_dh->nb);
bus_for_each_dev(&scsi_bus_type, NULL, scsi_dh, scsi_dh_notifier_add);
spin_lock(&list_lock);
list_add(&scsi_dh->list, &scsi_dh_list);
spin_unlock(&list_lock);
done:
return ret;
}
EXPORT_SYMBOL_GPL(scsi_register_device_handler);
static int scsi_dh_notifier_remove(struct device *dev, void *data)
{
struct scsi_device_handler *scsi_dh = data;
scsi_dh->nb.notifier_call(&scsi_dh->nb, BUS_NOTIFY_DEL_DEVICE, dev);
return 0;
}
/*
* scsi_unregister_device_handler - register a device handler personality
* module.
* @scsi_dh - device handler to be unregistered.
*
* Returns 0 on success, -ENODEV if handler not registered.
*/
int scsi_unregister_device_handler(struct scsi_device_handler *scsi_dh)
{
int ret = -ENODEV;
struct scsi_device_handler *tmp;
tmp = get_device_handler(scsi_dh->name);
if (!tmp)
goto done;
ret = bus_unregister_notifier(&scsi_bus_type, &scsi_dh->nb);
bus_for_each_dev(&scsi_bus_type, NULL, scsi_dh,
scsi_dh_notifier_remove);
spin_lock(&list_lock);
list_del(&scsi_dh->list);
spin_unlock(&list_lock);
done:
return ret;
}
EXPORT_SYMBOL_GPL(scsi_unregister_device_handler);
/*
* scsi_dh_activate - activate the path associated with the scsi_device
* corresponding to the given request queue.
* @q - Request queue that is associated with the scsi_device to be
* activated.
*/
int scsi_dh_activate(struct request_queue *q)
{
int err = 0;
unsigned long flags;
struct scsi_device *sdev;
struct scsi_device_handler *scsi_dh = NULL;
spin_lock_irqsave(q->queue_lock, flags);
sdev = q->queuedata;
if (sdev && sdev->scsi_dh_data)
scsi_dh = sdev->scsi_dh_data->scsi_dh;
if (!scsi_dh || !get_device(&sdev->sdev_gendev))
err = SCSI_DH_NOSYS;
spin_unlock_irqrestore(q->queue_lock, flags);
if (err)
return err;
if (scsi_dh->activate)
err = scsi_dh->activate(sdev);
put_device(&sdev->sdev_gendev);
return err;
}
EXPORT_SYMBOL_GPL(scsi_dh_activate);
/*
* scsi_dh_handler_exist - Return TRUE(1) if a device handler exists for
* the given name. FALSE(0) otherwise.
* @name - name of the device handler.
*/
int scsi_dh_handler_exist(const char *name)
{
return (get_device_handler(name) != NULL);
}
EXPORT_SYMBOL_GPL(scsi_dh_handler_exist);
MODULE_DESCRIPTION("SCSI device handler");
MODULE_AUTHOR("Chandra Seetharaman <sekharan@us.ibm.com>");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,499 @@
/*
* Target driver for EMC CLARiiON AX/CX-series hardware.
* Based on code from Lars Marowsky-Bree <lmb@suse.de>
* and Ed Goggin <egoggin@emc.com>.
*
* Copyright (C) 2006 Red Hat, Inc. All rights reserved.
* Copyright (C) 2006 Mike Christie
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <scsi/scsi.h>
#include <scsi/scsi_eh.h>
#include <scsi/scsi_dh.h>
#include <scsi/scsi_device.h>
#define CLARIION_NAME "emc_clariion"
#define CLARIION_TRESPASS_PAGE 0x22
#define CLARIION_BUFFER_SIZE 0x80
#define CLARIION_TIMEOUT (60 * HZ)
#define CLARIION_RETRIES 3
#define CLARIION_UNBOUND_LU -1
static unsigned char long_trespass[] = {
0, 0, 0, 0,
CLARIION_TRESPASS_PAGE, /* Page code */
0x09, /* Page length - 2 */
0x81, /* Trespass code + Honor reservation bit */
0xff, 0xff, /* Trespass target */
0, 0, 0, 0, 0, 0 /* Reserved bytes / unknown */
};
static unsigned char long_trespass_hr[] = {
0, 0, 0, 0,
CLARIION_TRESPASS_PAGE, /* Page code */
0x09, /* Page length - 2 */
0x01, /* Trespass code + Honor reservation bit */
0xff, 0xff, /* Trespass target */
0, 0, 0, 0, 0, 0 /* Reserved bytes / unknown */
};
static unsigned char short_trespass[] = {
0, 0, 0, 0,
CLARIION_TRESPASS_PAGE, /* Page code */
0x02, /* Page length - 2 */
0x81, /* Trespass code + Honor reservation bit */
0xff, /* Trespass target */
};
static unsigned char short_trespass_hr[] = {
0, 0, 0, 0,
CLARIION_TRESPASS_PAGE, /* Page code */
0x02, /* Page length - 2 */
0x01, /* Trespass code + Honor reservation bit */
0xff, /* Trespass target */
};
struct clariion_dh_data {
/*
* Use short trespass command (FC-series) or the long version
* (default for AX/CX CLARiiON arrays).
*/
unsigned short_trespass;
/*
* Whether or not (default) to honor SCSI reservations when
* initiating a switch-over.
*/
unsigned hr;
/* I/O buffer for both MODE_SELECT and INQUIRY commands. */
char buffer[CLARIION_BUFFER_SIZE];
/*
* SCSI sense buffer for commands -- assumes serial issuance
* and completion sequence of all commands for same multipath.
*/
unsigned char sense[SCSI_SENSE_BUFFERSIZE];
/* which SP (A=0,B=1,UNBOUND=-1) is dflt SP for path's mapped dev */
int default_sp;
/* which SP (A=0,B=1,UNBOUND=-1) is active for path's mapped dev */
int current_sp;
};
static inline struct clariion_dh_data
*get_clariion_data(struct scsi_device *sdev)
{
struct scsi_dh_data *scsi_dh_data = sdev->scsi_dh_data;
BUG_ON(scsi_dh_data == NULL);
return ((struct clariion_dh_data *) scsi_dh_data->buf);
}
/*
* Parse MODE_SELECT cmd reply.
*/
static int trespass_endio(struct scsi_device *sdev, int result)
{
int err = SCSI_DH_OK;
struct scsi_sense_hdr sshdr;
struct clariion_dh_data *csdev = get_clariion_data(sdev);
char *sense = csdev->sense;
if (status_byte(result) == CHECK_CONDITION &&
scsi_normalize_sense(sense, SCSI_SENSE_BUFFERSIZE, &sshdr)) {
sdev_printk(KERN_ERR, sdev, "Found valid sense data 0x%2x, "
"0x%2x, 0x%2x while sending CLARiiON trespass "
"command.\n", sshdr.sense_key, sshdr.asc,
sshdr.ascq);
if ((sshdr.sense_key == 0x05) && (sshdr.asc == 0x04) &&
(sshdr.ascq == 0x00)) {
/*
* Array based copy in progress -- do not send
* mode_select or copy will be aborted mid-stream.
*/
sdev_printk(KERN_INFO, sdev, "Array Based Copy in "
"progress while sending CLARiiON trespass "
"command.\n");
err = SCSI_DH_DEV_TEMP_BUSY;
} else if ((sshdr.sense_key == 0x02) && (sshdr.asc == 0x04) &&
(sshdr.ascq == 0x03)) {
/*
* LUN Not Ready - Manual Intervention Required
* indicates in-progress ucode upgrade (NDU).
*/
sdev_printk(KERN_INFO, sdev, "Detected in-progress "
"ucode upgrade NDU operation while sending "
"CLARiiON trespass command.\n");
err = SCSI_DH_DEV_TEMP_BUSY;
} else
err = SCSI_DH_DEV_FAILED;
} else if (result) {
sdev_printk(KERN_ERR, sdev, "Error 0x%x while sending "
"CLARiiON trespass command.\n", result);
err = SCSI_DH_IO;
}
return err;
}
static int parse_sp_info_reply(struct scsi_device *sdev, int result,
int *default_sp, int *current_sp, int *new_current_sp)
{
int err = SCSI_DH_OK;
struct clariion_dh_data *csdev = get_clariion_data(sdev);
if (result == 0) {
/* check for in-progress ucode upgrade (NDU) */
if (csdev->buffer[48] != 0) {
sdev_printk(KERN_NOTICE, sdev, "Detected in-progress "
"ucode upgrade NDU operation while finding "
"current active SP.");
err = SCSI_DH_DEV_TEMP_BUSY;
} else {
*default_sp = csdev->buffer[5];
if (csdev->buffer[4] == 2)
/* SP for path is current */
*current_sp = csdev->buffer[8];
else {
if (csdev->buffer[4] == 1)
/* SP for this path is NOT current */
if (csdev->buffer[8] == 0)
*current_sp = 1;
else
*current_sp = 0;
else
/* unbound LU or LUNZ */
*current_sp = CLARIION_UNBOUND_LU;
}
*new_current_sp = csdev->buffer[8];
}
} else {
struct scsi_sense_hdr sshdr;
err = SCSI_DH_IO;
if (scsi_normalize_sense(csdev->sense, SCSI_SENSE_BUFFERSIZE,
&sshdr))
sdev_printk(KERN_ERR, sdev, "Found valid sense data "
"0x%2x, 0x%2x, 0x%2x while finding current "
"active SP.", sshdr.sense_key, sshdr.asc,
sshdr.ascq);
else
sdev_printk(KERN_ERR, sdev, "Error 0x%x finding "
"current active SP.", result);
}
return err;
}
static int sp_info_endio(struct scsi_device *sdev, int result,
int mode_select_sent, int *done)
{
struct clariion_dh_data *csdev = get_clariion_data(sdev);
int err_flags, default_sp, current_sp, new_current_sp;
err_flags = parse_sp_info_reply(sdev, result, &default_sp,
&current_sp, &new_current_sp);
if (err_flags != SCSI_DH_OK)
goto done;
if (mode_select_sent) {
csdev->default_sp = default_sp;
csdev->current_sp = current_sp;
} else {
/*
* Issue the actual module_selec request IFF either
* (1) we do not know the identity of the current SP OR
* (2) what we think we know is actually correct.
*/
if ((current_sp != CLARIION_UNBOUND_LU) &&
(new_current_sp != current_sp)) {
csdev->default_sp = default_sp;
csdev->current_sp = current_sp;
sdev_printk(KERN_INFO, sdev, "Ignoring path group "
"switch-over command for CLARiiON SP%s since "
" mapped device is already initialized.",
current_sp ? "B" : "A");
if (done)
*done = 1; /* as good as doing it */
}
}
done:
return err_flags;
}
/*
* Get block request for REQ_BLOCK_PC command issued to path. Currently
* limited to MODE_SELECT (trespass) and INQUIRY (VPD page 0xC0) commands.
*
* Uses data and sense buffers in hardware handler context structure and
* assumes serial servicing of commands, both issuance and completion.
*/
static struct request *get_req(struct scsi_device *sdev, int cmd)
{
struct clariion_dh_data *csdev = get_clariion_data(sdev);
struct request *rq;
unsigned char *page22;
int len = 0;
rq = blk_get_request(sdev->request_queue,
(cmd == MODE_SELECT) ? WRITE : READ, GFP_ATOMIC);
if (!rq) {
sdev_printk(KERN_INFO, sdev, "get_req: blk_get_request failed");
return NULL;
}
memset(&rq->cmd, 0, BLK_MAX_CDB);
rq->cmd[0] = cmd;
rq->cmd_len = COMMAND_SIZE(rq->cmd[0]);
switch (cmd) {
case MODE_SELECT:
if (csdev->short_trespass) {
page22 = csdev->hr ? short_trespass_hr : short_trespass;
len = sizeof(short_trespass);
} else {
page22 = csdev->hr ? long_trespass_hr : long_trespass;
len = sizeof(long_trespass);
}
/*
* Can't DMA from kernel BSS -- must copy selected trespass
* command mode page contents to context buffer which is
* allocated by kmalloc.
*/
BUG_ON((len > CLARIION_BUFFER_SIZE));
memcpy(csdev->buffer, page22, len);
rq->cmd_flags |= REQ_RW;
rq->cmd[1] = 0x10;
break;
case INQUIRY:
rq->cmd[1] = 0x1;
rq->cmd[2] = 0xC0;
len = CLARIION_BUFFER_SIZE;
memset(csdev->buffer, 0, CLARIION_BUFFER_SIZE);
break;
default:
BUG_ON(1);
break;
}
rq->cmd[4] = len;
rq->cmd_type = REQ_TYPE_BLOCK_PC;
rq->cmd_flags |= REQ_FAILFAST;
rq->timeout = CLARIION_TIMEOUT;
rq->retries = CLARIION_RETRIES;
rq->sense = csdev->sense;
memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE);
rq->sense_len = 0;
if (blk_rq_map_kern(sdev->request_queue, rq, csdev->buffer,
len, GFP_ATOMIC)) {
__blk_put_request(rq->q, rq);
return NULL;
}
return rq;
}
static int send_cmd(struct scsi_device *sdev, int cmd)
{
struct request *rq = get_req(sdev, cmd);
if (!rq)
return SCSI_DH_RES_TEMP_UNAVAIL;
return blk_execute_rq(sdev->request_queue, NULL, rq, 1);
}
static int clariion_activate(struct scsi_device *sdev)
{
int result, done = 0;
result = send_cmd(sdev, INQUIRY);
result = sp_info_endio(sdev, result, 0, &done);
if (result || done)
goto done;
result = send_cmd(sdev, MODE_SELECT);
result = trespass_endio(sdev, result);
if (result)
goto done;
result = send_cmd(sdev, INQUIRY);
result = sp_info_endio(sdev, result, 1, NULL);
done:
return result;
}
static int clariion_check_sense(struct scsi_device *sdev,
struct scsi_sense_hdr *sense_hdr)
{
switch (sense_hdr->sense_key) {
case NOT_READY:
if (sense_hdr->asc == 0x04 && sense_hdr->ascq == 0x03)
/*
* 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.
*
* Can return FAILED only when we want the error
* recovery process to kick in.
*/
return SUCCESS;
break;
case ILLEGAL_REQUEST:
if (sense_hdr->asc == 0x25 && sense_hdr->ascq == 0x01)
/*
* 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.)
*
* Can return FAILED only when we want the error
* recovery process to kick in.
*/
return SUCCESS;
break;
case UNIT_ATTENTION:
if (sense_hdr->asc == 0x29 && sense_hdr->ascq == 0x00)
/*
* Unit Attention Code. This is the first IO
* to the new path, so just retry.
*/
return NEEDS_RETRY;
break;
}
/* success just means we do not care what scsi-ml does */
return SUCCESS;
}
static const struct {
char *vendor;
char *model;
} clariion_dev_list[] = {
{"DGC", "RAID"},
{"DGC", "DISK"},
{NULL, NULL},
};
static int clariion_bus_notify(struct notifier_block *, unsigned long, void *);
static struct scsi_device_handler clariion_dh = {
.name = CLARIION_NAME,
.module = THIS_MODULE,
.nb.notifier_call = clariion_bus_notify,
.check_sense = clariion_check_sense,
.activate = clariion_activate,
};
/*
* TODO: need some interface so we can set trespass values
*/
static int clariion_bus_notify(struct notifier_block *nb,
unsigned long action, void *data)
{
struct device *dev = data;
struct scsi_device *sdev = to_scsi_device(dev);
struct scsi_dh_data *scsi_dh_data;
struct clariion_dh_data *h;
int i, found = 0;
unsigned long flags;
if (action == BUS_NOTIFY_ADD_DEVICE) {
for (i = 0; clariion_dev_list[i].vendor; i++) {
if (!strncmp(sdev->vendor, clariion_dev_list[i].vendor,
strlen(clariion_dev_list[i].vendor)) &&
!strncmp(sdev->model, clariion_dev_list[i].model,
strlen(clariion_dev_list[i].model))) {
found = 1;
break;
}
}
if (!found)
goto out;
scsi_dh_data = kzalloc(sizeof(struct scsi_device_handler *)
+ sizeof(*h) , GFP_KERNEL);
if (!scsi_dh_data) {
sdev_printk(KERN_ERR, sdev, "Attach failed %s.\n",
CLARIION_NAME);
goto out;
}
scsi_dh_data->scsi_dh = &clariion_dh;
h = (struct clariion_dh_data *) scsi_dh_data->buf;
h->default_sp = CLARIION_UNBOUND_LU;
h->current_sp = CLARIION_UNBOUND_LU;
spin_lock_irqsave(sdev->request_queue->queue_lock, flags);
sdev->scsi_dh_data = scsi_dh_data;
spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags);
sdev_printk(KERN_NOTICE, sdev, "Attached %s.\n", CLARIION_NAME);
try_module_get(THIS_MODULE);
} else if (action == BUS_NOTIFY_DEL_DEVICE) {
if (sdev->scsi_dh_data == NULL ||
sdev->scsi_dh_data->scsi_dh != &clariion_dh)
goto out;
spin_lock_irqsave(sdev->request_queue->queue_lock, flags);
scsi_dh_data = sdev->scsi_dh_data;
sdev->scsi_dh_data = NULL;
spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags);
sdev_printk(KERN_NOTICE, sdev, "Dettached %s.\n",
CLARIION_NAME);
kfree(scsi_dh_data);
module_put(THIS_MODULE);
}
out:
return 0;
}
static int __init clariion_init(void)
{
int r;
r = scsi_register_device_handler(&clariion_dh);
if (r != 0)
printk(KERN_ERR "Failed to register scsi device handler.");
return r;
}
static void __exit clariion_exit(void)
{
scsi_unregister_device_handler(&clariion_dh);
}
module_init(clariion_init);
module_exit(clariion_exit);
MODULE_DESCRIPTION("EMC CX/AX/FC-family driver");
MODULE_AUTHOR("Mike Christie <michaelc@cs.wisc.edu>, Chandra Seetharaman <sekharan@us.ibm.com>");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,202 @@
/*
* Basic HP/COMPAQ MSA 1000 support. This is only needed if your HW cannot be
* upgraded.
*
* Copyright (C) 2006 Red Hat, Inc. All rights reserved.
* Copyright (C) 2006 Mike Christie
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <scsi/scsi.h>
#include <scsi/scsi_dbg.h>
#include <scsi/scsi_eh.h>
#include <scsi/scsi_dh.h>
#define HP_SW_NAME "hp_sw"
#define HP_SW_TIMEOUT (60 * HZ)
#define HP_SW_RETRIES 3
struct hp_sw_dh_data {
unsigned char sense[SCSI_SENSE_BUFFERSIZE];
int retries;
};
static inline struct hp_sw_dh_data *get_hp_sw_data(struct scsi_device *sdev)
{
struct scsi_dh_data *scsi_dh_data = sdev->scsi_dh_data;
BUG_ON(scsi_dh_data == NULL);
return ((struct hp_sw_dh_data *) scsi_dh_data->buf);
}
static int hp_sw_done(struct scsi_device *sdev)
{
struct hp_sw_dh_data *h = get_hp_sw_data(sdev);
struct scsi_sense_hdr sshdr;
int rc;
sdev_printk(KERN_INFO, sdev, "hp_sw_done\n");
rc = scsi_normalize_sense(h->sense, SCSI_SENSE_BUFFERSIZE, &sshdr);
if (!rc)
goto done;
switch (sshdr.sense_key) {
case NOT_READY:
if ((sshdr.asc == 0x04) && (sshdr.ascq == 3)) {
rc = SCSI_DH_RETRY;
h->retries++;
break;
}
/* fall through */
default:
h->retries++;
rc = SCSI_DH_IMM_RETRY;
}
done:
if (rc == SCSI_DH_OK || rc == SCSI_DH_IO)
h->retries = 0;
else if (h->retries > HP_SW_RETRIES) {
h->retries = 0;
rc = SCSI_DH_IO;
}
return rc;
}
static int hp_sw_activate(struct scsi_device *sdev)
{
struct hp_sw_dh_data *h = get_hp_sw_data(sdev);
struct request *req;
int ret = SCSI_DH_RES_TEMP_UNAVAIL;
req = blk_get_request(sdev->request_queue, WRITE, GFP_ATOMIC);
if (!req)
goto done;
sdev_printk(KERN_INFO, sdev, "sending START_STOP.");
req->cmd_type = REQ_TYPE_BLOCK_PC;
req->cmd_flags |= REQ_FAILFAST;
req->cmd_len = COMMAND_SIZE(START_STOP);
memset(req->cmd, 0, MAX_COMMAND_SIZE);
req->cmd[0] = START_STOP;
req->cmd[4] = 1; /* Start spin cycle */
req->timeout = HP_SW_TIMEOUT;
req->sense = h->sense;
memset(req->sense, 0, SCSI_SENSE_BUFFERSIZE);
req->sense_len = 0;
ret = blk_execute_rq(req->q, NULL, req, 1);
if (!ret) /* SUCCESS */
ret = hp_sw_done(sdev);
else
ret = SCSI_DH_IO;
done:
return ret;
}
static const struct {
char *vendor;
char *model;
} hp_sw_dh_data_list[] = {
{"COMPAQ", "MSA"},
{"HP", "HSV"},
{"DEC", "HSG80"},
{NULL, NULL},
};
static int hp_sw_bus_notify(struct notifier_block *, unsigned long, void *);
static struct scsi_device_handler hp_sw_dh = {
.name = HP_SW_NAME,
.module = THIS_MODULE,
.nb.notifier_call = hp_sw_bus_notify,
.activate = hp_sw_activate,
};
static int hp_sw_bus_notify(struct notifier_block *nb,
unsigned long action, void *data)
{
struct device *dev = data;
struct scsi_device *sdev = to_scsi_device(dev);
struct scsi_dh_data *scsi_dh_data;
int i, found = 0;
unsigned long flags;
if (action == BUS_NOTIFY_ADD_DEVICE) {
for (i = 0; hp_sw_dh_data_list[i].vendor; i++) {
if (!strncmp(sdev->vendor, hp_sw_dh_data_list[i].vendor,
strlen(hp_sw_dh_data_list[i].vendor)) &&
!strncmp(sdev->model, hp_sw_dh_data_list[i].model,
strlen(hp_sw_dh_data_list[i].model))) {
found = 1;
break;
}
}
if (!found)
goto out;
scsi_dh_data = kzalloc(sizeof(struct scsi_device_handler *)
+ sizeof(struct hp_sw_dh_data) , GFP_KERNEL);
if (!scsi_dh_data) {
sdev_printk(KERN_ERR, sdev, "Attach Failed %s.\n",
HP_SW_NAME);
goto out;
}
scsi_dh_data->scsi_dh = &hp_sw_dh;
spin_lock_irqsave(sdev->request_queue->queue_lock, flags);
sdev->scsi_dh_data = scsi_dh_data;
spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags);
try_module_get(THIS_MODULE);
sdev_printk(KERN_NOTICE, sdev, "Attached %s.\n", HP_SW_NAME);
} else if (action == BUS_NOTIFY_DEL_DEVICE) {
if (sdev->scsi_dh_data == NULL ||
sdev->scsi_dh_data->scsi_dh != &hp_sw_dh)
goto out;
spin_lock_irqsave(sdev->request_queue->queue_lock, flags);
scsi_dh_data = sdev->scsi_dh_data;
sdev->scsi_dh_data = NULL;
spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags);
module_put(THIS_MODULE);
sdev_printk(KERN_NOTICE, sdev, "Dettached %s.\n", HP_SW_NAME);
kfree(scsi_dh_data);
}
out:
return 0;
}
static int __init hp_sw_init(void)
{
return scsi_register_device_handler(&hp_sw_dh);
}
static void __exit hp_sw_exit(void)
{
scsi_unregister_device_handler(&hp_sw_dh);
}
module_init(hp_sw_init);
module_exit(hp_sw_exit);
MODULE_DESCRIPTION("HP MSA 1000");
MODULE_AUTHOR("Mike Christie <michaelc@cs.wisc.edu");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,691 @@
/*
* Engenio/LSI RDAC SCSI Device Handler
*
* Copyright (C) 2005 Mike Christie. All rights reserved.
* Copyright (C) Chandra Seetharaman, IBM Corp. 2007
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
*/
#include <scsi/scsi.h>
#include <scsi/scsi_eh.h>
#include <scsi/scsi_dh.h>
#define RDAC_NAME "rdac"
/*
* LSI mode page stuff
*
* These struct definitions and the forming of the
* mode page were taken from the LSI RDAC 2.4 GPL'd
* driver, and then converted to Linux conventions.
*/
#define RDAC_QUIESCENCE_TIME 20;
/*
* Page Codes
*/
#define RDAC_PAGE_CODE_REDUNDANT_CONTROLLER 0x2c
/*
* Controller modes definitions
*/
#define RDAC_MODE_TRANSFER_SPECIFIED_LUNS 0x02
/*
* RDAC Options field
*/
#define RDAC_FORCED_QUIESENCE 0x02
#define RDAC_TIMEOUT (60 * HZ)
#define RDAC_RETRIES 3
struct rdac_mode_6_hdr {
u8 data_len;
u8 medium_type;
u8 device_params;
u8 block_desc_len;
};
struct rdac_mode_10_hdr {
u16 data_len;
u8 medium_type;
u8 device_params;
u16 reserved;
u16 block_desc_len;
};
struct rdac_mode_common {
u8 controller_serial[16];
u8 alt_controller_serial[16];
u8 rdac_mode[2];
u8 alt_rdac_mode[2];
u8 quiescence_timeout;
u8 rdac_options;
};
struct rdac_pg_legacy {
struct rdac_mode_6_hdr hdr;
u8 page_code;
u8 page_len;
struct rdac_mode_common common;
#define MODE6_MAX_LUN 32
u8 lun_table[MODE6_MAX_LUN];
u8 reserved2[32];
u8 reserved3;
u8 reserved4;
};
struct rdac_pg_expanded {
struct rdac_mode_10_hdr hdr;
u8 page_code;
u8 subpage_code;
u8 page_len[2];
struct rdac_mode_common common;
u8 lun_table[256];
u8 reserved3;
u8 reserved4;
};
struct c9_inquiry {
u8 peripheral_info;
u8 page_code; /* 0xC9 */
u8 reserved1;
u8 page_len;
u8 page_id[4]; /* "vace" */
u8 avte_cvp;
u8 path_prio;
u8 reserved2[38];
};
#define SUBSYS_ID_LEN 16
#define SLOT_ID_LEN 2
struct c4_inquiry {
u8 peripheral_info;
u8 page_code; /* 0xC4 */
u8 reserved1;
u8 page_len;
u8 page_id[4]; /* "subs" */
u8 subsys_id[SUBSYS_ID_LEN];
u8 revision[4];
u8 slot_id[SLOT_ID_LEN];
u8 reserved[2];
};
struct rdac_controller {
u8 subsys_id[SUBSYS_ID_LEN];
u8 slot_id[SLOT_ID_LEN];
int use_ms10;
struct kref kref;
struct list_head node; /* list of all controllers */
union {
struct rdac_pg_legacy legacy;
struct rdac_pg_expanded expanded;
} mode_select;
};
struct c8_inquiry {
u8 peripheral_info;
u8 page_code; /* 0xC8 */
u8 reserved1;
u8 page_len;
u8 page_id[4]; /* "edid" */
u8 reserved2[3];
u8 vol_uniq_id_len;
u8 vol_uniq_id[16];
u8 vol_user_label_len;
u8 vol_user_label[60];
u8 array_uniq_id_len;
u8 array_unique_id[16];
u8 array_user_label_len;
u8 array_user_label[60];
u8 lun[8];
};
struct c2_inquiry {
u8 peripheral_info;
u8 page_code; /* 0xC2 */
u8 reserved1;
u8 page_len;
u8 page_id[4]; /* "swr4" */
u8 sw_version[3];
u8 sw_date[3];
u8 features_enabled;
u8 max_lun_supported;
u8 partitions[239]; /* Total allocation length should be 0xFF */
};
struct rdac_dh_data {
struct rdac_controller *ctlr;
#define UNINITIALIZED_LUN (1 << 8)
unsigned lun;
#define RDAC_STATE_ACTIVE 0
#define RDAC_STATE_PASSIVE 1
unsigned char state;
unsigned char sense[SCSI_SENSE_BUFFERSIZE];
union {
struct c2_inquiry c2;
struct c4_inquiry c4;
struct c8_inquiry c8;
struct c9_inquiry c9;
} inq;
};
static LIST_HEAD(ctlr_list);
static DEFINE_SPINLOCK(list_lock);
static inline struct rdac_dh_data *get_rdac_data(struct scsi_device *sdev)
{
struct scsi_dh_data *scsi_dh_data = sdev->scsi_dh_data;
BUG_ON(scsi_dh_data == NULL);
return ((struct rdac_dh_data *) scsi_dh_data->buf);
}
static struct request *get_rdac_req(struct scsi_device *sdev,
void *buffer, unsigned buflen, int rw)
{
struct request *rq;
struct request_queue *q = sdev->request_queue;
struct rdac_dh_data *h = get_rdac_data(sdev);
rq = blk_get_request(q, rw, GFP_KERNEL);
if (!rq) {
sdev_printk(KERN_INFO, sdev,
"get_rdac_req: blk_get_request failed.\n");
return NULL;
}
if (buflen && blk_rq_map_kern(q, rq, buffer, buflen, GFP_KERNEL)) {
blk_put_request(rq);
sdev_printk(KERN_INFO, sdev,
"get_rdac_req: blk_rq_map_kern failed.\n");
return NULL;
}
memset(&rq->cmd, 0, BLK_MAX_CDB);
rq->sense = h->sense;
memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE);
rq->sense_len = 0;
rq->cmd_type = REQ_TYPE_BLOCK_PC;
rq->cmd_flags |= REQ_FAILFAST | REQ_NOMERGE;
rq->retries = RDAC_RETRIES;
rq->timeout = RDAC_TIMEOUT;
return rq;
}
static struct request *rdac_failover_get(struct scsi_device *sdev)
{
struct request *rq;
struct rdac_mode_common *common;
unsigned data_size;
struct rdac_dh_data *h = get_rdac_data(sdev);
if (h->ctlr->use_ms10) {
struct rdac_pg_expanded *rdac_pg;
data_size = sizeof(struct rdac_pg_expanded);
rdac_pg = &h->ctlr->mode_select.expanded;
memset(rdac_pg, 0, data_size);
common = &rdac_pg->common;
rdac_pg->page_code = RDAC_PAGE_CODE_REDUNDANT_CONTROLLER + 0x40;
rdac_pg->subpage_code = 0x1;
rdac_pg->page_len[0] = 0x01;
rdac_pg->page_len[1] = 0x28;
rdac_pg->lun_table[h->lun] = 0x81;
} else {
struct rdac_pg_legacy *rdac_pg;
data_size = sizeof(struct rdac_pg_legacy);
rdac_pg = &h->ctlr->mode_select.legacy;
memset(rdac_pg, 0, data_size);
common = &rdac_pg->common;
rdac_pg->page_code = RDAC_PAGE_CODE_REDUNDANT_CONTROLLER;
rdac_pg->page_len = 0x68;
rdac_pg->lun_table[h->lun] = 0x81;
}
common->rdac_mode[1] = RDAC_MODE_TRANSFER_SPECIFIED_LUNS;
common->quiescence_timeout = RDAC_QUIESCENCE_TIME;
common->rdac_options = RDAC_FORCED_QUIESENCE;
/* get request for block layer packet command */
rq = get_rdac_req(sdev, &h->ctlr->mode_select, data_size, WRITE);
if (!rq)
return NULL;
/* Prepare the command. */
if (h->ctlr->use_ms10) {
rq->cmd[0] = MODE_SELECT_10;
rq->cmd[7] = data_size >> 8;
rq->cmd[8] = data_size & 0xff;
} else {
rq->cmd[0] = MODE_SELECT;
rq->cmd[4] = data_size;
}
rq->cmd_len = COMMAND_SIZE(rq->cmd[0]);
return rq;
}
static void release_controller(struct kref *kref)
{
struct rdac_controller *ctlr;
ctlr = container_of(kref, struct rdac_controller, kref);
spin_lock(&list_lock);
list_del(&ctlr->node);
spin_unlock(&list_lock);
kfree(ctlr);
}
static struct rdac_controller *get_controller(u8 *subsys_id, u8 *slot_id)
{
struct rdac_controller *ctlr, *tmp;
spin_lock(&list_lock);
list_for_each_entry(tmp, &ctlr_list, node) {
if ((memcmp(tmp->subsys_id, subsys_id, SUBSYS_ID_LEN) == 0) &&
(memcmp(tmp->slot_id, slot_id, SLOT_ID_LEN) == 0)) {
kref_get(&tmp->kref);
spin_unlock(&list_lock);
return tmp;
}
}
ctlr = kmalloc(sizeof(*ctlr), GFP_ATOMIC);
if (!ctlr)
goto done;
/* initialize fields of controller */
memcpy(ctlr->subsys_id, subsys_id, SUBSYS_ID_LEN);
memcpy(ctlr->slot_id, slot_id, SLOT_ID_LEN);
kref_init(&ctlr->kref);
ctlr->use_ms10 = -1;
list_add(&ctlr->node, &ctlr_list);
done:
spin_unlock(&list_lock);
return ctlr;
}
static int submit_inquiry(struct scsi_device *sdev, int page_code,
unsigned int len)
{
struct request *rq;
struct request_queue *q = sdev->request_queue;
struct rdac_dh_data *h = get_rdac_data(sdev);
int err = SCSI_DH_RES_TEMP_UNAVAIL;
rq = get_rdac_req(sdev, &h->inq, len, READ);
if (!rq)
goto done;
/* Prepare the command. */
rq->cmd[0] = INQUIRY;
rq->cmd[1] = 1;
rq->cmd[2] = page_code;
rq->cmd[4] = len;
rq->cmd_len = COMMAND_SIZE(INQUIRY);
err = blk_execute_rq(q, NULL, rq, 1);
if (err == -EIO)
err = SCSI_DH_IO;
done:
return err;
}
static int get_lun(struct scsi_device *sdev)
{
int err;
struct c8_inquiry *inqp;
struct rdac_dh_data *h = get_rdac_data(sdev);
err = submit_inquiry(sdev, 0xC8, sizeof(struct c8_inquiry));
if (err == SCSI_DH_OK) {
inqp = &h->inq.c8;
h->lun = inqp->lun[7]; /* currently it uses only one byte */
}
return err;
}
#define RDAC_OWNED 0
#define RDAC_UNOWNED 1
#define RDAC_FAILED 2
static int check_ownership(struct scsi_device *sdev)
{
int err;
struct c9_inquiry *inqp;
struct rdac_dh_data *h = get_rdac_data(sdev);
err = submit_inquiry(sdev, 0xC9, sizeof(struct c9_inquiry));
if (err == SCSI_DH_OK) {
err = RDAC_UNOWNED;
inqp = &h->inq.c9;
/*
* If in AVT mode or if the path already owns the LUN,
* return RDAC_OWNED;
*/
if (((inqp->avte_cvp >> 7) == 0x1) ||
((inqp->avte_cvp & 0x1) != 0))
err = RDAC_OWNED;
} else
err = RDAC_FAILED;
return err;
}
static int initialize_controller(struct scsi_device *sdev)
{
int err;
struct c4_inquiry *inqp;
struct rdac_dh_data *h = get_rdac_data(sdev);
err = submit_inquiry(sdev, 0xC4, sizeof(struct c4_inquiry));
if (err == SCSI_DH_OK) {
inqp = &h->inq.c4;
h->ctlr = get_controller(inqp->subsys_id, inqp->slot_id);
if (!h->ctlr)
err = SCSI_DH_RES_TEMP_UNAVAIL;
}
return err;
}
static int set_mode_select(struct scsi_device *sdev)
{
int err;
struct c2_inquiry *inqp;
struct rdac_dh_data *h = get_rdac_data(sdev);
err = submit_inquiry(sdev, 0xC2, sizeof(struct c2_inquiry));
if (err == SCSI_DH_OK) {
inqp = &h->inq.c2;
/*
* If more than MODE6_MAX_LUN luns are supported, use
* mode select 10
*/
if (inqp->max_lun_supported >= MODE6_MAX_LUN)
h->ctlr->use_ms10 = 1;
else
h->ctlr->use_ms10 = 0;
}
return err;
}
static int mode_select_handle_sense(struct scsi_device *sdev)
{
struct scsi_sense_hdr sense_hdr;
struct rdac_dh_data *h = get_rdac_data(sdev);
int sense, err = SCSI_DH_IO, ret;
ret = scsi_normalize_sense(h->sense, SCSI_SENSE_BUFFERSIZE, &sense_hdr);
if (!ret)
goto done;
err = SCSI_DH_OK;
sense = (sense_hdr.sense_key << 16) | (sense_hdr.asc << 8) |
sense_hdr.ascq;
/* If it is retryable failure, submit the c9 inquiry again */
if (sense == 0x59136 || sense == 0x68b02 || sense == 0xb8b02 ||
sense == 0x62900) {
/* 0x59136 - Command lock contention
* 0x[6b]8b02 - Quiesense in progress or achieved
* 0x62900 - Power On, Reset, or Bus Device Reset
*/
err = SCSI_DH_RETRY;
}
if (sense)
sdev_printk(KERN_INFO, sdev,
"MODE_SELECT failed with sense 0x%x.\n", sense);
done:
return err;
}
static int send_mode_select(struct scsi_device *sdev)
{
struct request *rq;
struct request_queue *q = sdev->request_queue;
struct rdac_dh_data *h = get_rdac_data(sdev);
int err = SCSI_DH_RES_TEMP_UNAVAIL;
rq = rdac_failover_get(sdev);
if (!rq)
goto done;
sdev_printk(KERN_INFO, sdev, "queueing MODE_SELECT command.\n");
err = blk_execute_rq(q, NULL, rq, 1);
if (err != SCSI_DH_OK)
err = mode_select_handle_sense(sdev);
if (err == SCSI_DH_OK)
h->state = RDAC_STATE_ACTIVE;
done:
return err;
}
static int rdac_activate(struct scsi_device *sdev)
{
struct rdac_dh_data *h = get_rdac_data(sdev);
int err = SCSI_DH_OK;
if (h->lun == UNINITIALIZED_LUN) {
err = get_lun(sdev);
if (err != SCSI_DH_OK)
goto done;
}
err = check_ownership(sdev);
switch (err) {
case RDAC_UNOWNED:
break;
case RDAC_OWNED:
err = SCSI_DH_OK;
goto done;
case RDAC_FAILED:
default:
err = SCSI_DH_IO;
goto done;
}
if (!h->ctlr) {
err = initialize_controller(sdev);
if (err != SCSI_DH_OK)
goto done;
}
if (h->ctlr->use_ms10 == -1) {
err = set_mode_select(sdev);
if (err != SCSI_DH_OK)
goto done;
}
err = send_mode_select(sdev);
done:
return err;
}
static int rdac_prep_fn(struct scsi_device *sdev, struct request *req)
{
struct rdac_dh_data *h = get_rdac_data(sdev);
int ret = BLKPREP_OK;
if (h->state != RDAC_STATE_ACTIVE) {
ret = BLKPREP_KILL;
req->cmd_flags |= REQ_QUIET;
}
return ret;
}
static int rdac_check_sense(struct scsi_device *sdev,
struct scsi_sense_hdr *sense_hdr)
{
struct rdac_dh_data *h = get_rdac_data(sdev);
switch (sense_hdr->sense_key) {
case NOT_READY:
if (sense_hdr->asc == 0x04 && sense_hdr->ascq == 0x81)
/* LUN Not Ready - Storage firmware incompatible
* Manual code synchonisation required.
*
* Nothing we can do here. Try to bypass the path.
*/
return SUCCESS;
if (sense_hdr->asc == 0x04 && sense_hdr->ascq == 0xA1)
/* LUN Not Ready - Quiescense in progress
*
* Just retry and wait.
*/
return NEEDS_RETRY;
break;
case ILLEGAL_REQUEST:
if (sense_hdr->asc == 0x94 && sense_hdr->ascq == 0x01) {
/* Invalid Request - Current Logical Unit Ownership.
* Controller is not the current owner of the LUN,
* Fail the path, so that the other path be used.
*/
h->state = RDAC_STATE_PASSIVE;
return SUCCESS;
}
break;
case UNIT_ATTENTION:
if (sense_hdr->asc == 0x29 && sense_hdr->ascq == 0x00)
/*
* Power On, Reset, or Bus Device Reset, just retry.
*/
return NEEDS_RETRY;
break;
}
/* success just means we do not care what scsi-ml does */
return SCSI_RETURN_NOT_HANDLED;
}
static const struct {
char *vendor;
char *model;
} rdac_dev_list[] = {
{"IBM", "1722"},
{"IBM", "1724"},
{"IBM", "1726"},
{"IBM", "1742"},
{"IBM", "1814"},
{"IBM", "1815"},
{"IBM", "1818"},
{"IBM", "3526"},
{"SGI", "TP9400"},
{"SGI", "TP9500"},
{"SGI", "IS"},
{"STK", "OPENstorage D280"},
{"SUN", "CSM200_R"},
{"SUN", "LCSM100_F"},
{NULL, NULL},
};
static int rdac_bus_notify(struct notifier_block *, unsigned long, void *);
static struct scsi_device_handler rdac_dh = {
.name = RDAC_NAME,
.module = THIS_MODULE,
.nb.notifier_call = rdac_bus_notify,
.prep_fn = rdac_prep_fn,
.check_sense = rdac_check_sense,
.activate = rdac_activate,
};
/*
* TODO: need some interface so we can set trespass values
*/
static int rdac_bus_notify(struct notifier_block *nb,
unsigned long action, void *data)
{
struct device *dev = data;
struct scsi_device *sdev = to_scsi_device(dev);
struct scsi_dh_data *scsi_dh_data;
struct rdac_dh_data *h;
int i, found = 0;
unsigned long flags;
if (action == BUS_NOTIFY_ADD_DEVICE) {
for (i = 0; rdac_dev_list[i].vendor; i++) {
if (!strncmp(sdev->vendor, rdac_dev_list[i].vendor,
strlen(rdac_dev_list[i].vendor)) &&
!strncmp(sdev->model, rdac_dev_list[i].model,
strlen(rdac_dev_list[i].model))) {
found = 1;
break;
}
}
if (!found)
goto out;
scsi_dh_data = kzalloc(sizeof(struct scsi_device_handler *)
+ sizeof(*h) , GFP_KERNEL);
if (!scsi_dh_data) {
sdev_printk(KERN_ERR, sdev, "Attach failed %s.\n",
RDAC_NAME);
goto out;
}
scsi_dh_data->scsi_dh = &rdac_dh;
h = (struct rdac_dh_data *) scsi_dh_data->buf;
h->lun = UNINITIALIZED_LUN;
h->state = RDAC_STATE_ACTIVE;
spin_lock_irqsave(sdev->request_queue->queue_lock, flags);
sdev->scsi_dh_data = scsi_dh_data;
spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags);
try_module_get(THIS_MODULE);
sdev_printk(KERN_NOTICE, sdev, "Attached %s.\n", RDAC_NAME);
} else if (action == BUS_NOTIFY_DEL_DEVICE) {
if (sdev->scsi_dh_data == NULL ||
sdev->scsi_dh_data->scsi_dh != &rdac_dh)
goto out;
spin_lock_irqsave(sdev->request_queue->queue_lock, flags);
scsi_dh_data = sdev->scsi_dh_data;
sdev->scsi_dh_data = NULL;
spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags);
h = (struct rdac_dh_data *) scsi_dh_data->buf;
if (h->ctlr)
kref_put(&h->ctlr->kref, release_controller);
kfree(scsi_dh_data);
module_put(THIS_MODULE);
sdev_printk(KERN_NOTICE, sdev, "Dettached %s.\n", RDAC_NAME);
}
out:
return 0;
}
static int __init rdac_init(void)
{
int r;
r = scsi_register_device_handler(&rdac_dh);
if (r != 0)
printk(KERN_ERR "Failed to register scsi device handler.");
return r;
}
static void __exit rdac_exit(void)
{
scsi_unregister_device_handler(&rdac_dh);
}
module_init(rdac_init);
module_exit(rdac_exit);
MODULE_DESCRIPTION("Multipath LSI/Engenio RDAC driver");
MODULE_AUTHOR("Mike Christie, Chandra Seetharaman");
MODULE_LICENSE("GPL");

View File

@ -219,19 +219,10 @@ static void esp_reset_esp(struct esp *esp)
/* Now reset the ESP chip */
scsi_esp_cmd(esp, ESP_CMD_RC);
scsi_esp_cmd(esp, ESP_CMD_NULL | ESP_CMD_DMA);
if (esp->rev == FAST)
esp_write8(ESP_CONFIG2_FENAB, ESP_CFG2);
scsi_esp_cmd(esp, ESP_CMD_NULL | ESP_CMD_DMA);
/* Reload the configuration registers */
esp_write8(esp->cfact, ESP_CFACT);
esp->prev_stp = 0;
esp_write8(esp->prev_stp, ESP_STP);
esp->prev_soff = 0;
esp_write8(esp->prev_soff, ESP_SOFF);
esp_write8(esp->neg_defp, ESP_TIMEO);
/* This is the only point at which it is reliable to read
* the ID-code for a fast ESP chip variants.
*/
@ -316,6 +307,17 @@ static void esp_reset_esp(struct esp *esp)
break;
}
/* Reload the configuration registers */
esp_write8(esp->cfact, ESP_CFACT);
esp->prev_stp = 0;
esp_write8(esp->prev_stp, ESP_STP);
esp->prev_soff = 0;
esp_write8(esp->prev_soff, ESP_SOFF);
esp_write8(esp->neg_defp, ESP_TIMEO);
/* Eat any bitrot in the chip */
esp_read8(ESP_INTRPT);
udelay(100);

View File

@ -290,7 +290,7 @@ static void scsi_host_dev_release(struct device *dev)
kfree(shost);
}
struct device_type scsi_host_type = {
static struct device_type scsi_host_type = {
.name = "scsi_host",
.release = scsi_host_dev_release,
};

View File

@ -5,3 +5,4 @@ ibmvscsic-$(CONFIG_PPC_ISERIES) += iseries_vscsi.o
ibmvscsic-$(CONFIG_PPC_PSERIES) += rpa_vscsi.o
obj-$(CONFIG_SCSI_IBMVSCSIS) += ibmvstgt.o
obj-$(CONFIG_SCSI_IBMVFC) += ibmvfc.o

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,682 @@
/*
* ibmvfc.h -- driver for IBM Power Virtual Fibre Channel Adapter
*
* Written By: Brian King <brking@linux.vnet.ibm.com>, IBM Corporation
*
* Copyright (C) IBM Corporation, 2008
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef _IBMVFC_H
#define _IBMVFC_H
#include <linux/list.h>
#include <linux/types.h>
#include "viosrp.h"
#define IBMVFC_NAME "ibmvfc"
#define IBMVFC_DRIVER_VERSION "1.0.0"
#define IBMVFC_DRIVER_DATE "(July 1, 2008)"
#define IBMVFC_DEFAULT_TIMEOUT 15
#define IBMVFC_INIT_TIMEOUT 30
#define IBMVFC_MAX_REQUESTS_DEFAULT 100
#define IBMVFC_DEBUG 0
#define IBMVFC_MAX_TARGETS 1024
#define IBMVFC_MAX_LUN 0xffffffff
#define IBMVFC_MAX_SECTORS 0xffffu
#define IBMVFC_MAX_DISC_THREADS 4
#define IBMVFC_TGT_MEMPOOL_SZ 64
#define IBMVFC_MAX_CMDS_PER_LUN 64
#define IBMVFC_MAX_INIT_RETRIES 3
#define IBMVFC_DEV_LOSS_TMO (5 * 60)
#define IBMVFC_DEFAULT_LOG_LEVEL 2
#define IBMVFC_MAX_CDB_LEN 16
/*
* Ensure we have resources for ERP and initialization:
* 1 for ERP
* 1 for initialization
* 1 for each discovery thread
*/
#define IBMVFC_NUM_INTERNAL_REQ (1 + 1 + disc_threads)
#define IBMVFC_MAD_SUCCESS 0x00
#define IBMVFC_MAD_NOT_SUPPORTED 0xF1
#define IBMVFC_MAD_FAILED 0xF7
#define IBMVFC_MAD_DRIVER_FAILED 0xEE
#define IBMVFC_MAD_CRQ_ERROR 0xEF
enum ibmvfc_crq_valid {
IBMVFC_CRQ_CMD_RSP = 0x80,
IBMVFC_CRQ_INIT_RSP = 0xC0,
IBMVFC_CRQ_XPORT_EVENT = 0xFF,
};
enum ibmvfc_crq_format {
IBMVFC_CRQ_INIT = 0x01,
IBMVFC_CRQ_INIT_COMPLETE = 0x02,
IBMVFC_PARTITION_MIGRATED = 0x06,
};
enum ibmvfc_cmd_status_flags {
IBMVFC_FABRIC_MAPPED = 0x0001,
IBMVFC_VIOS_FAILURE = 0x0002,
IBMVFC_FC_FAILURE = 0x0004,
IBMVFC_FC_SCSI_ERROR = 0x0008,
IBMVFC_HW_EVENT_LOGGED = 0x0010,
IBMVFC_VIOS_LOGGED = 0x0020,
};
enum ibmvfc_fabric_mapped_errors {
IBMVFC_UNABLE_TO_ESTABLISH = 0x0001,
IBMVFC_XPORT_FAULT = 0x0002,
IBMVFC_CMD_TIMEOUT = 0x0003,
IBMVFC_ENETDOWN = 0x0004,
IBMVFC_HW_FAILURE = 0x0005,
IBMVFC_LINK_DOWN_ERR = 0x0006,
IBMVFC_LINK_DEAD_ERR = 0x0007,
IBMVFC_UNABLE_TO_REGISTER = 0x0008,
IBMVFC_XPORT_BUSY = 0x000A,
IBMVFC_XPORT_DEAD = 0x000B,
IBMVFC_CONFIG_ERROR = 0x000C,
IBMVFC_NAME_SERVER_FAIL = 0x000D,
IBMVFC_LINK_HALTED = 0x000E,
IBMVFC_XPORT_GENERAL = 0x8000,
};
enum ibmvfc_vios_errors {
IBMVFC_CRQ_FAILURE = 0x0001,
IBMVFC_SW_FAILURE = 0x0002,
IBMVFC_INVALID_PARAMETER = 0x0003,
IBMVFC_MISSING_PARAMETER = 0x0004,
IBMVFC_HOST_IO_BUS = 0x0005,
IBMVFC_TRANS_CANCELLED = 0x0006,
IBMVFC_TRANS_CANCELLED_IMPLICIT = 0x0007,
IBMVFC_INSUFFICIENT_RESOURCE = 0x0008,
IBMVFC_COMMAND_FAILED = 0x8000,
};
enum ibmvfc_mad_types {
IBMVFC_NPIV_LOGIN = 0x0001,
IBMVFC_DISC_TARGETS = 0x0002,
IBMVFC_PORT_LOGIN = 0x0004,
IBMVFC_PROCESS_LOGIN = 0x0008,
IBMVFC_QUERY_TARGET = 0x0010,
IBMVFC_IMPLICIT_LOGOUT = 0x0040,
IBMVFC_TMF_MAD = 0x0100,
};
struct ibmvfc_mad_common {
u32 version;
u32 reserved;
u32 opcode;
u16 status;
u16 length;
u64 tag;
}__attribute__((packed, aligned (8)));
struct ibmvfc_npiv_login_mad {
struct ibmvfc_mad_common common;
struct srp_direct_buf buffer;
}__attribute__((packed, aligned (8)));
#define IBMVFC_MAX_NAME 256
struct ibmvfc_npiv_login {
u32 ostype;
#define IBMVFC_OS_LINUX 0x02
u32 pad;
u64 max_dma_len;
u32 max_payload;
u32 max_response;
u32 partition_num;
u32 vfc_frame_version;
u16 fcp_version;
u16 flags;
#define IBMVFC_CLIENT_MIGRATED 0x01
#define IBMVFC_FLUSH_ON_HALT 0x02
u32 max_cmds;
u64 capabilities;
#define IBMVFC_CAN_MIGRATE 0x01
u64 node_name;
struct srp_direct_buf async;
u8 partition_name[IBMVFC_MAX_NAME];
u8 device_name[IBMVFC_MAX_NAME];
u8 drc_name[IBMVFC_MAX_NAME];
u64 reserved2[2];
}__attribute__((packed, aligned (8)));
struct ibmvfc_common_svc_parms {
u16 fcph_version;
u16 b2b_credit;
u16 features;
u16 bb_rcv_sz; /* upper nibble is BB_SC_N */
u32 ratov;
u32 edtov;
}__attribute__((packed, aligned (4)));
struct ibmvfc_service_parms {
struct ibmvfc_common_svc_parms common;
u8 port_name[8];
u8 node_name[8];
u32 class1_parms[4];
u32 class2_parms[4];
u32 class3_parms[4];
u32 obsolete[4];
u32 vendor_version[4];
u32 services_avail[2];
u32 ext_len;
u32 reserved[30];
u32 clk_sync_qos[2];
}__attribute__((packed, aligned (4)));
struct ibmvfc_npiv_login_resp {
u32 version;
u16 status;
u16 error;
u32 flags;
#define IBMVFC_NATIVE_FC 0x01
#define IBMVFC_CAN_FLUSH_ON_HALT 0x08
u32 reserved;
u64 capabilites;
u32 max_cmds;
u32 scsi_id_sz;
u64 max_dma_len;
u64 scsi_id;
u64 port_name;
u64 node_name;
u64 link_speed;
u8 partition_name[IBMVFC_MAX_NAME];
u8 device_name[IBMVFC_MAX_NAME];
u8 port_loc_code[IBMVFC_MAX_NAME];
u8 drc_name[IBMVFC_MAX_NAME];
struct ibmvfc_service_parms service_parms;
u64 reserved2;
}__attribute__((packed, aligned (8)));
union ibmvfc_npiv_login_data {
struct ibmvfc_npiv_login login;
struct ibmvfc_npiv_login_resp resp;
}__attribute__((packed, aligned (8)));
struct ibmvfc_discover_targets_buf {
u32 scsi_id[1];
#define IBMVFC_DISC_TGT_SCSI_ID_MASK 0x00ffffff
};
struct ibmvfc_discover_targets {
struct ibmvfc_mad_common common;
struct srp_direct_buf buffer;
u32 flags;
u16 status;
u16 error;
u32 bufflen;
u32 num_avail;
u32 num_written;
u64 reserved[2];
}__attribute__((packed, aligned (8)));
enum ibmvfc_fc_reason {
IBMVFC_INVALID_ELS_CMD_CODE = 0x01,
IBMVFC_INVALID_VERSION = 0x02,
IBMVFC_LOGICAL_ERROR = 0x03,
IBMVFC_INVALID_CT_IU_SIZE = 0x04,
IBMVFC_LOGICAL_BUSY = 0x05,
IBMVFC_PROTOCOL_ERROR = 0x07,
IBMVFC_UNABLE_TO_PERFORM_REQ = 0x09,
IBMVFC_CMD_NOT_SUPPORTED = 0x0B,
IBMVFC_SERVER_NOT_AVAIL = 0x0D,
IBMVFC_CMD_IN_PROGRESS = 0x0E,
IBMVFC_VENDOR_SPECIFIC = 0xFF,
};
enum ibmvfc_fc_type {
IBMVFC_FABRIC_REJECT = 0x01,
IBMVFC_PORT_REJECT = 0x02,
IBMVFC_LS_REJECT = 0x03,
IBMVFC_FABRIC_BUSY = 0x04,
IBMVFC_PORT_BUSY = 0x05,
IBMVFC_BASIC_REJECT = 0x06,
};
enum ibmvfc_gs_explain {
IBMVFC_PORT_NAME_NOT_REG = 0x02,
};
struct ibmvfc_port_login {
struct ibmvfc_mad_common common;
u64 scsi_id;
u16 reserved;
u16 fc_service_class;
u32 blksz;
u32 hdr_per_blk;
u16 status;
u16 error; /* also fc_reason */
u16 fc_explain;
u16 fc_type;
u32 reserved2;
struct ibmvfc_service_parms service_parms;
struct ibmvfc_service_parms service_parms_change;
u64 reserved3[2];
}__attribute__((packed, aligned (8)));
struct ibmvfc_prli_svc_parms {
u8 type;
#define IBMVFC_SCSI_FCP_TYPE 0x08
u8 type_ext;
u16 flags;
#define IBMVFC_PRLI_ORIG_PA_VALID 0x8000
#define IBMVFC_PRLI_RESP_PA_VALID 0x4000
#define IBMVFC_PRLI_EST_IMG_PAIR 0x2000
u32 orig_pa;
u32 resp_pa;
u32 service_parms;
#define IBMVFC_PRLI_TASK_RETRY 0x00000200
#define IBMVFC_PRLI_RETRY 0x00000100
#define IBMVFC_PRLI_DATA_OVERLAY 0x00000040
#define IBMVFC_PRLI_INITIATOR_FUNC 0x00000020
#define IBMVFC_PRLI_TARGET_FUNC 0x00000010
#define IBMVFC_PRLI_READ_FCP_XFER_RDY_DISABLED 0x00000002
#define IBMVFC_PRLI_WR_FCP_XFER_RDY_DISABLED 0x00000001
}__attribute__((packed, aligned (4)));
struct ibmvfc_process_login {
struct ibmvfc_mad_common common;
u64 scsi_id;
struct ibmvfc_prli_svc_parms parms;
u8 reserved[48];
u16 status;
u16 error; /* also fc_reason */
u32 reserved2;
u64 reserved3[2];
}__attribute__((packed, aligned (8)));
struct ibmvfc_query_tgt {
struct ibmvfc_mad_common common;
u64 wwpn;
u64 scsi_id;
u16 status;
u16 error;
u16 fc_explain;
u16 fc_type;
u64 reserved[2];
}__attribute__((packed, aligned (8)));
struct ibmvfc_implicit_logout {
struct ibmvfc_mad_common common;
u64 old_scsi_id;
u64 reserved[2];
}__attribute__((packed, aligned (8)));
struct ibmvfc_tmf {
struct ibmvfc_mad_common common;
u64 scsi_id;
struct scsi_lun lun;
u32 flags;
#define IBMVFC_TMF_ABORT_TASK 0x02
#define IBMVFC_TMF_ABORT_TASK_SET 0x04
#define IBMVFC_TMF_LUN_RESET 0x10
#define IBMVFC_TMF_TGT_RESET 0x20
#define IBMVFC_TMF_LUA_VALID 0x40
u32 cancel_key;
u32 my_cancel_key;
#define IBMVFC_TMF_CANCEL_KEY 0x80000000
u32 pad;
u64 reserved[2];
}__attribute__((packed, aligned (8)));
enum ibmvfc_fcp_rsp_info_codes {
RSP_NO_FAILURE = 0x00,
RSP_TMF_REJECTED = 0x04,
RSP_TMF_FAILED = 0x05,
RSP_TMF_INVALID_LUN = 0x09,
};
struct ibmvfc_fcp_rsp_info {
u16 reserved;
u8 rsp_code;
u8 reserved2[4];
}__attribute__((packed, aligned (2)));
enum ibmvfc_fcp_rsp_flags {
FCP_BIDI_RSP = 0x80,
FCP_BIDI_READ_RESID_UNDER = 0x40,
FCP_BIDI_READ_RESID_OVER = 0x20,
FCP_CONF_REQ = 0x10,
FCP_RESID_UNDER = 0x08,
FCP_RESID_OVER = 0x04,
FCP_SNS_LEN_VALID = 0x02,
FCP_RSP_LEN_VALID = 0x01,
};
union ibmvfc_fcp_rsp_data {
struct ibmvfc_fcp_rsp_info info;
u8 sense[SCSI_SENSE_BUFFERSIZE + sizeof(struct ibmvfc_fcp_rsp_info)];
}__attribute__((packed, aligned (8)));
struct ibmvfc_fcp_rsp {
u64 reserved;
u16 retry_delay_timer;
u8 flags;
u8 scsi_status;
u32 fcp_resid;
u32 fcp_sense_len;
u32 fcp_rsp_len;
union ibmvfc_fcp_rsp_data data;
}__attribute__((packed, aligned (8)));
enum ibmvfc_cmd_flags {
IBMVFC_SCATTERLIST = 0x0001,
IBMVFC_NO_MEM_DESC = 0x0002,
IBMVFC_READ = 0x0004,
IBMVFC_WRITE = 0x0008,
IBMVFC_TMF = 0x0080,
IBMVFC_CLASS_3_ERR = 0x0100,
};
enum ibmvfc_fc_task_attr {
IBMVFC_SIMPLE_TASK = 0x00,
IBMVFC_HEAD_OF_QUEUE = 0x01,
IBMVFC_ORDERED_TASK = 0x02,
IBMVFC_ACA_TASK = 0x04,
};
enum ibmvfc_fc_tmf_flags {
IBMVFC_ABORT_TASK_SET = 0x02,
IBMVFC_LUN_RESET = 0x10,
IBMVFC_TARGET_RESET = 0x20,
};
struct ibmvfc_fcp_cmd_iu {
struct scsi_lun lun;
u8 crn;
u8 pri_task_attr;
u8 tmf_flags;
u8 add_cdb_len;
#define IBMVFC_RDDATA 0x02
#define IBMVFC_WRDATA 0x01
u8 cdb[IBMVFC_MAX_CDB_LEN];
u32 xfer_len;
}__attribute__((packed, aligned (4)));
struct ibmvfc_cmd {
u64 task_tag;
u32 frame_type;
u32 payload_len;
u32 resp_len;
u32 adapter_resid;
u16 status;
u16 error;
u16 flags;
u16 response_flags;
#define IBMVFC_ADAPTER_RESID_VALID 0x01
u32 cancel_key;
u32 exchange_id;
struct srp_direct_buf ext_func;
struct srp_direct_buf ioba;
struct srp_direct_buf resp;
u64 correlation;
u64 tgt_scsi_id;
u64 tag;
u64 reserved3[2];
struct ibmvfc_fcp_cmd_iu iu;
struct ibmvfc_fcp_rsp rsp;
}__attribute__((packed, aligned (8)));
struct ibmvfc_trace_start_entry {
u32 xfer_len;
}__attribute__((packed));
struct ibmvfc_trace_end_entry {
u16 status;
u16 error;
u8 fcp_rsp_flags;
u8 rsp_code;
u8 scsi_status;
u8 reserved;
}__attribute__((packed));
struct ibmvfc_trace_entry {
struct ibmvfc_event *evt;
u32 time;
u32 scsi_id;
u32 lun;
u8 fmt;
u8 op_code;
u8 tmf_flags;
u8 type;
#define IBMVFC_TRC_START 0x00
#define IBMVFC_TRC_END 0xff
union {
struct ibmvfc_trace_start_entry start;
struct ibmvfc_trace_end_entry end;
} u;
}__attribute__((packed, aligned (8)));
enum ibmvfc_crq_formats {
IBMVFC_CMD_FORMAT = 0x01,
IBMVFC_ASYNC_EVENT = 0x02,
IBMVFC_MAD_FORMAT = 0x04,
};
enum ibmvfc_async_event {
IBMVFC_AE_ELS_PLOGI = 0x0001,
IBMVFC_AE_ELS_LOGO = 0x0002,
IBMVFC_AE_ELS_PRLO = 0x0004,
IBMVFC_AE_SCN_NPORT = 0x0008,
IBMVFC_AE_SCN_GROUP = 0x0010,
IBMVFC_AE_SCN_DOMAIN = 0x0020,
IBMVFC_AE_SCN_FABRIC = 0x0040,
IBMVFC_AE_LINK_UP = 0x0080,
IBMVFC_AE_LINK_DOWN = 0x0100,
IBMVFC_AE_LINK_DEAD = 0x0200,
IBMVFC_AE_HALT = 0x0400,
IBMVFC_AE_RESUME = 0x0800,
IBMVFC_AE_ADAPTER_FAILED = 0x1000,
};
struct ibmvfc_crq {
u8 valid;
u8 format;
u8 reserved[6];
u64 ioba;
}__attribute__((packed, aligned (8)));
struct ibmvfc_crq_queue {
struct ibmvfc_crq *msgs;
int size, cur;
dma_addr_t msg_token;
};
struct ibmvfc_async_crq {
u8 valid;
u8 pad[3];
u32 pad2;
u64 event;
u64 scsi_id;
u64 wwpn;
u64 node_name;
u64 reserved;
}__attribute__((packed, aligned (8)));
struct ibmvfc_async_crq_queue {
struct ibmvfc_async_crq *msgs;
int size, cur;
dma_addr_t msg_token;
};
union ibmvfc_iu {
struct ibmvfc_mad_common mad_common;
struct ibmvfc_npiv_login_mad npiv_login;
struct ibmvfc_discover_targets discover_targets;
struct ibmvfc_port_login plogi;
struct ibmvfc_process_login prli;
struct ibmvfc_query_tgt query_tgt;
struct ibmvfc_implicit_logout implicit_logout;
struct ibmvfc_tmf tmf;
struct ibmvfc_cmd cmd;
}__attribute__((packed, aligned (8)));
enum ibmvfc_target_action {
IBMVFC_TGT_ACTION_NONE = 0,
IBMVFC_TGT_ACTION_INIT,
IBMVFC_TGT_ACTION_INIT_WAIT,
IBMVFC_TGT_ACTION_ADD_RPORT,
IBMVFC_TGT_ACTION_DEL_RPORT,
};
struct ibmvfc_target {
struct list_head queue;
struct ibmvfc_host *vhost;
u64 scsi_id;
u64 new_scsi_id;
struct fc_rport *rport;
int target_id;
enum ibmvfc_target_action action;
int need_login;
int init_retries;
struct ibmvfc_service_parms service_parms;
struct ibmvfc_service_parms service_parms_change;
struct fc_rport_identifiers ids;
void (*job_step) (struct ibmvfc_target *);
struct kref kref;
};
/* a unit of work for the hosting partition */
struct ibmvfc_event {
struct list_head queue;
struct ibmvfc_host *vhost;
struct ibmvfc_target *tgt;
struct scsi_cmnd *cmnd;
atomic_t free;
union ibmvfc_iu *xfer_iu;
void (*done) (struct ibmvfc_event *);
struct ibmvfc_crq crq;
union ibmvfc_iu iu;
union ibmvfc_iu *sync_iu;
struct srp_direct_buf *ext_list;
dma_addr_t ext_list_token;
struct completion comp;
struct timer_list timer;
};
/* a pool of event structs for use */
struct ibmvfc_event_pool {
struct ibmvfc_event *events;
u32 size;
union ibmvfc_iu *iu_storage;
dma_addr_t iu_token;
};
enum ibmvfc_host_action {
IBMVFC_HOST_ACTION_NONE = 0,
IBMVFC_HOST_ACTION_INIT,
IBMVFC_HOST_ACTION_INIT_WAIT,
IBMVFC_HOST_ACTION_QUERY,
IBMVFC_HOST_ACTION_QUERY_TGTS,
IBMVFC_HOST_ACTION_TGT_DEL,
IBMVFC_HOST_ACTION_ALLOC_TGTS,
IBMVFC_HOST_ACTION_TGT_INIT,
IBMVFC_HOST_ACTION_TGT_ADD,
};
enum ibmvfc_host_state {
IBMVFC_NO_CRQ = 0,
IBMVFC_INITIALIZING,
IBMVFC_ACTIVE,
IBMVFC_HALTED,
IBMVFC_LINK_DOWN,
IBMVFC_LINK_DEAD,
IBMVFC_HOST_OFFLINE,
};
struct ibmvfc_host {
char name[8];
struct list_head queue;
struct Scsi_Host *host;
enum ibmvfc_host_state state;
enum ibmvfc_host_action action;
#define IBMVFC_NUM_TRACE_INDEX_BITS 8
#define IBMVFC_NUM_TRACE_ENTRIES (1 << IBMVFC_NUM_TRACE_INDEX_BITS)
#define IBMVFC_TRACE_SIZE (sizeof(struct ibmvfc_trace_entry) * IBMVFC_NUM_TRACE_ENTRIES)
struct ibmvfc_trace_entry *trace;
u32 trace_index:IBMVFC_NUM_TRACE_INDEX_BITS;
int num_targets;
struct list_head targets;
struct list_head sent;
struct list_head free;
struct device *dev;
struct ibmvfc_event_pool pool;
struct dma_pool *sg_pool;
mempool_t *tgt_pool;
struct ibmvfc_crq_queue crq;
struct ibmvfc_async_crq_queue async_crq;
struct ibmvfc_npiv_login login_info;
union ibmvfc_npiv_login_data *login_buf;
dma_addr_t login_buf_dma;
int disc_buf_sz;
int log_level;
struct ibmvfc_discover_targets_buf *disc_buf;
int task_set;
int init_retries;
int discovery_threads;
int client_migrated;
int reinit;
int events_to_log;
#define IBMVFC_AE_LINKUP 0x0001
#define IBMVFC_AE_LINKDOWN 0x0002
#define IBMVFC_AE_RSCN 0x0004
dma_addr_t disc_buf_dma;
unsigned int partition_number;
char partition_name[97];
void (*job_step) (struct ibmvfc_host *);
struct task_struct *work_thread;
wait_queue_head_t init_wait_q;
wait_queue_head_t work_wait_q;
};
#define DBG_CMD(CMD) do { if (ibmvfc_debug) CMD; } while (0)
#define tgt_dbg(t, fmt, ...) \
DBG_CMD(dev_info((t)->vhost->dev, "%lX: " fmt, (t)->scsi_id, ##__VA_ARGS__))
#define tgt_err(t, fmt, ...) \
dev_err((t)->vhost->dev, "%lX: " fmt, (t)->scsi_id, ##__VA_ARGS__)
#define ibmvfc_dbg(vhost, ...) \
DBG_CMD(dev_info((vhost)->dev, ##__VA_ARGS__))
#define ibmvfc_log(vhost, level, ...) \
do { \
if (level >= (vhost)->log_level) \
dev_err((vhost)->dev, ##__VA_ARGS__); \
} while (0)
#define ENTER DBG_CMD(printk(KERN_INFO IBMVFC_NAME": Entering %s\n", __FUNCTION__))
#define LEAVE DBG_CMD(printk(KERN_INFO IBMVFC_NAME": Leaving %s\n", __FUNCTION__))
#ifdef CONFIG_SCSI_IBMVFC_TRACE
#define ibmvfc_create_trace_file(kobj, attr) sysfs_create_bin_file(kobj, attr)
#define ibmvfc_remove_trace_file(kobj, attr) sysfs_remove_bin_file(kobj, attr)
#else
#define ibmvfc_create_trace_file(kobj, attr) 0
#define ibmvfc_remove_trace_file(kobj, attr) do { } while (0)
#endif
#endif

View File

@ -64,6 +64,10 @@ MODULE_LICENSE("GPL");
#define BUG_ON(expr)
#endif
static struct scsi_transport_template *iscsi_tcp_scsi_transport;
static struct scsi_host_template iscsi_sht;
static struct iscsi_transport iscsi_tcp_transport;
static unsigned int iscsi_max_lun = 512;
module_param_named(max_lun, iscsi_max_lun, uint, S_IRUGO);
@ -494,39 +498,43 @@ iscsi_tcp_data_recv_prep(struct iscsi_tcp_conn *tcp_conn)
* must be called with session lock
*/
static void
iscsi_tcp_cleanup_ctask(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
iscsi_tcp_cleanup_task(struct iscsi_conn *conn, struct iscsi_task *task)
{
struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
struct iscsi_tcp_task *tcp_task = task->dd_data;
struct iscsi_r2t_info *r2t;
/* flush ctask's r2t queues */
while (__kfifo_get(tcp_ctask->r2tqueue, (void*)&r2t, sizeof(void*))) {
__kfifo_put(tcp_ctask->r2tpool.queue, (void*)&r2t,
/* nothing to do for mgmt tasks */
if (!task->sc)
return;
/* flush task's r2t queues */
while (__kfifo_get(tcp_task->r2tqueue, (void*)&r2t, sizeof(void*))) {
__kfifo_put(tcp_task->r2tpool.queue, (void*)&r2t,
sizeof(void*));
debug_scsi("iscsi_tcp_cleanup_ctask pending r2t dropped\n");
debug_scsi("iscsi_tcp_cleanup_task pending r2t dropped\n");
}
r2t = tcp_ctask->r2t;
r2t = tcp_task->r2t;
if (r2t != NULL) {
__kfifo_put(tcp_ctask->r2tpool.queue, (void*)&r2t,
__kfifo_put(tcp_task->r2tpool.queue, (void*)&r2t,
sizeof(void*));
tcp_ctask->r2t = NULL;
tcp_task->r2t = NULL;
}
}
/**
* iscsi_data_rsp - SCSI Data-In Response processing
* @conn: iscsi connection
* @ctask: scsi command task
* @task: scsi command task
**/
static int
iscsi_data_rsp(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
iscsi_data_rsp(struct iscsi_conn *conn, struct iscsi_task *task)
{
struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
struct iscsi_tcp_task *tcp_task = task->dd_data;
struct iscsi_data_rsp *rhdr = (struct iscsi_data_rsp *)tcp_conn->in.hdr;
struct iscsi_session *session = conn->session;
struct scsi_cmnd *sc = ctask->sc;
struct scsi_cmnd *sc = task->sc;
int datasn = be32_to_cpu(rhdr->datasn);
unsigned total_in_length = scsi_in(sc)->length;
@ -534,18 +542,18 @@ iscsi_data_rsp(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
if (tcp_conn->in.datalen == 0)
return 0;
if (tcp_ctask->exp_datasn != datasn) {
debug_tcp("%s: ctask->exp_datasn(%d) != rhdr->datasn(%d)\n",
__FUNCTION__, tcp_ctask->exp_datasn, datasn);
if (tcp_task->exp_datasn != datasn) {
debug_tcp("%s: task->exp_datasn(%d) != rhdr->datasn(%d)\n",
__func__, tcp_task->exp_datasn, datasn);
return ISCSI_ERR_DATASN;
}
tcp_ctask->exp_datasn++;
tcp_task->exp_datasn++;
tcp_ctask->data_offset = be32_to_cpu(rhdr->offset);
if (tcp_ctask->data_offset + tcp_conn->in.datalen > total_in_length) {
tcp_task->data_offset = be32_to_cpu(rhdr->offset);
if (tcp_task->data_offset + tcp_conn->in.datalen > total_in_length) {
debug_tcp("%s: data_offset(%d) + data_len(%d) > total_length_in(%d)\n",
__FUNCTION__, tcp_ctask->data_offset,
__func__, tcp_task->data_offset,
tcp_conn->in.datalen, total_in_length);
return ISCSI_ERR_DATA_OFFSET;
}
@ -574,7 +582,7 @@ iscsi_data_rsp(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
/**
* iscsi_solicit_data_init - initialize first Data-Out
* @conn: iscsi connection
* @ctask: scsi command task
* @task: scsi command task
* @r2t: R2T info
*
* Notes:
@ -584,7 +592,7 @@ iscsi_data_rsp(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
* This function is called with connection lock taken.
**/
static void
iscsi_solicit_data_init(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask,
iscsi_solicit_data_init(struct iscsi_conn *conn, struct iscsi_task *task,
struct iscsi_r2t_info *r2t)
{
struct iscsi_data *hdr;
@ -595,8 +603,8 @@ iscsi_solicit_data_init(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask,
hdr->datasn = cpu_to_be32(r2t->solicit_datasn);
r2t->solicit_datasn++;
hdr->opcode = ISCSI_OP_SCSI_DATA_OUT;
memcpy(hdr->lun, ctask->hdr->lun, sizeof(hdr->lun));
hdr->itt = ctask->hdr->itt;
memcpy(hdr->lun, task->hdr->lun, sizeof(hdr->lun));
hdr->itt = task->hdr->itt;
hdr->exp_statsn = r2t->exp_statsn;
hdr->offset = cpu_to_be32(r2t->data_offset);
if (r2t->data_length > conn->max_xmit_dlength) {
@ -616,14 +624,14 @@ iscsi_solicit_data_init(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask,
/**
* iscsi_r2t_rsp - iSCSI R2T Response processing
* @conn: iscsi connection
* @ctask: scsi command task
* @task: scsi command task
**/
static int
iscsi_r2t_rsp(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
iscsi_r2t_rsp(struct iscsi_conn *conn, struct iscsi_task *task)
{
struct iscsi_r2t_info *r2t;
struct iscsi_session *session = conn->session;
struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
struct iscsi_tcp_task *tcp_task = task->dd_data;
struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
struct iscsi_r2t_rsp *rhdr = (struct iscsi_r2t_rsp *)tcp_conn->in.hdr;
int r2tsn = be32_to_cpu(rhdr->r2tsn);
@ -636,23 +644,23 @@ iscsi_r2t_rsp(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
return ISCSI_ERR_DATALEN;
}
if (tcp_ctask->exp_datasn != r2tsn){
debug_tcp("%s: ctask->exp_datasn(%d) != rhdr->r2tsn(%d)\n",
__FUNCTION__, tcp_ctask->exp_datasn, r2tsn);
if (tcp_task->exp_datasn != r2tsn){
debug_tcp("%s: task->exp_datasn(%d) != rhdr->r2tsn(%d)\n",
__func__, tcp_task->exp_datasn, r2tsn);
return ISCSI_ERR_R2TSN;
}
/* fill-in new R2T associated with the task */
iscsi_update_cmdsn(session, (struct iscsi_nopin*)rhdr);
if (!ctask->sc || session->state != ISCSI_STATE_LOGGED_IN) {
if (!task->sc || session->state != ISCSI_STATE_LOGGED_IN) {
iscsi_conn_printk(KERN_INFO, conn,
"dropping R2T itt %d in recovery.\n",
ctask->itt);
task->itt);
return 0;
}
rc = __kfifo_get(tcp_ctask->r2tpool.queue, (void*)&r2t, sizeof(void*));
rc = __kfifo_get(tcp_task->r2tpool.queue, (void*)&r2t, sizeof(void*));
BUG_ON(!rc);
r2t->exp_statsn = rhdr->statsn;
@ -660,7 +668,7 @@ iscsi_r2t_rsp(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
if (r2t->data_length == 0) {
iscsi_conn_printk(KERN_ERR, conn,
"invalid R2T with zero data len\n");
__kfifo_put(tcp_ctask->r2tpool.queue, (void*)&r2t,
__kfifo_put(tcp_task->r2tpool.queue, (void*)&r2t,
sizeof(void*));
return ISCSI_ERR_DATALEN;
}
@ -671,12 +679,12 @@ iscsi_r2t_rsp(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
r2t->data_length, session->max_burst);
r2t->data_offset = be32_to_cpu(rhdr->data_offset);
if (r2t->data_offset + r2t->data_length > scsi_out(ctask->sc)->length) {
if (r2t->data_offset + r2t->data_length > scsi_out(task->sc)->length) {
iscsi_conn_printk(KERN_ERR, conn,
"invalid R2T with data len %u at offset %u "
"and total length %d\n", r2t->data_length,
r2t->data_offset, scsi_out(ctask->sc)->length);
__kfifo_put(tcp_ctask->r2tpool.queue, (void*)&r2t,
r2t->data_offset, scsi_out(task->sc)->length);
__kfifo_put(tcp_task->r2tpool.queue, (void*)&r2t,
sizeof(void*));
return ISCSI_ERR_DATALEN;
}
@ -684,13 +692,13 @@ iscsi_r2t_rsp(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
r2t->ttt = rhdr->ttt; /* no flip */
r2t->solicit_datasn = 0;
iscsi_solicit_data_init(conn, ctask, r2t);
iscsi_solicit_data_init(conn, task, r2t);
tcp_ctask->exp_datasn = r2tsn + 1;
__kfifo_put(tcp_ctask->r2tqueue, (void*)&r2t, sizeof(void*));
tcp_task->exp_datasn = r2tsn + 1;
__kfifo_put(tcp_task->r2tqueue, (void*)&r2t, sizeof(void*));
conn->r2t_pdus_cnt++;
iscsi_requeue_ctask(ctask);
iscsi_requeue_task(task);
return 0;
}
@ -733,10 +741,8 @@ static int
iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr)
{
int rc = 0, opcode, ahslen;
struct iscsi_session *session = conn->session;
struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
struct iscsi_cmd_task *ctask;
uint32_t itt;
struct iscsi_task *task;
/* verify PDU length */
tcp_conn->in.datalen = ntoh24(hdr->dlength);
@ -754,7 +760,7 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr)
opcode = hdr->opcode & ISCSI_OPCODE_MASK;
/* verify itt (itt encoding: age+cid+itt) */
rc = iscsi_verify_itt(conn, hdr, &itt);
rc = iscsi_verify_itt(conn, hdr->itt);
if (rc)
return rc;
@ -763,16 +769,21 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr)
switch(opcode) {
case ISCSI_OP_SCSI_DATA_IN:
ctask = session->cmds[itt];
spin_lock(&conn->session->lock);
rc = iscsi_data_rsp(conn, ctask);
spin_unlock(&conn->session->lock);
if (rc)
return rc;
task = iscsi_itt_to_ctask(conn, hdr->itt);
if (!task)
rc = ISCSI_ERR_BAD_ITT;
else
rc = iscsi_data_rsp(conn, task);
if (rc) {
spin_unlock(&conn->session->lock);
break;
}
if (tcp_conn->in.datalen) {
struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
struct iscsi_tcp_task *tcp_task = task->dd_data;
struct hash_desc *rx_hash = NULL;
struct scsi_data_buffer *sdb = scsi_in(ctask->sc);
struct scsi_data_buffer *sdb = scsi_in(task->sc);
/*
* Setup copy of Data-In into the Scsi_Cmnd
@ -787,17 +798,21 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr)
debug_tcp("iscsi_tcp_begin_data_in(%p, offset=%d, "
"datalen=%d)\n", tcp_conn,
tcp_ctask->data_offset,
tcp_task->data_offset,
tcp_conn->in.datalen);
return iscsi_segment_seek_sg(&tcp_conn->in.segment,
sdb->table.sgl,
sdb->table.nents,
tcp_ctask->data_offset,
tcp_conn->in.datalen,
iscsi_tcp_process_data_in,
rx_hash);
rc = iscsi_segment_seek_sg(&tcp_conn->in.segment,
sdb->table.sgl,
sdb->table.nents,
tcp_task->data_offset,
tcp_conn->in.datalen,
iscsi_tcp_process_data_in,
rx_hash);
spin_unlock(&conn->session->lock);
return rc;
}
/* fall through */
rc = __iscsi_complete_pdu(conn, hdr, NULL, 0);
spin_unlock(&conn->session->lock);
break;
case ISCSI_OP_SCSI_CMD_RSP:
if (tcp_conn->in.datalen) {
iscsi_tcp_data_recv_prep(tcp_conn);
@ -806,15 +821,17 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr)
rc = iscsi_complete_pdu(conn, hdr, NULL, 0);
break;
case ISCSI_OP_R2T:
ctask = session->cmds[itt];
if (ahslen)
spin_lock(&conn->session->lock);
task = iscsi_itt_to_ctask(conn, hdr->itt);
if (!task)
rc = ISCSI_ERR_BAD_ITT;
else if (ahslen)
rc = ISCSI_ERR_AHSLEN;
else if (ctask->sc->sc_data_direction == DMA_TO_DEVICE) {
spin_lock(&session->lock);
rc = iscsi_r2t_rsp(conn, ctask);
spin_unlock(&session->lock);
} else
else if (task->sc->sc_data_direction == DMA_TO_DEVICE)
rc = iscsi_r2t_rsp(conn, task);
else
rc = ISCSI_ERR_PROTO;
spin_unlock(&conn->session->lock);
break;
case ISCSI_OP_LOGIN_RSP:
case ISCSI_OP_TEXT_RSP:
@ -1176,7 +1193,7 @@ iscsi_tcp_send_hdr_prep(struct iscsi_conn *conn, void *hdr, size_t hdrlen)
{
struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
debug_tcp("%s(%p%s)\n", __FUNCTION__, tcp_conn,
debug_tcp("%s(%p%s)\n", __func__, tcp_conn,
conn->hdrdgst_en? ", digest enabled" : "");
/* Clear the data segment - needs to be filled in by the
@ -1185,7 +1202,7 @@ iscsi_tcp_send_hdr_prep(struct iscsi_conn *conn, void *hdr, size_t hdrlen)
/* If header digest is enabled, compute the CRC and
* place the digest into the same buffer. We make
* sure that both iscsi_tcp_ctask and mtask have
* sure that both iscsi_tcp_task and mtask have
* sufficient room.
*/
if (conn->hdrdgst_en) {
@ -1217,7 +1234,7 @@ iscsi_tcp_send_data_prep(struct iscsi_conn *conn, struct scatterlist *sg,
struct hash_desc *tx_hash = NULL;
unsigned int hdr_spec_len;
debug_tcp("%s(%p, offset=%d, datalen=%d%s)\n", __FUNCTION__,
debug_tcp("%s(%p, offset=%d, datalen=%d%s)\n", __func__,
tcp_conn, offset, len,
conn->datadgst_en? ", digest enabled" : "");
@ -1242,7 +1259,7 @@ iscsi_tcp_send_linear_data_prepare(struct iscsi_conn *conn, void *data,
struct hash_desc *tx_hash = NULL;
unsigned int hdr_spec_len;
debug_tcp("%s(%p, datalen=%d%s)\n", __FUNCTION__, tcp_conn, len,
debug_tcp("%s(%p, datalen=%d%s)\n", __func__, tcp_conn, len,
conn->datadgst_en? ", digest enabled" : "");
/* Make sure the datalen matches what the caller
@ -1260,7 +1277,7 @@ iscsi_tcp_send_linear_data_prepare(struct iscsi_conn *conn, void *data,
/**
* iscsi_solicit_data_cont - initialize next Data-Out
* @conn: iscsi connection
* @ctask: scsi command task
* @task: scsi command task
* @r2t: R2T info
* @left: bytes left to transfer
*
@ -1271,7 +1288,7 @@ iscsi_tcp_send_linear_data_prepare(struct iscsi_conn *conn, void *data,
* Called under connection lock.
**/
static int
iscsi_solicit_data_cont(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask,
iscsi_solicit_data_cont(struct iscsi_conn *conn, struct iscsi_task *task,
struct iscsi_r2t_info *r2t)
{
struct iscsi_data *hdr;
@ -1288,8 +1305,8 @@ iscsi_solicit_data_cont(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask,
hdr->datasn = cpu_to_be32(r2t->solicit_datasn);
r2t->solicit_datasn++;
hdr->opcode = ISCSI_OP_SCSI_DATA_OUT;
memcpy(hdr->lun, ctask->hdr->lun, sizeof(hdr->lun));
hdr->itt = ctask->hdr->itt;
memcpy(hdr->lun, task->hdr->lun, sizeof(hdr->lun));
hdr->itt = task->hdr->itt;
hdr->exp_statsn = r2t->exp_statsn;
new_offset = r2t->data_offset + r2t->sent;
hdr->offset = cpu_to_be32(new_offset);
@ -1307,89 +1324,76 @@ iscsi_solicit_data_cont(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask,
}
/**
* iscsi_tcp_ctask - Initialize iSCSI SCSI_READ or SCSI_WRITE commands
* iscsi_tcp_task - Initialize iSCSI SCSI_READ or SCSI_WRITE commands
* @conn: iscsi connection
* @ctask: scsi command task
* @task: scsi command task
* @sc: scsi command
**/
static int
iscsi_tcp_ctask_init(struct iscsi_cmd_task *ctask)
iscsi_tcp_task_init(struct iscsi_task *task)
{
struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
struct iscsi_conn *conn = ctask->conn;
struct scsi_cmnd *sc = ctask->sc;
struct iscsi_tcp_task *tcp_task = task->dd_data;
struct iscsi_conn *conn = task->conn;
struct scsi_cmnd *sc = task->sc;
int err;
BUG_ON(__kfifo_len(tcp_ctask->r2tqueue));
tcp_ctask->sent = 0;
tcp_ctask->exp_datasn = 0;
if (!sc) {
/*
* mgmt tasks do not have a scatterlist since they come
* in from the iscsi interface.
*/
debug_scsi("mtask deq [cid %d itt 0x%x]\n", conn->id,
task->itt);
/* Prepare PDU, optionally w/ immediate data */
iscsi_tcp_send_hdr_prep(conn, task->hdr, sizeof(*task->hdr));
/* If we have immediate data, attach a payload */
if (task->data_count)
iscsi_tcp_send_linear_data_prepare(conn, task->data,
task->data_count);
return 0;
}
BUG_ON(__kfifo_len(tcp_task->r2tqueue));
tcp_task->sent = 0;
tcp_task->exp_datasn = 0;
/* Prepare PDU, optionally w/ immediate data */
debug_scsi("ctask deq [cid %d itt 0x%x imm %d unsol %d]\n",
conn->id, ctask->itt, ctask->imm_count,
ctask->unsol_count);
iscsi_tcp_send_hdr_prep(conn, ctask->hdr, ctask->hdr_len);
debug_scsi("task deq [cid %d itt 0x%x imm %d unsol %d]\n",
conn->id, task->itt, task->imm_count,
task->unsol_count);
iscsi_tcp_send_hdr_prep(conn, task->hdr, task->hdr_len);
if (!ctask->imm_count)
if (!task->imm_count)
return 0;
/* If we have immediate data, attach a payload */
err = iscsi_tcp_send_data_prep(conn, scsi_out(sc)->table.sgl,
scsi_out(sc)->table.nents,
0, ctask->imm_count);
0, task->imm_count);
if (err)
return err;
tcp_ctask->sent += ctask->imm_count;
ctask->imm_count = 0;
return 0;
}
/**
* iscsi_tcp_mtask_xmit - xmit management(immediate) task
* @conn: iscsi connection
* @mtask: task management task
*
* Notes:
* The function can return -EAGAIN in which case caller must
* call it again later, or recover. '0' return code means successful
* xmit.
**/
static int
iscsi_tcp_mtask_xmit(struct iscsi_conn *conn, struct iscsi_mgmt_task *mtask)
{
int rc;
/* Flush any pending data first. */
rc = iscsi_tcp_flush(conn);
if (rc < 0)
return rc;
if (mtask->hdr->itt == RESERVED_ITT) {
struct iscsi_session *session = conn->session;
spin_lock_bh(&session->lock);
iscsi_free_mgmt_task(conn, mtask);
spin_unlock_bh(&session->lock);
}
tcp_task->sent += task->imm_count;
task->imm_count = 0;
return 0;
}
/*
* iscsi_tcp_ctask_xmit - xmit normal PDU task
* @conn: iscsi connection
* @ctask: iscsi command task
* iscsi_tcp_task_xmit - xmit normal PDU task
* @task: iscsi command task
*
* We're expected to return 0 when everything was transmitted succesfully,
* -EAGAIN if there's still data in the queue, or != 0 for any other kind
* of error.
*/
static int
iscsi_tcp_ctask_xmit(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
iscsi_tcp_task_xmit(struct iscsi_task *task)
{
struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
struct scsi_cmnd *sc = ctask->sc;
struct scsi_data_buffer *sdb = scsi_out(sc);
struct iscsi_conn *conn = task->conn;
struct iscsi_tcp_task *tcp_task = task->dd_data;
struct scsi_cmnd *sc = task->sc;
struct scsi_data_buffer *sdb;
int rc = 0;
flush:
@ -1398,31 +1402,39 @@ iscsi_tcp_ctask_xmit(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
if (rc < 0)
return rc;
/* mgmt command */
if (!sc) {
if (task->hdr->itt == RESERVED_ITT)
iscsi_put_task(task);
return 0;
}
/* Are we done already? */
if (sc->sc_data_direction != DMA_TO_DEVICE)
return 0;
if (ctask->unsol_count != 0) {
struct iscsi_data *hdr = &tcp_ctask->unsol_dtask.hdr;
sdb = scsi_out(sc);
if (task->unsol_count != 0) {
struct iscsi_data *hdr = &tcp_task->unsol_dtask.hdr;
/* Prepare a header for the unsolicited PDU.
* The amount of data we want to send will be
* in ctask->data_count.
* in task->data_count.
* FIXME: return the data count instead.
*/
iscsi_prep_unsolicit_data_pdu(ctask, hdr);
iscsi_prep_unsolicit_data_pdu(task, hdr);
debug_tcp("unsol dout [itt 0x%x doff %d dlen %d]\n",
ctask->itt, tcp_ctask->sent, ctask->data_count);
task->itt, tcp_task->sent, task->data_count);
iscsi_tcp_send_hdr_prep(conn, hdr, sizeof(*hdr));
rc = iscsi_tcp_send_data_prep(conn, sdb->table.sgl,
sdb->table.nents, tcp_ctask->sent,
ctask->data_count);
sdb->table.nents, tcp_task->sent,
task->data_count);
if (rc)
goto fail;
tcp_ctask->sent += ctask->data_count;
ctask->unsol_count -= ctask->data_count;
tcp_task->sent += task->data_count;
task->unsol_count -= task->data_count;
goto flush;
} else {
struct iscsi_session *session = conn->session;
@ -1431,22 +1443,22 @@ iscsi_tcp_ctask_xmit(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
/* All unsolicited PDUs sent. Check for solicited PDUs.
*/
spin_lock_bh(&session->lock);
r2t = tcp_ctask->r2t;
r2t = tcp_task->r2t;
if (r2t != NULL) {
/* Continue with this R2T? */
if (!iscsi_solicit_data_cont(conn, ctask, r2t)) {
if (!iscsi_solicit_data_cont(conn, task, r2t)) {
debug_scsi(" done with r2t %p\n", r2t);
__kfifo_put(tcp_ctask->r2tpool.queue,
__kfifo_put(tcp_task->r2tpool.queue,
(void*)&r2t, sizeof(void*));
tcp_ctask->r2t = r2t = NULL;
tcp_task->r2t = r2t = NULL;
}
}
if (r2t == NULL) {
__kfifo_get(tcp_ctask->r2tqueue, (void*)&tcp_ctask->r2t,
__kfifo_get(tcp_task->r2tqueue, (void*)&tcp_task->r2t,
sizeof(void*));
r2t = tcp_ctask->r2t;
r2t = tcp_task->r2t;
}
spin_unlock_bh(&session->lock);
@ -1457,7 +1469,7 @@ iscsi_tcp_ctask_xmit(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
}
debug_scsi("sol dout %p [dsn %d itt 0x%x doff %d dlen %d]\n",
r2t, r2t->solicit_datasn - 1, ctask->itt,
r2t, r2t->solicit_datasn - 1, task->itt,
r2t->data_offset + r2t->sent, r2t->data_count);
iscsi_tcp_send_hdr_prep(conn, &r2t->dtask.hdr,
@ -1469,7 +1481,7 @@ iscsi_tcp_ctask_xmit(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
r2t->data_count);
if (rc)
goto fail;
tcp_ctask->sent += r2t->data_count;
tcp_task->sent += r2t->data_count;
r2t->sent += r2t->data_count;
goto flush;
}
@ -1486,7 +1498,7 @@ iscsi_tcp_conn_create(struct iscsi_cls_session *cls_session, uint32_t conn_idx)
struct iscsi_cls_conn *cls_conn;
struct iscsi_tcp_conn *tcp_conn;
cls_conn = iscsi_conn_setup(cls_session, conn_idx);
cls_conn = iscsi_conn_setup(cls_session, sizeof(*tcp_conn), conn_idx);
if (!cls_conn)
return NULL;
conn = cls_conn->dd_data;
@ -1496,18 +1508,14 @@ iscsi_tcp_conn_create(struct iscsi_cls_session *cls_session, uint32_t conn_idx)
*/
conn->max_recv_dlength = ISCSI_DEF_MAX_RECV_SEG_LEN;
tcp_conn = kzalloc(sizeof(*tcp_conn), GFP_KERNEL);
if (!tcp_conn)
goto tcp_conn_alloc_fail;
conn->dd_data = tcp_conn;
tcp_conn = conn->dd_data;
tcp_conn->iscsi_conn = conn;
tcp_conn->tx_hash.tfm = crypto_alloc_hash("crc32c", 0,
CRYPTO_ALG_ASYNC);
tcp_conn->tx_hash.flags = 0;
if (IS_ERR(tcp_conn->tx_hash.tfm))
goto free_tcp_conn;
goto free_conn;
tcp_conn->rx_hash.tfm = crypto_alloc_hash("crc32c", 0,
CRYPTO_ALG_ASYNC);
@ -1519,14 +1527,12 @@ iscsi_tcp_conn_create(struct iscsi_cls_session *cls_session, uint32_t conn_idx)
free_tx_tfm:
crypto_free_hash(tcp_conn->tx_hash.tfm);
free_tcp_conn:
free_conn:
iscsi_conn_printk(KERN_ERR, conn,
"Could not create connection due to crc32c "
"loading error. Make sure the crc32c "
"module is built as a module or into the "
"kernel\n");
kfree(tcp_conn);
tcp_conn_alloc_fail:
iscsi_conn_teardown(cls_conn);
return NULL;
}
@ -1547,7 +1553,6 @@ iscsi_tcp_release_conn(struct iscsi_conn *conn)
spin_lock_bh(&session->lock);
tcp_conn->sock = NULL;
conn->recv_lock = NULL;
spin_unlock_bh(&session->lock);
sockfd_put(sock);
}
@ -1559,20 +1564,32 @@ iscsi_tcp_conn_destroy(struct iscsi_cls_conn *cls_conn)
struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
iscsi_tcp_release_conn(conn);
iscsi_conn_teardown(cls_conn);
if (tcp_conn->tx_hash.tfm)
crypto_free_hash(tcp_conn->tx_hash.tfm);
if (tcp_conn->rx_hash.tfm)
crypto_free_hash(tcp_conn->rx_hash.tfm);
kfree(tcp_conn);
iscsi_conn_teardown(cls_conn);
}
static void
iscsi_tcp_conn_stop(struct iscsi_cls_conn *cls_conn, int flag)
{
struct iscsi_conn *conn = cls_conn->dd_data;
struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
/* userspace may have goofed up and not bound us */
if (!tcp_conn->sock)
return;
/*
* Make sure our recv side is stopped.
* Older tools called conn stop before ep_disconnect
* so IO could still be coming in.
*/
write_lock_bh(&tcp_conn->sock->sk->sk_callback_lock);
set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_rx);
write_unlock_bh(&tcp_conn->sock->sk->sk_callback_lock);
iscsi_conn_stop(cls_conn, flag);
iscsi_tcp_release_conn(conn);
@ -1623,6 +1640,8 @@ iscsi_tcp_conn_bind(struct iscsi_cls_session *cls_session,
struct iscsi_cls_conn *cls_conn, uint64_t transport_eph,
int is_leading)
{
struct Scsi_Host *shost = iscsi_session_to_shost(cls_session);
struct iscsi_host *ihost = shost_priv(shost);
struct iscsi_conn *conn = cls_conn->dd_data;
struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
struct sock *sk;
@ -1646,8 +1665,8 @@ iscsi_tcp_conn_bind(struct iscsi_cls_session *cls_session,
if (err)
goto free_socket;
err = iscsi_tcp_get_addr(conn, sock, conn->local_address,
&conn->local_port, kernel_getsockname);
err = iscsi_tcp_get_addr(conn, sock, ihost->local_address,
&ihost->local_port, kernel_getsockname);
if (err)
goto free_socket;
@ -1664,13 +1683,6 @@ iscsi_tcp_conn_bind(struct iscsi_cls_session *cls_session,
sk->sk_sndtimeo = 15 * HZ; /* FIXME: make it configurable */
sk->sk_allocation = GFP_ATOMIC;
/* FIXME: disable Nagle's algorithm */
/*
* Intercept TCP callbacks for sendfile like receive
* processing.
*/
conn->recv_lock = &sk->sk_callback_lock;
iscsi_conn_set_callbacks(conn);
tcp_conn->sendpage = tcp_conn->sock->ops->sendpage;
/*
@ -1684,21 +1696,6 @@ iscsi_tcp_conn_bind(struct iscsi_cls_session *cls_session,
return err;
}
/* called with host lock */
static void
iscsi_tcp_mtask_init(struct iscsi_conn *conn, struct iscsi_mgmt_task *mtask)
{
debug_scsi("mtask deq [cid %d itt 0x%x]\n", conn->id, mtask->itt);
/* Prepare PDU, optionally w/ immediate data */
iscsi_tcp_send_hdr_prep(conn, mtask->hdr, sizeof(*mtask->hdr));
/* If we have immediate data, attach a payload */
if (mtask->data_count)
iscsi_tcp_send_linear_data_prepare(conn, mtask->data,
mtask->data_count);
}
static int
iscsi_r2tpool_alloc(struct iscsi_session *session)
{
@ -1709,8 +1706,8 @@ iscsi_r2tpool_alloc(struct iscsi_session *session)
* initialize per-task: R2T pool and xmit queue
*/
for (cmd_i = 0; cmd_i < session->cmds_max; cmd_i++) {
struct iscsi_cmd_task *ctask = session->cmds[cmd_i];
struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
struct iscsi_task *task = session->cmds[cmd_i];
struct iscsi_tcp_task *tcp_task = task->dd_data;
/*
* pre-allocated x4 as much r2ts to handle race when
@ -1719,16 +1716,16 @@ iscsi_r2tpool_alloc(struct iscsi_session *session)
*/
/* R2T pool */
if (iscsi_pool_init(&tcp_ctask->r2tpool, session->max_r2t * 4, NULL,
if (iscsi_pool_init(&tcp_task->r2tpool, session->max_r2t * 4, NULL,
sizeof(struct iscsi_r2t_info))) {
goto r2t_alloc_fail;
}
/* R2T xmit queue */
tcp_ctask->r2tqueue = kfifo_alloc(
tcp_task->r2tqueue = kfifo_alloc(
session->max_r2t * 4 * sizeof(void*), GFP_KERNEL, NULL);
if (tcp_ctask->r2tqueue == ERR_PTR(-ENOMEM)) {
iscsi_pool_free(&tcp_ctask->r2tpool);
if (tcp_task->r2tqueue == ERR_PTR(-ENOMEM)) {
iscsi_pool_free(&tcp_task->r2tpool);
goto r2t_alloc_fail;
}
}
@ -1737,11 +1734,11 @@ iscsi_r2tpool_alloc(struct iscsi_session *session)
r2t_alloc_fail:
for (i = 0; i < cmd_i; i++) {
struct iscsi_cmd_task *ctask = session->cmds[i];
struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
struct iscsi_task *task = session->cmds[i];
struct iscsi_tcp_task *tcp_task = task->dd_data;
kfifo_free(tcp_ctask->r2tqueue);
iscsi_pool_free(&tcp_ctask->r2tpool);
kfifo_free(tcp_task->r2tqueue);
iscsi_pool_free(&tcp_task->r2tpool);
}
return -ENOMEM;
}
@ -1752,11 +1749,11 @@ iscsi_r2tpool_free(struct iscsi_session *session)
int i;
for (i = 0; i < session->cmds_max; i++) {
struct iscsi_cmd_task *ctask = session->cmds[i];
struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
struct iscsi_task *task = session->cmds[i];
struct iscsi_tcp_task *tcp_task = task->dd_data;
kfifo_free(tcp_ctask->r2tqueue);
iscsi_pool_free(&tcp_ctask->r2tpool);
kfifo_free(tcp_task->r2tqueue);
iscsi_pool_free(&tcp_task->r2tpool);
}
}
@ -1821,29 +1818,6 @@ iscsi_tcp_conn_get_param(struct iscsi_cls_conn *cls_conn,
return len;
}
static int
iscsi_tcp_host_get_param(struct Scsi_Host *shost, enum iscsi_host_param param,
char *buf)
{
struct iscsi_session *session = iscsi_hostdata(shost->hostdata);
int len;
switch (param) {
case ISCSI_HOST_PARAM_IPADDRESS:
spin_lock_bh(&session->lock);
if (!session->leadconn)
len = -ENODEV;
else
len = sprintf(buf, "%s\n",
session->leadconn->local_address);
spin_unlock_bh(&session->lock);
break;
default:
return iscsi_host_get_param(shost, param, buf);
}
return len;
}
static void
iscsi_conn_get_stats(struct iscsi_cls_conn *cls_conn, struct iscsi_stats *stats)
{
@ -1869,54 +1843,70 @@ iscsi_conn_get_stats(struct iscsi_cls_conn *cls_conn, struct iscsi_stats *stats)
}
static struct iscsi_cls_session *
iscsi_tcp_session_create(struct iscsi_transport *iscsit,
struct scsi_transport_template *scsit,
uint16_t cmds_max, uint16_t qdepth,
uint32_t initial_cmdsn, uint32_t *hostno)
iscsi_tcp_session_create(struct iscsi_endpoint *ep, uint16_t cmds_max,
uint16_t qdepth, uint32_t initial_cmdsn,
uint32_t *hostno)
{
struct iscsi_cls_session *cls_session;
struct iscsi_session *session;
uint32_t hn;
struct Scsi_Host *shost;
int cmd_i;
cls_session = iscsi_session_setup(iscsit, scsit, cmds_max, qdepth,
sizeof(struct iscsi_tcp_cmd_task),
sizeof(struct iscsi_tcp_mgmt_task),
initial_cmdsn, &hn);
if (!cls_session)
if (ep) {
printk(KERN_ERR "iscsi_tcp: invalid ep %p.\n", ep);
return NULL;
*hostno = hn;
}
session = class_to_transport_session(cls_session);
shost = iscsi_host_alloc(&iscsi_sht, 0, qdepth);
if (!shost)
return NULL;
shost->transportt = iscsi_tcp_scsi_transport;
shost->max_lun = iscsi_max_lun;
shost->max_id = 0;
shost->max_channel = 0;
shost->max_cmd_len = SCSI_MAX_VARLEN_CDB_SIZE;
if (iscsi_host_add(shost, NULL))
goto free_host;
*hostno = shost->host_no;
cls_session = iscsi_session_setup(&iscsi_tcp_transport, shost, cmds_max,
sizeof(struct iscsi_tcp_task),
initial_cmdsn, 0);
if (!cls_session)
goto remove_host;
session = cls_session->dd_data;
shost->can_queue = session->scsi_cmds_max;
for (cmd_i = 0; cmd_i < session->cmds_max; cmd_i++) {
struct iscsi_cmd_task *ctask = session->cmds[cmd_i];
struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
struct iscsi_task *task = session->cmds[cmd_i];
struct iscsi_tcp_task *tcp_task = task->dd_data;
ctask->hdr = &tcp_ctask->hdr.cmd_hdr;
ctask->hdr_max = sizeof(tcp_ctask->hdr) - ISCSI_DIGEST_SIZE;
task->hdr = &tcp_task->hdr.cmd_hdr;
task->hdr_max = sizeof(tcp_task->hdr) - ISCSI_DIGEST_SIZE;
}
for (cmd_i = 0; cmd_i < session->mgmtpool_max; cmd_i++) {
struct iscsi_mgmt_task *mtask = session->mgmt_cmds[cmd_i];
struct iscsi_tcp_mgmt_task *tcp_mtask = mtask->dd_data;
mtask->hdr = (struct iscsi_hdr *) &tcp_mtask->hdr;
}
if (iscsi_r2tpool_alloc(class_to_transport_session(cls_session)))
goto r2tpool_alloc_fail;
if (iscsi_r2tpool_alloc(session))
goto remove_session;
return cls_session;
r2tpool_alloc_fail:
remove_session:
iscsi_session_teardown(cls_session);
remove_host:
iscsi_host_remove(shost);
free_host:
iscsi_host_free(shost);
return NULL;
}
static void iscsi_tcp_session_destroy(struct iscsi_cls_session *cls_session)
{
iscsi_r2tpool_free(class_to_transport_session(cls_session));
iscsi_session_teardown(cls_session);
struct Scsi_Host *shost = iscsi_session_to_shost(cls_session);
iscsi_r2tpool_free(cls_session->dd_data);
iscsi_host_remove(shost);
iscsi_host_free(shost);
}
static int iscsi_tcp_slave_configure(struct scsi_device *sdev)
@ -1971,14 +1961,11 @@ static struct iscsi_transport iscsi_tcp_transport = {
ISCSI_USERNAME_IN | ISCSI_PASSWORD_IN |
ISCSI_FAST_ABORT | ISCSI_ABORT_TMO |
ISCSI_LU_RESET_TMO |
ISCSI_PING_TMO | ISCSI_RECV_TMO,
ISCSI_PING_TMO | ISCSI_RECV_TMO |
ISCSI_IFACE_NAME | ISCSI_INITIATOR_NAME,
.host_param_mask = ISCSI_HOST_HWADDRESS | ISCSI_HOST_IPADDRESS |
ISCSI_HOST_INITIATOR_NAME |
ISCSI_HOST_NETDEV_NAME,
.host_template = &iscsi_sht,
.conndata_size = sizeof(struct iscsi_conn),
.max_conn = 1,
.max_cmd_len = 16,
/* session management */
.create_session = iscsi_tcp_session_create,
.destroy_session = iscsi_tcp_session_destroy,
@ -1992,16 +1979,14 @@ static struct iscsi_transport iscsi_tcp_transport = {
.start_conn = iscsi_conn_start,
.stop_conn = iscsi_tcp_conn_stop,
/* iscsi host params */
.get_host_param = iscsi_tcp_host_get_param,
.get_host_param = iscsi_host_get_param,
.set_host_param = iscsi_host_set_param,
/* IO */
.send_pdu = iscsi_conn_send_pdu,
.get_stats = iscsi_conn_get_stats,
.init_cmd_task = iscsi_tcp_ctask_init,
.init_mgmt_task = iscsi_tcp_mtask_init,
.xmit_cmd_task = iscsi_tcp_ctask_xmit,
.xmit_mgmt_task = iscsi_tcp_mtask_xmit,
.cleanup_cmd_task = iscsi_tcp_cleanup_ctask,
.init_task = iscsi_tcp_task_init,
.xmit_task = iscsi_tcp_task_xmit,
.cleanup_task = iscsi_tcp_cleanup_task,
/* recovery */
.session_recovery_timedout = iscsi_session_recovery_timedout,
};
@ -2014,9 +1999,10 @@ iscsi_tcp_init(void)
iscsi_max_lun);
return -EINVAL;
}
iscsi_tcp_transport.max_lun = iscsi_max_lun;
if (!iscsi_register_transport(&iscsi_tcp_transport))
iscsi_tcp_scsi_transport = iscsi_register_transport(
&iscsi_tcp_transport);
if (!iscsi_tcp_scsi_transport)
return -ENODEV;
return 0;

View File

@ -103,11 +103,6 @@ struct iscsi_data_task {
char hdrext[ISCSI_DIGEST_SIZE];/* Header-Digest */
};
struct iscsi_tcp_mgmt_task {
struct iscsi_hdr hdr;
char hdrext[ISCSI_DIGEST_SIZE]; /* Header-Digest */
};
struct iscsi_r2t_info {
__be32 ttt; /* copied from R2T */
__be32 exp_statsn; /* copied from R2T */
@ -119,7 +114,7 @@ struct iscsi_r2t_info {
struct iscsi_data_task dtask; /* Data-Out header buf */
};
struct iscsi_tcp_cmd_task {
struct iscsi_tcp_task {
struct iscsi_hdr_buff {
struct iscsi_cmd cmd_hdr;
char hdrextbuf[ISCSI_MAX_AHS_SIZE +

File diff suppressed because it is too large Load Diff

View File

@ -33,6 +33,7 @@ struct lpfc_sli2_slim;
#define LPFC_MAX_SG_SEG_CNT 256 /* sg element count per scsi cmnd */
#define LPFC_IOCB_LIST_CNT 2250 /* list of IOCBs for fast-path usage. */
#define LPFC_Q_RAMP_UP_INTERVAL 120 /* lun q_depth ramp up interval */
#define LPFC_VNAME_LEN 100 /* vport symbolic name length */
/*
* Following time intervals are used of adjusting SCSI device
@ -59,6 +60,9 @@ struct lpfc_sli2_slim;
#define MAX_HBAEVT 32
/* lpfc wait event data ready flag */
#define LPFC_DATA_READY (1<<0)
enum lpfc_polling_flags {
ENABLE_FCP_RING_POLLING = 0x1,
DISABLE_FCP_RING_INT = 0x2
@ -425,9 +429,6 @@ struct lpfc_hba {
uint16_t pci_cfg_value;
uint8_t work_found;
#define LPFC_MAX_WORKER_ITERATION 4
uint8_t fc_linkspeed; /* Link speed after last READ_LA */
uint32_t fc_eventTag; /* event tag for link attention */
@ -489,8 +490,9 @@ struct lpfc_hba {
uint32_t work_hs; /* HS stored in case of ERRAT */
uint32_t work_status[2]; /* Extra status from SLIM */
wait_queue_head_t *work_wait;
wait_queue_head_t work_waitq;
struct task_struct *worker_thread;
long data_flags;
uint32_t hbq_in_use; /* HBQs in use flag */
struct list_head hbqbuf_in_list; /* in-fly hbq buffer list */
@ -637,6 +639,17 @@ lpfc_is_link_up(struct lpfc_hba *phba)
phba->link_state == LPFC_HBA_READY;
}
static inline void
lpfc_worker_wake_up(struct lpfc_hba *phba)
{
/* Set the lpfc data pending flag */
set_bit(LPFC_DATA_READY, &phba->data_flags);
/* Wake up worker thread */
wake_up(&phba->work_waitq);
return;
}
#define FC_REG_DUMP_EVENT 0x10 /* Register for Dump events */
#define FC_REG_TEMPERATURE_EVENT 0x20 /* Register for temperature
event */

View File

@ -1995,8 +1995,7 @@ sysfs_mbox_read(struct kobject *kobj, struct bin_attribute *bin_attr,
/* Don't allow mailbox commands to be sent when blocked
* or when in the middle of discovery
*/
if (phba->sli.sli_flag & LPFC_BLOCK_MGMT_IO ||
vport->fc_flag & FC_NDISC_ACTIVE) {
if (phba->sli.sli_flag & LPFC_BLOCK_MGMT_IO) {
sysfs_mbox_idle(phba);
spin_unlock_irq(&phba->hbalock);
return -EAGAIN;

View File

@ -142,7 +142,7 @@ int lpfc_config_port_post(struct lpfc_hba *);
int lpfc_hba_down_prep(struct lpfc_hba *);
int lpfc_hba_down_post(struct lpfc_hba *);
void lpfc_hba_init(struct lpfc_hba *, uint32_t *);
int lpfc_post_buffer(struct lpfc_hba *, struct lpfc_sli_ring *, int, int);
int lpfc_post_buffer(struct lpfc_hba *, struct lpfc_sli_ring *, int);
void lpfc_decode_firmware_rev(struct lpfc_hba *, char *, int);
int lpfc_online(struct lpfc_hba *);
void lpfc_unblock_mgmt_io(struct lpfc_hba *);
@ -263,6 +263,7 @@ extern int lpfc_sli_mode;
extern int lpfc_enable_npiv;
int lpfc_vport_symbolic_node_name(struct lpfc_vport *, char *, size_t);
int lpfc_vport_symbolic_port_name(struct lpfc_vport *, char *, size_t);
void lpfc_terminate_rport_io(struct fc_rport *);
void lpfc_dev_loss_tmo_callbk(struct fc_rport *rport);

View File

@ -101,7 +101,7 @@ lpfc_ct_unsol_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
/* Not enough posted buffers; Try posting more buffers */
phba->fc_stat.NoRcvBuf++;
if (!(phba->sli3_options & LPFC_SLI3_HBQ_ENABLED))
lpfc_post_buffer(phba, pring, 2, 1);
lpfc_post_buffer(phba, pring, 2);
return;
}
@ -151,7 +151,7 @@ lpfc_ct_unsol_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
}
list_del(&iocbq->list);
lpfc_sli_release_iocbq(phba, iocbq);
lpfc_post_buffer(phba, pring, i, 1);
lpfc_post_buffer(phba, pring, i);
}
}
}
@ -990,7 +990,7 @@ lpfc_cmpl_ct_cmd_rff_id(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
return;
}
static int
int
lpfc_vport_symbolic_port_name(struct lpfc_vport *vport, char *symbol,
size_t size)
{
@ -1679,20 +1679,18 @@ lpfc_fdmi_tmo(unsigned long ptr)
{
struct lpfc_vport *vport = (struct lpfc_vport *)ptr;
struct lpfc_hba *phba = vport->phba;
uint32_t tmo_posted;
unsigned long iflag;
spin_lock_irqsave(&vport->work_port_lock, iflag);
if (!(vport->work_port_events & WORKER_FDMI_TMO)) {
tmo_posted = vport->work_port_events & WORKER_FDMI_TMO;
if (!tmo_posted)
vport->work_port_events |= WORKER_FDMI_TMO;
spin_unlock_irqrestore(&vport->work_port_lock, iflag);
spin_unlock_irqrestore(&vport->work_port_lock, iflag);
spin_lock_irqsave(&phba->hbalock, iflag);
if (phba->work_wait)
lpfc_worker_wake_up(phba);
spin_unlock_irqrestore(&phba->hbalock, iflag);
}
else
spin_unlock_irqrestore(&vport->work_port_lock, iflag);
if (!tmo_posted)
lpfc_worker_wake_up(phba);
return;
}
void

View File

@ -1754,29 +1754,34 @@ lpfc_cancel_retry_delay_tmo(struct lpfc_vport *vport, struct lpfc_nodelist *nlp)
struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
struct lpfc_work_evt *evtp;
if (!(nlp->nlp_flag & NLP_DELAY_TMO))
return;
spin_lock_irq(shost->host_lock);
nlp->nlp_flag &= ~NLP_DELAY_TMO;
spin_unlock_irq(shost->host_lock);
del_timer_sync(&nlp->nlp_delayfunc);
nlp->nlp_last_elscmd = 0;
if (!list_empty(&nlp->els_retry_evt.evt_listp)) {
list_del_init(&nlp->els_retry_evt.evt_listp);
/* Decrement nlp reference count held for the delayed retry */
evtp = &nlp->els_retry_evt;
lpfc_nlp_put((struct lpfc_nodelist *)evtp->evt_arg1);
}
if (nlp->nlp_flag & NLP_NPR_2B_DISC) {
spin_lock_irq(shost->host_lock);
nlp->nlp_flag &= ~NLP_NPR_2B_DISC;
spin_unlock_irq(shost->host_lock);
if (vport->num_disc_nodes) {
/* Check to see if there are more
* PLOGIs to be sent
*/
lpfc_more_plogi(vport);
if (vport->port_state < LPFC_VPORT_READY) {
/* Check if there are more ADISCs to be sent */
lpfc_more_adisc(vport);
if ((vport->num_disc_nodes == 0) &&
(vport->fc_npr_cnt))
lpfc_els_disc_plogi(vport);
} else {
/* Check if there are more PLOGIs to be sent */
lpfc_more_plogi(vport);
}
if (vport->num_disc_nodes == 0) {
spin_lock_irq(shost->host_lock);
vport->fc_flag &= ~FC_NDISC_ACTIVE;
@ -1798,10 +1803,6 @@ lpfc_els_retry_delay(unsigned long ptr)
unsigned long flags;
struct lpfc_work_evt *evtp = &ndlp->els_retry_evt;
ndlp = (struct lpfc_nodelist *) ptr;
phba = ndlp->vport->phba;
evtp = &ndlp->els_retry_evt;
spin_lock_irqsave(&phba->hbalock, flags);
if (!list_empty(&evtp->evt_listp)) {
spin_unlock_irqrestore(&phba->hbalock, flags);
@ -1812,11 +1813,11 @@ lpfc_els_retry_delay(unsigned long ptr)
* count until the queued work is done
*/
evtp->evt_arg1 = lpfc_nlp_get(ndlp);
evtp->evt = LPFC_EVT_ELS_RETRY;
list_add_tail(&evtp->evt_listp, &phba->work_list);
if (phba->work_wait)
if (evtp->evt_arg1) {
evtp->evt = LPFC_EVT_ELS_RETRY;
list_add_tail(&evtp->evt_listp, &phba->work_list);
lpfc_worker_wake_up(phba);
}
spin_unlock_irqrestore(&phba->hbalock, flags);
return;
}
@ -2761,10 +2762,11 @@ lpfc_els_rsp_prli_acc(struct lpfc_vport *vport, struct lpfc_iocbq *oldiocb,
npr = (PRLI *) pcmd;
vpd = &phba->vpd;
/*
* If our firmware version is 3.20 or later,
* set the following bits for FC-TAPE support.
* If the remote port is a target and our firmware version is 3.20 or
* later, set the following bits for FC-TAPE support.
*/
if (vpd->rev.feaLevelHigh >= 0x02) {
if ((ndlp->nlp_type & NLP_FCP_TARGET) &&
(vpd->rev.feaLevelHigh >= 0x02)) {
npr->ConfmComplAllowed = 1;
npr->Retry = 1;
npr->TaskRetryIdReq = 1;
@ -3056,27 +3058,16 @@ lpfc_rscn_recovery_check(struct lpfc_vport *vport)
{
struct lpfc_nodelist *ndlp = NULL;
/* Look at all nodes effected by pending RSCNs and move
* them to NPR state.
*/
/* Move all affected nodes by pending RSCNs to NPR state. */
list_for_each_entry(ndlp, &vport->fc_nodes, nlp_listp) {
if (!NLP_CHK_NODE_ACT(ndlp) ||
ndlp->nlp_state == NLP_STE_UNUSED_NODE ||
lpfc_rscn_payload_check(vport, ndlp->nlp_DID) == 0)
(ndlp->nlp_state == NLP_STE_UNUSED_NODE) ||
!lpfc_rscn_payload_check(vport, ndlp->nlp_DID))
continue;
lpfc_disc_state_machine(vport, ndlp, NULL,
NLP_EVT_DEVICE_RECOVERY);
/*
* Make sure NLP_DELAY_TMO is NOT running after a device
* recovery event.
*/
if (ndlp->nlp_flag & NLP_DELAY_TMO)
lpfc_cancel_retry_delay_tmo(vport, ndlp);
NLP_EVT_DEVICE_RECOVERY);
lpfc_cancel_retry_delay_tmo(vport, ndlp);
}
return 0;
}
@ -3781,91 +3772,27 @@ static int
lpfc_els_rcv_fan(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
struct lpfc_nodelist *fan_ndlp)
{
struct lpfc_dmabuf *pcmd;
uint32_t *lp;
IOCB_t *icmd;
uint32_t cmd, did;
FAN *fp;
struct lpfc_nodelist *ndlp, *next_ndlp;
struct lpfc_hba *phba = vport->phba;
uint32_t *lp;
FAN *fp;
/* FAN received */
lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
"0265 FAN received\n");
icmd = &cmdiocb->iocb;
did = icmd->un.elsreq64.remoteID;
pcmd = (struct lpfc_dmabuf *)cmdiocb->context2;
lp = (uint32_t *)pcmd->virt;
cmd = *lp++;
fp = (FAN *) lp;
lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS, "0265 FAN received\n");
lp = (uint32_t *)((struct lpfc_dmabuf *)cmdiocb->context2)->virt;
fp = (FAN *) ++lp;
/* FAN received; Fan does not have a reply sequence */
if (phba->pport->port_state == LPFC_LOCAL_CFG_LINK) {
if ((vport == phba->pport) &&
(vport->port_state == LPFC_LOCAL_CFG_LINK)) {
if ((memcmp(&phba->fc_fabparam.nodeName, &fp->FnodeName,
sizeof(struct lpfc_name)) != 0) ||
sizeof(struct lpfc_name))) ||
(memcmp(&phba->fc_fabparam.portName, &fp->FportName,
sizeof(struct lpfc_name)) != 0)) {
/*
* This node has switched fabrics. FLOGI is required
* Clean up the old rpi's
*/
list_for_each_entry_safe(ndlp, next_ndlp,
&vport->fc_nodes, nlp_listp) {
if (!NLP_CHK_NODE_ACT(ndlp))
continue;
if (ndlp->nlp_state != NLP_STE_NPR_NODE)
continue;
if (ndlp->nlp_type & NLP_FABRIC) {
/*
* Clean up old Fabric, Nameserver and
* other NLP_FABRIC logins
*/
lpfc_drop_node(vport, ndlp);
} else if (!(ndlp->nlp_flag & NLP_NPR_ADISC)) {
/* Fail outstanding I/O now since this
* device is marked for PLOGI
*/
lpfc_unreg_rpi(vport, ndlp);
}
}
sizeof(struct lpfc_name)))) {
/* This port has switched fabrics. FLOGI is required */
lpfc_initial_flogi(vport);
return 0;
} else {
/* FAN verified - skip FLOGI */
vport->fc_myDID = vport->fc_prevDID;
lpfc_issue_fabric_reglogin(vport);
}
/* Discovery not needed,
* move the nodes to their original state.
*/
list_for_each_entry_safe(ndlp, next_ndlp, &vport->fc_nodes,
nlp_listp) {
if (!NLP_CHK_NODE_ACT(ndlp))
continue;
if (ndlp->nlp_state != NLP_STE_NPR_NODE)
continue;
switch (ndlp->nlp_prev_state) {
case NLP_STE_UNMAPPED_NODE:
ndlp->nlp_prev_state = NLP_STE_NPR_NODE;
lpfc_nlp_set_state(vport, ndlp,
NLP_STE_UNMAPPED_NODE);
break;
case NLP_STE_MAPPED_NODE:
ndlp->nlp_prev_state = NLP_STE_NPR_NODE;
lpfc_nlp_set_state(vport, ndlp,
NLP_STE_MAPPED_NODE);
break;
default:
break;
}
}
/* Start discovery - this should just do CLEAR_LA */
lpfc_disc_start(vport);
}
return 0;
}
@ -3875,20 +3802,17 @@ lpfc_els_timeout(unsigned long ptr)
{
struct lpfc_vport *vport = (struct lpfc_vport *) ptr;
struct lpfc_hba *phba = vport->phba;
uint32_t tmo_posted;
unsigned long iflag;
spin_lock_irqsave(&vport->work_port_lock, iflag);
if ((vport->work_port_events & WORKER_ELS_TMO) == 0) {
tmo_posted = vport->work_port_events & WORKER_ELS_TMO;
if (!tmo_posted)
vport->work_port_events |= WORKER_ELS_TMO;
spin_unlock_irqrestore(&vport->work_port_lock, iflag);
spin_unlock_irqrestore(&vport->work_port_lock, iflag);
spin_lock_irqsave(&phba->hbalock, iflag);
if (phba->work_wait)
lpfc_worker_wake_up(phba);
spin_unlock_irqrestore(&phba->hbalock, iflag);
}
else
spin_unlock_irqrestore(&vport->work_port_lock, iflag);
if (!tmo_posted)
lpfc_worker_wake_up(phba);
return;
}
@ -3933,9 +3857,6 @@ lpfc_els_timeout_handler(struct lpfc_vport *vport)
els_command == ELS_CMD_FDISC)
continue;
if (vport != piocb->vport)
continue;
if (piocb->drvrTimeout > 0) {
if (piocb->drvrTimeout >= timeout)
piocb->drvrTimeout -= timeout;
@ -4089,7 +4010,7 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
payload = ((struct lpfc_dmabuf *)elsiocb->context2)->virt;
cmd = *payload;
if ((phba->sli3_options & LPFC_SLI3_HBQ_ENABLED) == 0)
lpfc_post_buffer(phba, pring, 1, 1);
lpfc_post_buffer(phba, pring, 1);
did = icmd->un.rcvels.remoteID;
if (icmd->ulpStatus) {
@ -4398,7 +4319,7 @@ lpfc_els_unsol_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
phba->fc_stat.NoRcvBuf++;
/* Not enough posted buffers; Try posting more buffers */
if (!(phba->sli3_options & LPFC_SLI3_HBQ_ENABLED))
lpfc_post_buffer(phba, pring, 0, 1);
lpfc_post_buffer(phba, pring, 0);
return;
}
@ -4842,18 +4763,16 @@ lpfc_fabric_block_timeout(unsigned long ptr)
struct lpfc_hba *phba = (struct lpfc_hba *) ptr;
unsigned long iflags;
uint32_t tmo_posted;
spin_lock_irqsave(&phba->pport->work_port_lock, iflags);
tmo_posted = phba->pport->work_port_events & WORKER_FABRIC_BLOCK_TMO;
if (!tmo_posted)
phba->pport->work_port_events |= WORKER_FABRIC_BLOCK_TMO;
spin_unlock_irqrestore(&phba->pport->work_port_lock, iflags);
if (!tmo_posted) {
spin_lock_irqsave(&phba->hbalock, iflags);
if (phba->work_wait)
lpfc_worker_wake_up(phba);
spin_unlock_irqrestore(&phba->hbalock, iflags);
}
if (!tmo_posted)
lpfc_worker_wake_up(phba);
return;
}
static void

View File

@ -153,11 +153,11 @@ lpfc_dev_loss_tmo_callbk(struct fc_rport *rport)
* count until this queued work is done
*/
evtp->evt_arg1 = lpfc_nlp_get(ndlp);
evtp->evt = LPFC_EVT_DEV_LOSS;
list_add_tail(&evtp->evt_listp, &phba->work_list);
if (phba->work_wait)
wake_up(phba->work_wait);
if (evtp->evt_arg1) {
evtp->evt = LPFC_EVT_DEV_LOSS;
list_add_tail(&evtp->evt_listp, &phba->work_list);
lpfc_worker_wake_up(phba);
}
spin_unlock_irq(&phba->hbalock);
return;
@ -276,14 +276,6 @@ lpfc_dev_loss_tmo_handler(struct lpfc_nodelist *ndlp)
lpfc_disc_state_machine(vport, ndlp, NULL, NLP_EVT_DEVICE_RM);
}
void
lpfc_worker_wake_up(struct lpfc_hba *phba)
{
wake_up(phba->work_wait);
return;
}
static void
lpfc_work_list_done(struct lpfc_hba *phba)
{
@ -429,6 +421,8 @@ lpfc_work_done(struct lpfc_hba *phba)
|| (pring->flag & LPFC_DEFERRED_RING_EVENT)) {
if (pring->flag & LPFC_STOP_IOCB_EVENT) {
pring->flag |= LPFC_DEFERRED_RING_EVENT;
/* Set the lpfc data pending flag */
set_bit(LPFC_DATA_READY, &phba->data_flags);
} else {
pring->flag &= ~LPFC_DEFERRED_RING_EVENT;
lpfc_sli_handle_slow_ring_event(phba, pring,
@ -459,69 +453,29 @@ lpfc_work_done(struct lpfc_hba *phba)
lpfc_work_list_done(phba);
}
static int
check_work_wait_done(struct lpfc_hba *phba)
{
struct lpfc_vport *vport;
struct lpfc_sli_ring *pring = &phba->sli.ring[LPFC_ELS_RING];
int rc = 0;
spin_lock_irq(&phba->hbalock);
list_for_each_entry(vport, &phba->port_list, listentry) {
if (vport->work_port_events) {
rc = 1;
break;
}
}
if (rc || phba->work_ha || (!list_empty(&phba->work_list)) ||
kthread_should_stop() || pring->flag & LPFC_DEFERRED_RING_EVENT) {
rc = 1;
phba->work_found++;
} else
phba->work_found = 0;
spin_unlock_irq(&phba->hbalock);
return rc;
}
int
lpfc_do_work(void *p)
{
struct lpfc_hba *phba = p;
int rc;
DECLARE_WAIT_QUEUE_HEAD_ONSTACK(work_waitq);
set_user_nice(current, -20);
phba->work_wait = &work_waitq;
phba->work_found = 0;
phba->data_flags = 0;
while (1) {
rc = wait_event_interruptible(work_waitq,
check_work_wait_done(phba));
/* wait and check worker queue activities */
rc = wait_event_interruptible(phba->work_waitq,
(test_and_clear_bit(LPFC_DATA_READY,
&phba->data_flags)
|| kthread_should_stop()));
BUG_ON(rc);
if (kthread_should_stop())
break;
/* Attend pending lpfc data processing */
lpfc_work_done(phba);
/* If there is alot of slow ring work, like during link up
* check_work_wait_done() may cause this thread to not give
* up the CPU for very long periods of time. This may cause
* soft lockups or other problems. To avoid these situations
* give up the CPU here after LPFC_MAX_WORKER_ITERATION
* consecutive iterations.
*/
if (phba->work_found >= LPFC_MAX_WORKER_ITERATION) {
phba->work_found = 0;
schedule();
}
}
spin_lock_irq(&phba->hbalock);
phba->work_wait = NULL;
spin_unlock_irq(&phba->hbalock);
return 0;
}
@ -551,10 +505,10 @@ lpfc_workq_post_event(struct lpfc_hba *phba, void *arg1, void *arg2,
spin_lock_irqsave(&phba->hbalock, flags);
list_add_tail(&evtp->evt_listp, &phba->work_list);
if (phba->work_wait)
lpfc_worker_wake_up(phba);
spin_unlock_irqrestore(&phba->hbalock, flags);
lpfc_worker_wake_up(phba);
return 1;
}
@ -963,6 +917,10 @@ lpfc_mbx_process_link_up(struct lpfc_hba *phba, READ_LA_VAR *la)
if (phba->fc_topology == TOPOLOGY_LOOP) {
phba->sli3_options &= ~LPFC_SLI3_NPIV_ENABLED;
if (phba->cfg_enable_npiv)
lpfc_printf_log(phba, KERN_ERR, LOG_LINK_EVENT,
"1309 Link Up Event npiv not supported in loop "
"topology\n");
/* Get Loop Map information */
if (la->il)
vport->fc_flag |= FC_LBIT;
@ -1087,6 +1045,8 @@ lpfc_mbx_cmpl_read_la(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
MAILBOX_t *mb = &pmb->mb;
struct lpfc_dmabuf *mp = (struct lpfc_dmabuf *) (pmb->context1);
/* Unblock ELS traffic */
phba->sli.ring[LPFC_ELS_RING].flag &= ~LPFC_STOP_IOCB_EVENT;
/* Check for error */
if (mb->mbxStatus) {
lpfc_printf_log(phba, KERN_INFO, LOG_LINK_EVENT,
@ -1650,7 +1610,6 @@ lpfc_nlp_set_state(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
ndlp->nlp_DID, old_state, state);
if (old_state == NLP_STE_NPR_NODE &&
(ndlp->nlp_flag & NLP_DELAY_TMO) != 0 &&
state != NLP_STE_NPR_NODE)
lpfc_cancel_retry_delay_tmo(vport, ndlp);
if (old_state == NLP_STE_UNMAPPED_NODE) {
@ -1687,8 +1646,7 @@ lpfc_dequeue_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
{
struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
if ((ndlp->nlp_flag & NLP_DELAY_TMO) != 0)
lpfc_cancel_retry_delay_tmo(vport, ndlp);
lpfc_cancel_retry_delay_tmo(vport, ndlp);
if (ndlp->nlp_state && !list_empty(&ndlp->nlp_listp))
lpfc_nlp_counters(vport, ndlp->nlp_state, -1);
spin_lock_irq(shost->host_lock);
@ -1701,8 +1659,7 @@ lpfc_dequeue_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
static void
lpfc_disable_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
{
if ((ndlp->nlp_flag & NLP_DELAY_TMO) != 0)
lpfc_cancel_retry_delay_tmo(vport, ndlp);
lpfc_cancel_retry_delay_tmo(vport, ndlp);
if (ndlp->nlp_state && !list_empty(&ndlp->nlp_listp))
lpfc_nlp_counters(vport, ndlp->nlp_state, -1);
lpfc_nlp_state_cleanup(vport, ndlp, ndlp->nlp_state,
@ -2121,10 +2078,8 @@ lpfc_cleanup_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
ndlp->nlp_last_elscmd = 0;
del_timer_sync(&ndlp->nlp_delayfunc);
if (!list_empty(&ndlp->els_retry_evt.evt_listp))
list_del_init(&ndlp->els_retry_evt.evt_listp);
if (!list_empty(&ndlp->dev_loss_evt.evt_listp))
list_del_init(&ndlp->dev_loss_evt.evt_listp);
list_del_init(&ndlp->els_retry_evt.evt_listp);
list_del_init(&ndlp->dev_loss_evt.evt_listp);
lpfc_unreg_rpi(vport, ndlp);
@ -2144,10 +2099,7 @@ lpfc_nlp_remove(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
LPFC_MBOXQ_t *mbox;
int rc;
if (ndlp->nlp_flag & NLP_DELAY_TMO) {
lpfc_cancel_retry_delay_tmo(vport, ndlp);
}
lpfc_cancel_retry_delay_tmo(vport, ndlp);
if (ndlp->nlp_flag & NLP_DEFER_RM && !ndlp->nlp_rpi) {
/* For this case we need to cleanup the default rpi
* allocated by the firmware.
@ -2317,8 +2269,7 @@ lpfc_setup_disc_node(struct lpfc_vport *vport, uint32_t did)
/* Since this node is marked for discovery,
* delay timeout is not needed.
*/
if (ndlp->nlp_flag & NLP_DELAY_TMO)
lpfc_cancel_retry_delay_tmo(vport, ndlp);
lpfc_cancel_retry_delay_tmo(vport, ndlp);
} else
ndlp = NULL;
} else {
@ -2643,21 +2594,20 @@ lpfc_disc_timeout(unsigned long ptr)
{
struct lpfc_vport *vport = (struct lpfc_vport *) ptr;
struct lpfc_hba *phba = vport->phba;
uint32_t tmo_posted;
unsigned long flags = 0;
if (unlikely(!phba))
return;
if ((vport->work_port_events & WORKER_DISC_TMO) == 0) {
spin_lock_irqsave(&vport->work_port_lock, flags);
spin_lock_irqsave(&vport->work_port_lock, flags);
tmo_posted = vport->work_port_events & WORKER_DISC_TMO;
if (!tmo_posted)
vport->work_port_events |= WORKER_DISC_TMO;
spin_unlock_irqrestore(&vport->work_port_lock, flags);
spin_unlock_irqrestore(&vport->work_port_lock, flags);
spin_lock_irqsave(&phba->hbalock, flags);
if (phba->work_wait)
lpfc_worker_wake_up(phba);
spin_unlock_irqrestore(&phba->hbalock, flags);
}
if (!tmo_posted)
lpfc_worker_wake_up(phba);
return;
}

View File

@ -145,8 +145,10 @@ lpfc_config_port_prep(struct lpfc_hba *phba)
return -ERESTART;
}
if (phba->sli_rev == 3 && !mb->un.varRdRev.v3rsp)
if (phba->sli_rev == 3 && !mb->un.varRdRev.v3rsp) {
mempool_free(pmb, phba->mbox_mem_pool);
return -EINVAL;
}
/* Save information as VPD data */
vp->rev.rBit = 1;
@ -551,18 +553,18 @@ static void
lpfc_hb_timeout(unsigned long ptr)
{
struct lpfc_hba *phba;
uint32_t tmo_posted;
unsigned long iflag;
phba = (struct lpfc_hba *)ptr;
spin_lock_irqsave(&phba->pport->work_port_lock, iflag);
if (!(phba->pport->work_port_events & WORKER_HB_TMO))
tmo_posted = phba->pport->work_port_events & WORKER_HB_TMO;
if (!tmo_posted)
phba->pport->work_port_events |= WORKER_HB_TMO;
spin_unlock_irqrestore(&phba->pport->work_port_lock, iflag);
spin_lock_irqsave(&phba->hbalock, iflag);
if (phba->work_wait)
wake_up(phba->work_wait);
spin_unlock_irqrestore(&phba->hbalock, iflag);
if (!tmo_posted)
lpfc_worker_wake_up(phba);
return;
}
@ -851,6 +853,8 @@ lpfc_handle_latt(struct lpfc_hba *phba)
lpfc_read_la(phba, pmb, mp);
pmb->mbox_cmpl = lpfc_mbx_cmpl_read_la;
pmb->vport = vport;
/* Block ELS IOCBs until we have processed this mbox command */
phba->sli.ring[LPFC_ELS_RING].flag |= LPFC_STOP_IOCB_EVENT;
rc = lpfc_sli_issue_mbox (phba, pmb, MBX_NOWAIT);
if (rc == MBX_NOT_FINISHED) {
rc = 4;
@ -866,6 +870,7 @@ lpfc_handle_latt(struct lpfc_hba *phba)
return;
lpfc_handle_latt_free_mbuf:
phba->sli.ring[LPFC_ELS_RING].flag &= ~LPFC_STOP_IOCB_EVENT;
lpfc_mbuf_free(phba, mp->virt, mp->phys);
lpfc_handle_latt_free_mp:
kfree(mp);
@ -1194,8 +1199,7 @@ lpfc_get_hba_model_desc(struct lpfc_hba *phba, uint8_t *mdp, uint8_t *descp)
/* Returns the number of buffers NOT posted. */
/**************************************************/
int
lpfc_post_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, int cnt,
int type)
lpfc_post_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, int cnt)
{
IOCB_t *icmd;
struct lpfc_iocbq *iocb;
@ -1295,7 +1299,7 @@ lpfc_post_rcv_buf(struct lpfc_hba *phba)
struct lpfc_sli *psli = &phba->sli;
/* Ring 0, ELS / CT buffers */
lpfc_post_buffer(phba, &psli->ring[LPFC_ELS_RING], LPFC_BUF_RING0, 1);
lpfc_post_buffer(phba, &psli->ring[LPFC_ELS_RING], LPFC_BUF_RING0);
/* Ring 2 - FCP no buffers needed */
return 0;
@ -1454,6 +1458,15 @@ lpfc_cleanup(struct lpfc_vport *vport)
lpfc_disc_state_machine(vport, ndlp, NULL,
NLP_EVT_DEVICE_RM);
/* nlp_type zero is not defined, nlp_flag zero also not defined,
* nlp_state is unused, this happens when
* an initiator has logged
* into us so cleanup this ndlp.
*/
if ((ndlp->nlp_type == 0) && (ndlp->nlp_flag == 0) &&
(ndlp->nlp_state == 0))
lpfc_nlp_put(ndlp);
}
/* At this point, ALL ndlp's should be gone
@ -2101,6 +2114,9 @@ lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid)
phba->work_ha_mask = (HA_ERATT|HA_MBATT|HA_LATT);
phba->work_ha_mask |= (HA_RXMASK << (LPFC_ELS_RING * 4));
/* Initialize the wait queue head for the kernel thread */
init_waitqueue_head(&phba->work_waitq);
/* Startup the kernel thread for this host adapter. */
phba->worker_thread = kthread_run(lpfc_do_work, phba,
"lpfc_worker_%d", phba->brd_no);

View File

@ -235,10 +235,7 @@ lpfc_els_abort(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp)
(iocb->iocb_cmpl) (phba, iocb, iocb);
}
}
/* If we are delaying issuing an ELS command, cancel it */
if (ndlp->nlp_flag & NLP_DELAY_TMO)
lpfc_cancel_retry_delay_tmo(phba->pport, ndlp);
lpfc_cancel_retry_delay_tmo(phba->pport, ndlp);
return 0;
}
@ -249,7 +246,6 @@ lpfc_rcv_plogi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
struct lpfc_hba *phba = vport->phba;
struct lpfc_dmabuf *pcmd;
struct lpfc_work_evt *evtp;
uint32_t *lp;
IOCB_t *icmd;
struct serv_parm *sp;
@ -425,73 +421,8 @@ lpfc_rcv_plogi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
ndlp, mbox);
return 1;
}
/* If the remote NPort logs into us, before we can initiate
* discovery to them, cleanup the NPort from discovery accordingly.
*/
if (ndlp->nlp_state == NLP_STE_NPR_NODE) {
spin_lock_irq(shost->host_lock);
ndlp->nlp_flag &= ~NLP_DELAY_TMO;
spin_unlock_irq(shost->host_lock);
del_timer_sync(&ndlp->nlp_delayfunc);
ndlp->nlp_last_elscmd = 0;
if (!list_empty(&ndlp->els_retry_evt.evt_listp)) {
list_del_init(&ndlp->els_retry_evt.evt_listp);
/* Decrement ndlp reference count held for the
* delayed retry
*/
evtp = &ndlp->els_retry_evt;
lpfc_nlp_put((struct lpfc_nodelist *)evtp->evt_arg1);
}
if (ndlp->nlp_flag & NLP_NPR_2B_DISC) {
spin_lock_irq(shost->host_lock);
ndlp->nlp_flag &= ~NLP_NPR_2B_DISC;
spin_unlock_irq(shost->host_lock);
if ((ndlp->nlp_flag & NLP_ADISC_SND) &&
(vport->num_disc_nodes)) {
/* Check to see if there are more
* ADISCs to be sent
*/
lpfc_more_adisc(vport);
if ((vport->num_disc_nodes == 0) &&
(vport->fc_npr_cnt))
lpfc_els_disc_plogi(vport);
if (vport->num_disc_nodes == 0) {
spin_lock_irq(shost->host_lock);
vport->fc_flag &= ~FC_NDISC_ACTIVE;
spin_unlock_irq(shost->host_lock);
lpfc_can_disctmo(vport);
lpfc_end_rscn(vport);
}
}
}
} else if ((ndlp->nlp_state == NLP_STE_PLOGI_ISSUE) &&
(ndlp->nlp_flag & NLP_NPR_2B_DISC) &&
(vport->num_disc_nodes)) {
spin_lock_irq(shost->host_lock);
ndlp->nlp_flag &= ~NLP_NPR_2B_DISC;
spin_unlock_irq(shost->host_lock);
/* Check to see if there are more
* PLOGIs to be sent
*/
lpfc_more_plogi(vport);
if (vport->num_disc_nodes == 0) {
spin_lock_irq(shost->host_lock);
vport->fc_flag &= ~FC_NDISC_ACTIVE;
spin_unlock_irq(shost->host_lock);
lpfc_can_disctmo(vport);
lpfc_end_rscn(vport);
}
}
lpfc_els_rsp_acc(vport, ELS_CMD_PLOGI, cmdiocb, ndlp, mbox);
return 1;
out:
stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC;
stat.un.b.lsRjtRsnCodeExp = LSEXP_OUT_OF_RESOURCE;
@ -574,7 +505,9 @@ lpfc_rcv_logo(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
else
lpfc_els_rsp_acc(vport, ELS_CMD_ACC, cmdiocb, ndlp, NULL);
if (!(ndlp->nlp_type & NLP_FABRIC) ||
if ((!(ndlp->nlp_type & NLP_FABRIC) &&
((ndlp->nlp_type & NLP_FCP_TARGET) ||
!(ndlp->nlp_type & NLP_FCP_INITIATOR))) ||
(ndlp->nlp_state == NLP_STE_ADISC_ISSUE)) {
/* Only try to re-login if this is NOT a Fabric Node */
mod_timer(&ndlp->nlp_delayfunc, jiffies + HZ * 1);
@ -751,6 +684,7 @@ static uint32_t
lpfc_rcv_plogi_plogi_issue(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
void *arg, uint32_t evt)
{
struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
struct lpfc_hba *phba = vport->phba;
struct lpfc_iocbq *cmdiocb = arg;
struct lpfc_dmabuf *pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
@ -776,7 +710,22 @@ lpfc_rcv_plogi_plogi_issue(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
lpfc_els_rsp_reject(vport, stat.un.lsRjtError, cmdiocb, ndlp,
NULL);
} else {
lpfc_rcv_plogi(vport, ndlp, cmdiocb);
if (lpfc_rcv_plogi(vport, ndlp, cmdiocb) &&
(ndlp->nlp_flag & NLP_NPR_2B_DISC) &&
(vport->num_disc_nodes)) {
spin_lock_irq(shost->host_lock);
ndlp->nlp_flag &= ~NLP_NPR_2B_DISC;
spin_unlock_irq(shost->host_lock);
/* Check if there are more PLOGIs to be sent */
lpfc_more_plogi(vport);
if (vport->num_disc_nodes == 0) {
spin_lock_irq(shost->host_lock);
vport->fc_flag &= ~FC_NDISC_ACTIVE;
spin_unlock_irq(shost->host_lock);
lpfc_can_disctmo(vport);
lpfc_end_rscn(vport);
}
}
} /* If our portname was less */
return ndlp->nlp_state;
@ -1040,6 +989,7 @@ static uint32_t
lpfc_rcv_plogi_adisc_issue(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
void *arg, uint32_t evt)
{
struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
struct lpfc_hba *phba = vport->phba;
struct lpfc_iocbq *cmdiocb;
@ -1048,9 +998,28 @@ lpfc_rcv_plogi_adisc_issue(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
cmdiocb = (struct lpfc_iocbq *) arg;
if (lpfc_rcv_plogi(vport, ndlp, cmdiocb))
return ndlp->nlp_state;
if (lpfc_rcv_plogi(vport, ndlp, cmdiocb)) {
if (ndlp->nlp_flag & NLP_NPR_2B_DISC) {
spin_lock_irq(shost->host_lock);
ndlp->nlp_flag &= ~NLP_NPR_2B_DISC;
spin_unlock_irq(shost->host_lock);
if (vport->num_disc_nodes) {
lpfc_more_adisc(vport);
if ((vport->num_disc_nodes == 0) &&
(vport->fc_npr_cnt))
lpfc_els_disc_plogi(vport);
if (vport->num_disc_nodes == 0) {
spin_lock_irq(shost->host_lock);
vport->fc_flag &= ~FC_NDISC_ACTIVE;
spin_unlock_irq(shost->host_lock);
lpfc_can_disctmo(vport);
lpfc_end_rscn(vport);
}
}
}
return ndlp->nlp_state;
}
ndlp->nlp_prev_state = NLP_STE_ADISC_ISSUE;
lpfc_issue_els_plogi(vport, ndlp->nlp_DID, 0);
lpfc_nlp_set_state(vport, ndlp, NLP_STE_PLOGI_ISSUE);
@ -1742,24 +1711,21 @@ lpfc_rcv_plogi_npr_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
struct lpfc_iocbq *cmdiocb = (struct lpfc_iocbq *) arg;
/* Ignore PLOGI if we have an outstanding LOGO */
if (ndlp->nlp_flag & (NLP_LOGO_SND | NLP_LOGO_ACC)) {
if (ndlp->nlp_flag & (NLP_LOGO_SND | NLP_LOGO_ACC))
return ndlp->nlp_state;
}
if (lpfc_rcv_plogi(vport, ndlp, cmdiocb)) {
lpfc_cancel_retry_delay_tmo(vport, ndlp);
spin_lock_irq(shost->host_lock);
ndlp->nlp_flag &= ~NLP_NPR_ADISC;
ndlp->nlp_flag &= ~(NLP_NPR_ADISC | NLP_NPR_2B_DISC);
spin_unlock_irq(shost->host_lock);
return ndlp->nlp_state;
} else if (!(ndlp->nlp_flag & NLP_NPR_2B_DISC)) {
/* send PLOGI immediately, move to PLOGI issue state */
if (!(ndlp->nlp_flag & NLP_DELAY_TMO)) {
ndlp->nlp_prev_state = NLP_STE_NPR_NODE;
lpfc_nlp_set_state(vport, ndlp, NLP_STE_PLOGI_ISSUE);
lpfc_issue_els_plogi(vport, ndlp->nlp_DID, 0);
}
}
/* send PLOGI immediately, move to PLOGI issue state */
if (!(ndlp->nlp_flag & NLP_DELAY_TMO)) {
ndlp->nlp_prev_state = NLP_STE_NPR_NODE;
lpfc_nlp_set_state(vport, ndlp, NLP_STE_PLOGI_ISSUE);
lpfc_issue_els_plogi(vport, ndlp->nlp_DID, 0);
}
return ndlp->nlp_state;
}
@ -1810,7 +1776,6 @@ lpfc_rcv_padisc_npr_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
struct lpfc_iocbq *cmdiocb = (struct lpfc_iocbq *) arg;
lpfc_rcv_padisc(vport, ndlp, cmdiocb);
/*
* Do not start discovery if discovery is about to start
* or discovery in progress for this node. Starting discovery
@ -1973,9 +1938,7 @@ lpfc_device_recov_npr_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
spin_lock_irq(shost->host_lock);
ndlp->nlp_flag &= ~(NLP_NODEV_REMOVE | NLP_NPR_2B_DISC);
spin_unlock_irq(shost->host_lock);
if (ndlp->nlp_flag & NLP_DELAY_TMO) {
lpfc_cancel_retry_delay_tmo(vport, ndlp);
}
lpfc_cancel_retry_delay_tmo(vport, ndlp);
return ndlp->nlp_state;
}

View File

@ -50,6 +50,7 @@ void
lpfc_adjust_queue_depth(struct lpfc_hba *phba)
{
unsigned long flags;
uint32_t evt_posted;
spin_lock_irqsave(&phba->hbalock, flags);
atomic_inc(&phba->num_rsrc_err);
@ -65,17 +66,13 @@ lpfc_adjust_queue_depth(struct lpfc_hba *phba)
spin_unlock_irqrestore(&phba->hbalock, flags);
spin_lock_irqsave(&phba->pport->work_port_lock, flags);
if ((phba->pport->work_port_events &
WORKER_RAMP_DOWN_QUEUE) == 0) {
evt_posted = phba->pport->work_port_events & WORKER_RAMP_DOWN_QUEUE;
if (!evt_posted)
phba->pport->work_port_events |= WORKER_RAMP_DOWN_QUEUE;
}
spin_unlock_irqrestore(&phba->pport->work_port_lock, flags);
spin_lock_irqsave(&phba->hbalock, flags);
if (phba->work_wait)
wake_up(phba->work_wait);
spin_unlock_irqrestore(&phba->hbalock, flags);
if (!evt_posted)
lpfc_worker_wake_up(phba);
return;
}
@ -89,6 +86,7 @@ lpfc_rampup_queue_depth(struct lpfc_vport *vport,
{
unsigned long flags;
struct lpfc_hba *phba = vport->phba;
uint32_t evt_posted;
atomic_inc(&phba->num_cmd_success);
if (vport->cfg_lun_queue_depth <= sdev->queue_depth)
@ -103,16 +101,14 @@ lpfc_rampup_queue_depth(struct lpfc_vport *vport,
spin_unlock_irqrestore(&phba->hbalock, flags);
spin_lock_irqsave(&phba->pport->work_port_lock, flags);
if ((phba->pport->work_port_events &
WORKER_RAMP_UP_QUEUE) == 0) {
evt_posted = phba->pport->work_port_events & WORKER_RAMP_UP_QUEUE;
if (!evt_posted)
phba->pport->work_port_events |= WORKER_RAMP_UP_QUEUE;
}
spin_unlock_irqrestore(&phba->pport->work_port_lock, flags);
spin_lock_irqsave(&phba->hbalock, flags);
if (phba->work_wait)
wake_up(phba->work_wait);
spin_unlock_irqrestore(&phba->hbalock, flags);
if (!evt_posted)
lpfc_worker_wake_up(phba);
return;
}
void
@ -609,9 +605,6 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn,
result = cmd->result;
sdev = cmd->device;
lpfc_scsi_unprep_dma_buf(phba, lpfc_cmd);
spin_lock_irqsave(sdev->host->host_lock, flags);
lpfc_cmd->pCmd = NULL; /* This must be done before scsi_done */
spin_unlock_irqrestore(sdev->host->host_lock, flags);
cmd->scsi_done(cmd);
if (phba->cfg_poll & ENABLE_FCP_RING_POLLING) {
@ -620,6 +613,7 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn,
* wake up the thread.
*/
spin_lock_irqsave(sdev->host->host_lock, flags);
lpfc_cmd->pCmd = NULL;
if (lpfc_cmd->waitq)
wake_up(lpfc_cmd->waitq);
spin_unlock_irqrestore(sdev->host->host_lock, flags);
@ -690,6 +684,7 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn,
* wake up the thread.
*/
spin_lock_irqsave(sdev->host->host_lock, flags);
lpfc_cmd->pCmd = NULL;
if (lpfc_cmd->waitq)
wake_up(lpfc_cmd->waitq);
spin_unlock_irqrestore(sdev->host->host_lock, flags);
@ -849,14 +844,15 @@ lpfc_scsi_tgt_reset(struct lpfc_scsi_buf *lpfc_cmd, struct lpfc_vport *vport,
struct lpfc_iocbq *iocbq;
struct lpfc_iocbq *iocbqrsp;
int ret;
int status;
if (!rdata->pnode || !NLP_CHK_NODE_ACT(rdata->pnode))
return FAILED;
lpfc_cmd->rdata = rdata;
ret = lpfc_scsi_prep_task_mgmt_cmd(vport, lpfc_cmd, lun,
status = lpfc_scsi_prep_task_mgmt_cmd(vport, lpfc_cmd, lun,
FCP_TARGET_RESET);
if (!ret)
if (!status)
return FAILED;
iocbq = &lpfc_cmd->cur_iocbq;
@ -869,12 +865,15 @@ lpfc_scsi_tgt_reset(struct lpfc_scsi_buf *lpfc_cmd, struct lpfc_vport *vport,
lpfc_printf_vlog(vport, KERN_INFO, LOG_FCP,
"0702 Issue Target Reset to TGT %d Data: x%x x%x\n",
tgt_id, rdata->pnode->nlp_rpi, rdata->pnode->nlp_flag);
ret = lpfc_sli_issue_iocb_wait(phba,
status = lpfc_sli_issue_iocb_wait(phba,
&phba->sli.ring[phba->sli.fcp_ring],
iocbq, iocbqrsp, lpfc_cmd->timeout);
if (ret != IOCB_SUCCESS) {
if (ret == IOCB_TIMEDOUT)
if (status != IOCB_SUCCESS) {
if (status == IOCB_TIMEDOUT) {
iocbq->iocb_cmpl = lpfc_tskmgmt_def_cmpl;
ret = TIMEOUT_ERROR;
} else
ret = FAILED;
lpfc_cmd->status = IOSTAT_DRIVER_REJECT;
} else {
ret = SUCCESS;
@ -1142,121 +1141,96 @@ lpfc_device_reset_handler(struct scsi_cmnd *cmnd)
struct lpfc_iocbq *iocbq, *iocbqrsp;
struct lpfc_rport_data *rdata = cmnd->device->hostdata;
struct lpfc_nodelist *pnode = rdata->pnode;
uint32_t cmd_result = 0, cmd_status = 0;
int ret = FAILED;
int iocb_status = IOCB_SUCCESS;
int cnt, loopcnt;
unsigned long later;
int ret = SUCCESS;
int status;
int cnt;
lpfc_block_error_handler(cmnd);
loopcnt = 0;
/*
* If target is not in a MAPPED state, delay the reset until
* target is rediscovered or devloss timeout expires.
*/
while (1) {
later = msecs_to_jiffies(2 * vport->cfg_devloss_tmo * 1000) + jiffies;
while (time_after(later, jiffies)) {
if (!pnode || !NLP_CHK_NODE_ACT(pnode))
goto out;
if (pnode->nlp_state != NLP_STE_MAPPED_NODE) {
schedule_timeout_uninterruptible(msecs_to_jiffies(500));
loopcnt++;
rdata = cmnd->device->hostdata;
if (!rdata ||
(loopcnt > ((vport->cfg_devloss_tmo * 2) + 1))){
lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP,
"0721 LUN Reset rport "
"failure: cnt x%x rdata x%p\n",
loopcnt, rdata);
goto out;
}
pnode = rdata->pnode;
if (!pnode || !NLP_CHK_NODE_ACT(pnode))
goto out;
}
return FAILED;
if (pnode->nlp_state == NLP_STE_MAPPED_NODE)
break;
schedule_timeout_uninterruptible(msecs_to_jiffies(500));
rdata = cmnd->device->hostdata;
if (!rdata)
break;
pnode = rdata->pnode;
}
if (!rdata || pnode->nlp_state != NLP_STE_MAPPED_NODE) {
lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP,
"0721 LUN Reset rport "
"failure: msec x%x rdata x%p\n",
jiffies_to_msecs(jiffies - later), rdata);
return FAILED;
}
lpfc_cmd = lpfc_get_scsi_buf(phba);
if (lpfc_cmd == NULL)
goto out;
return FAILED;
lpfc_cmd->timeout = 60;
lpfc_cmd->rdata = rdata;
ret = lpfc_scsi_prep_task_mgmt_cmd(vport, lpfc_cmd, cmnd->device->lun,
FCP_TARGET_RESET);
if (!ret)
goto out_free_scsi_buf;
status = lpfc_scsi_prep_task_mgmt_cmd(vport, lpfc_cmd,
cmnd->device->lun,
FCP_TARGET_RESET);
if (!status) {
lpfc_release_scsi_buf(phba, lpfc_cmd);
return FAILED;
}
iocbq = &lpfc_cmd->cur_iocbq;
/* get a buffer for this IOCB command response */
iocbqrsp = lpfc_sli_get_iocbq(phba);
if (iocbqrsp == NULL)
goto out_free_scsi_buf;
if (iocbqrsp == NULL) {
lpfc_release_scsi_buf(phba, lpfc_cmd);
return FAILED;
}
lpfc_printf_vlog(vport, KERN_INFO, LOG_FCP,
"0703 Issue target reset to TGT %d LUN %d "
"rpi x%x nlp_flag x%x\n", cmnd->device->id,
cmnd->device->lun, pnode->nlp_rpi, pnode->nlp_flag);
iocb_status = lpfc_sli_issue_iocb_wait(phba,
&phba->sli.ring[phba->sli.fcp_ring],
iocbq, iocbqrsp, lpfc_cmd->timeout);
if (iocb_status == IOCB_TIMEDOUT)
status = lpfc_sli_issue_iocb_wait(phba,
&phba->sli.ring[phba->sli.fcp_ring],
iocbq, iocbqrsp, lpfc_cmd->timeout);
if (status == IOCB_TIMEDOUT) {
iocbq->iocb_cmpl = lpfc_tskmgmt_def_cmpl;
if (iocb_status == IOCB_SUCCESS)
ret = SUCCESS;
else
ret = iocb_status;
cmd_result = iocbqrsp->iocb.un.ulpWord[4];
cmd_status = iocbqrsp->iocb.ulpStatus;
lpfc_sli_release_iocbq(phba, iocbqrsp);
/*
* All outstanding txcmplq I/Os should have been aborted by the device.
* Unfortunately, some targets do not abide by this forcing the driver
* to double check.
*/
cnt = lpfc_sli_sum_iocb(vport, cmnd->device->id, cmnd->device->lun,
LPFC_CTX_LUN);
if (cnt)
lpfc_sli_abort_iocb(vport, &phba->sli.ring[phba->sli.fcp_ring],
cmnd->device->id, cmnd->device->lun,
LPFC_CTX_LUN);
loopcnt = 0;
while(cnt) {
schedule_timeout_uninterruptible(LPFC_RESET_WAIT*HZ);
if (++loopcnt
> (2 * vport->cfg_devloss_tmo)/LPFC_RESET_WAIT)
break;
cnt = lpfc_sli_sum_iocb(vport, cmnd->device->id,
cmnd->device->lun, LPFC_CTX_LUN);
}
if (cnt) {
lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP,
"0719 device reset I/O flush failure: "
"cnt x%x\n", cnt);
ret = FAILED;
}
out_free_scsi_buf:
if (iocb_status != IOCB_TIMEDOUT) {
ret = TIMEOUT_ERROR;
} else {
if (status != IOCB_SUCCESS)
ret = FAILED;
lpfc_release_scsi_buf(phba, lpfc_cmd);
}
lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP,
"0713 SCSI layer issued device reset (%d, %d) "
"return x%x status x%x result x%x\n",
cmnd->device->id, cmnd->device->lun, ret,
cmd_status, cmd_result);
out:
iocbqrsp->iocb.ulpStatus,
iocbqrsp->iocb.un.ulpWord[4]);
lpfc_sli_release_iocbq(phba, iocbqrsp);
cnt = lpfc_sli_sum_iocb(vport, cmnd->device->id, cmnd->device->lun,
LPFC_CTX_TGT);
if (cnt)
lpfc_sli_abort_iocb(vport, &phba->sli.ring[phba->sli.fcp_ring],
cmnd->device->id, cmnd->device->lun,
LPFC_CTX_TGT);
later = msecs_to_jiffies(2 * vport->cfg_devloss_tmo * 1000) + jiffies;
while (time_after(later, jiffies) && cnt) {
schedule_timeout_uninterruptible(msecs_to_jiffies(20));
cnt = lpfc_sli_sum_iocb(vport, cmnd->device->id,
cmnd->device->lun, LPFC_CTX_TGT);
}
if (cnt) {
lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP,
"0719 device reset I/O flush failure: "
"cnt x%x\n", cnt);
ret = FAILED;
}
return ret;
}
@ -1268,19 +1242,12 @@ lpfc_bus_reset_handler(struct scsi_cmnd *cmnd)
struct lpfc_hba *phba = vport->phba;
struct lpfc_nodelist *ndlp = NULL;
int match;
int ret = FAILED, i, err_count = 0;
int cnt, loopcnt;
int ret = SUCCESS, status, i;
int cnt;
struct lpfc_scsi_buf * lpfc_cmd;
unsigned long later;
lpfc_block_error_handler(cmnd);
lpfc_cmd = lpfc_get_scsi_buf(phba);
if (lpfc_cmd == NULL)
goto out;
/* The lpfc_cmd storage is reused. Set all loop invariants. */
lpfc_cmd->timeout = 60;
/*
* Since the driver manages a single bus device, reset all
* targets known to the driver. Should any target reset
@ -1294,7 +1261,7 @@ lpfc_bus_reset_handler(struct scsi_cmnd *cmnd)
if (!NLP_CHK_NODE_ACT(ndlp))
continue;
if (ndlp->nlp_state == NLP_STE_MAPPED_NODE &&
i == ndlp->nlp_sid &&
ndlp->nlp_sid == i &&
ndlp->rport) {
match = 1;
break;
@ -1303,27 +1270,22 @@ lpfc_bus_reset_handler(struct scsi_cmnd *cmnd)
spin_unlock_irq(shost->host_lock);
if (!match)
continue;
ret = lpfc_scsi_tgt_reset(lpfc_cmd, vport, i,
cmnd->device->lun,
ndlp->rport->dd_data);
if (ret != SUCCESS) {
lpfc_cmd = lpfc_get_scsi_buf(phba);
if (lpfc_cmd) {
lpfc_cmd->timeout = 60;
status = lpfc_scsi_tgt_reset(lpfc_cmd, vport, i,
cmnd->device->lun,
ndlp->rport->dd_data);
if (status != TIMEOUT_ERROR)
lpfc_release_scsi_buf(phba, lpfc_cmd);
}
if (!lpfc_cmd || status != SUCCESS) {
lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP,
"0700 Bus Reset on target %d failed\n",
i);
err_count++;
break;
ret = FAILED;
}
}
if (ret != IOCB_TIMEDOUT)
lpfc_release_scsi_buf(phba, lpfc_cmd);
if (err_count == 0)
ret = SUCCESS;
else
ret = FAILED;
/*
* All outstanding txcmplq I/Os should have been aborted by
* the targets. Unfortunately, some targets do not abide by
@ -1333,27 +1295,19 @@ lpfc_bus_reset_handler(struct scsi_cmnd *cmnd)
if (cnt)
lpfc_sli_abort_iocb(vport, &phba->sli.ring[phba->sli.fcp_ring],
0, 0, LPFC_CTX_HOST);
loopcnt = 0;
while(cnt) {
schedule_timeout_uninterruptible(LPFC_RESET_WAIT*HZ);
if (++loopcnt
> (2 * vport->cfg_devloss_tmo)/LPFC_RESET_WAIT)
break;
later = msecs_to_jiffies(2 * vport->cfg_devloss_tmo * 1000) + jiffies;
while (time_after(later, jiffies) && cnt) {
schedule_timeout_uninterruptible(msecs_to_jiffies(20));
cnt = lpfc_sli_sum_iocb(vport, 0, 0, LPFC_CTX_HOST);
}
if (cnt) {
lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP,
"0715 Bus Reset I/O flush failure: "
"cnt x%x left x%x\n", cnt, i);
ret = FAILED;
}
lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP,
"0714 SCSI layer issued Bus Reset Data: x%x\n", ret);
out:
return ret;
}

View File

@ -324,9 +324,7 @@ lpfc_sli_next_iocb_slot (struct lpfc_hba *phba, struct lpfc_sli_ring *pring)
phba->work_ha |= HA_ERATT;
phba->work_hs = HS_FFER3;
/* hbalock should already be held */
if (phba->work_wait)
lpfc_worker_wake_up(phba);
lpfc_worker_wake_up(phba);
return NULL;
}
@ -1309,9 +1307,7 @@ lpfc_sli_rsp_pointers_error(struct lpfc_hba *phba, struct lpfc_sli_ring *pring)
phba->work_ha |= HA_ERATT;
phba->work_hs = HS_FFER3;
/* hbalock should already be held */
if (phba->work_wait)
lpfc_worker_wake_up(phba);
lpfc_worker_wake_up(phba);
return;
}
@ -2611,12 +2607,9 @@ lpfc_mbox_timeout(unsigned long ptr)
phba->pport->work_port_events |= WORKER_MBOX_TMO;
spin_unlock_irqrestore(&phba->pport->work_port_lock, iflag);
if (!tmo_posted) {
spin_lock_irqsave(&phba->hbalock, iflag);
if (phba->work_wait)
lpfc_worker_wake_up(phba);
spin_unlock_irqrestore(&phba->hbalock, iflag);
}
if (!tmo_posted)
lpfc_worker_wake_up(phba);
return;
}
void
@ -3374,8 +3367,12 @@ lpfc_sli_host_down(struct lpfc_vport *vport)
for (i = 0; i < psli->num_rings; i++) {
pring = &psli->ring[i];
prev_pring_flag = pring->flag;
if (pring->ringno == LPFC_ELS_RING) /* Only slow rings */
/* Only slow rings */
if (pring->ringno == LPFC_ELS_RING) {
pring->flag |= LPFC_DEFERRED_RING_EVENT;
/* Set the lpfc data pending flag */
set_bit(LPFC_DATA_READY, &phba->data_flags);
}
/*
* Error everything on the txq since these iocbs have not been
* given to the FW yet.
@ -3434,8 +3431,12 @@ lpfc_sli_hba_down(struct lpfc_hba *phba)
spin_lock_irqsave(&phba->hbalock, flags);
for (i = 0; i < psli->num_rings; i++) {
pring = &psli->ring[i];
if (pring->ringno == LPFC_ELS_RING) /* Only slow rings */
/* Only slow rings */
if (pring->ringno == LPFC_ELS_RING) {
pring->flag |= LPFC_DEFERRED_RING_EVENT;
/* Set the lpfc data pending flag */
set_bit(LPFC_DATA_READY, &phba->data_flags);
}
/*
* Error everything on the txq since these iocbs have not been
@ -3762,7 +3763,6 @@ lpfc_sli_validate_fcp_iocb(struct lpfc_iocbq *iocbq, struct lpfc_vport *vport,
lpfc_ctx_cmd ctx_cmd)
{
struct lpfc_scsi_buf *lpfc_cmd;
struct scsi_cmnd *cmnd;
int rc = 1;
if (!(iocbq->iocb_flag & LPFC_IO_FCP))
@ -3772,19 +3772,20 @@ lpfc_sli_validate_fcp_iocb(struct lpfc_iocbq *iocbq, struct lpfc_vport *vport,
return rc;
lpfc_cmd = container_of(iocbq, struct lpfc_scsi_buf, cur_iocbq);
cmnd = lpfc_cmd->pCmd;
if (cmnd == NULL)
if (lpfc_cmd->pCmd == NULL)
return rc;
switch (ctx_cmd) {
case LPFC_CTX_LUN:
if ((cmnd->device->id == tgt_id) &&
(cmnd->device->lun == lun_id))
if ((lpfc_cmd->rdata->pnode) &&
(lpfc_cmd->rdata->pnode->nlp_sid == tgt_id) &&
(scsilun_to_int(&lpfc_cmd->fcp_cmnd->fcp_lun) == lun_id))
rc = 0;
break;
case LPFC_CTX_TGT:
if (cmnd->device->id == tgt_id)
if ((lpfc_cmd->rdata->pnode) &&
(lpfc_cmd->rdata->pnode->nlp_sid == tgt_id))
rc = 0;
break;
case LPFC_CTX_HOST:
@ -3994,6 +3995,7 @@ lpfc_sli_issue_mbox_wait(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq,
if (pmboxq->context1)
return MBX_NOT_FINISHED;
pmboxq->mbox_flag &= ~LPFC_MBX_WAKE;
/* setup wake call as IOCB callback */
pmboxq->mbox_cmpl = lpfc_sli_wake_mbox_wait;
/* setup context field to pass wait_queue pointer to wake function */
@ -4159,7 +4161,7 @@ lpfc_intr_handler(int irq, void *dev_id)
"pwork:x%x hawork:x%x wait:x%x",
phba->work_ha, work_ha_copy,
(uint32_t)((unsigned long)
phba->work_wait));
&phba->work_waitq));
control &=
~(HC_R0INT_ENA << LPFC_ELS_RING);
@ -4172,7 +4174,7 @@ lpfc_intr_handler(int irq, void *dev_id)
"x%x hawork:x%x wait:x%x",
phba->work_ha, work_ha_copy,
(uint32_t)((unsigned long)
phba->work_wait));
&phba->work_waitq));
}
spin_unlock(&phba->hbalock);
}
@ -4297,9 +4299,8 @@ lpfc_intr_handler(int irq, void *dev_id)
spin_lock(&phba->hbalock);
phba->work_ha |= work_ha_copy;
if (phba->work_wait)
lpfc_worker_wake_up(phba);
spin_unlock(&phba->hbalock);
lpfc_worker_wake_up(phba);
}
ha_copy &= ~(phba->work_ha_mask);

View File

@ -18,7 +18,7 @@
* included with this package. *
*******************************************************************/
#define LPFC_DRIVER_VERSION "8.2.6"
#define LPFC_DRIVER_VERSION "8.2.7"
#define LPFC_DRIVER_NAME "lpfc"

View File

@ -216,6 +216,7 @@ lpfc_vport_create(struct fc_vport *fc_vport, bool disable)
int vpi;
int rc = VPORT_ERROR;
int status;
int size;
if ((phba->sli_rev < 3) ||
!(phba->sli3_options & LPFC_SLI3_NPIV_ENABLED)) {
@ -278,7 +279,20 @@ lpfc_vport_create(struct fc_vport *fc_vport, bool disable)
memcpy(vport->fc_portname.u.wwn, vport->fc_sparam.portName.u.wwn, 8);
memcpy(vport->fc_nodename.u.wwn, vport->fc_sparam.nodeName.u.wwn, 8);
size = strnlen(fc_vport->symbolic_name, LPFC_VNAME_LEN);
if (size) {
vport->vname = kzalloc(size+1, GFP_KERNEL);
if (!vport->vname) {
lpfc_printf_vlog(vport, KERN_ERR, LOG_VPORT,
"1814 Create VPORT failed. "
"vname allocation failed.\n");
rc = VPORT_ERROR;
lpfc_free_vpi(phba, vpi);
destroy_port(vport);
goto error_out;
}
memcpy(vport->vname, fc_vport->symbolic_name, size+1);
}
if (fc_vport->node_name != 0)
u64_to_wwn(fc_vport->node_name, vport->fc_nodename.u.wwn);
if (fc_vport->port_name != 0)

View File

@ -1765,7 +1765,7 @@ static int mesh_suspend(struct macio_dev *mdev, pm_message_t mesg)
default:
return 0;
}
if (mesg.event == mdev->ofdev.dev.power.power_state.event)
if (ms->phase == sleeping)
return 0;
scsi_block_requests(ms->host);
@ -1780,8 +1780,6 @@ static int mesh_suspend(struct macio_dev *mdev, pm_message_t mesg)
disable_irq(ms->meshintr);
set_mesh_power(ms, 0);
mdev->ofdev.dev.power.power_state = mesg;
return 0;
}
@ -1790,7 +1788,7 @@ static int mesh_resume(struct macio_dev *mdev)
struct mesh_state *ms = (struct mesh_state *)macio_get_drvdata(mdev);
unsigned long flags;
if (mdev->ofdev.dev.power.power_state.event == PM_EVENT_ON)
if (ms->phase != sleeping)
return 0;
set_mesh_power(ms, 1);
@ -1801,8 +1799,6 @@ static int mesh_resume(struct macio_dev *mdev)
enable_irq(ms->meshintr);
scsi_unblock_requests(ms->host);
mdev->ofdev.dev.power.power_state.event = PM_EVENT_ON;
return 0;
}

View File

@ -113,9 +113,6 @@ static struct iscsi_transport qla4xxx_iscsi_transport = {
.host_param_mask = ISCSI_HOST_HWADDRESS |
ISCSI_HOST_IPADDRESS |
ISCSI_HOST_INITIATOR_NAME,
.sessiondata_size = sizeof(struct ddb_entry),
.host_template = &qla4xxx_driver_template,
.tgt_dscvr = qla4xxx_tgt_dscvr,
.get_conn_param = qla4xxx_conn_get_param,
.get_session_param = qla4xxx_sess_get_param,
@ -275,7 +272,7 @@ int qla4xxx_add_sess(struct ddb_entry *ddb_entry)
return err;
}
ddb_entry->conn = iscsi_create_conn(ddb_entry->sess, 0);
ddb_entry->conn = iscsi_create_conn(ddb_entry->sess, 0, 0);
if (!ddb_entry->conn) {
iscsi_remove_session(ddb_entry->sess);
DEBUG2(printk(KERN_ERR "Could not add connection.\n"));
@ -292,7 +289,8 @@ struct ddb_entry *qla4xxx_alloc_sess(struct scsi_qla_host *ha)
struct ddb_entry *ddb_entry;
struct iscsi_cls_session *sess;
sess = iscsi_alloc_session(ha->host, &qla4xxx_iscsi_transport);
sess = iscsi_alloc_session(ha->host, &qla4xxx_iscsi_transport,
sizeof(struct ddb_entry));
if (!sess)
return NULL;

View File

@ -855,9 +855,18 @@ void scsi_finish_command(struct scsi_cmnd *cmd)
good_bytes = scsi_bufflen(cmd);
if (cmd->request->cmd_type != REQ_TYPE_BLOCK_PC) {
int old_good_bytes = good_bytes;
drv = scsi_cmd_to_driver(cmd);
if (drv->done)
good_bytes = drv->done(cmd);
/*
* USB may not give sense identifying bad sector and
* simply return a residue instead, so subtract off the
* residue if drv->done() error processing indicates no
* change to the completion length.
*/
if (good_bytes == old_good_bytes)
good_bytes -= scsi_get_resid(cmd);
}
scsi_io_completion(cmd, good_bytes);
}

View File

@ -94,6 +94,7 @@ static const char * scsi_debug_version_date = "20070104";
#define DEF_VIRTUAL_GB 0
#define DEF_FAKE_RW 0
#define DEF_VPD_USE_HOSTNO 1
#define DEF_SECTOR_SIZE 512
/* bit mask values for scsi_debug_opts */
#define SCSI_DEBUG_OPT_NOISE 1
@ -142,6 +143,7 @@ static int scsi_debug_no_lun_0 = DEF_NO_LUN_0;
static int scsi_debug_virtual_gb = DEF_VIRTUAL_GB;
static int scsi_debug_fake_rw = DEF_FAKE_RW;
static int scsi_debug_vpd_use_hostno = DEF_VPD_USE_HOSTNO;
static int scsi_debug_sector_size = DEF_SECTOR_SIZE;
static int scsi_debug_cmnd_count = 0;
@ -157,11 +159,6 @@ static int sdebug_heads; /* heads per disk */
static int sdebug_cylinders_per; /* cylinders per surface */
static int sdebug_sectors_per; /* sectors per cylinder */
/* default sector size is 512 bytes, 2**9 bytes */
#define POW2_SECT_SIZE 9
#define SECT_SIZE (1 << POW2_SECT_SIZE)
#define SECT_SIZE_PER(TGT) SECT_SIZE
#define SDEBUG_MAX_PARTS 4
#define SDEBUG_SENSE_LEN 32
@ -646,6 +643,14 @@ static int inquiry_evpd_b0(unsigned char * arr)
return sizeof(vpdb0_data);
}
static int inquiry_evpd_b1(unsigned char *arr)
{
memset(arr, 0, 0x3c);
arr[0] = 0;
arr[1] = 1;
return 0x3c;
}
#define SDEBUG_LONG_INQ_SZ 96
#define SDEBUG_MAX_INQ_ARR_SZ 584
@ -701,6 +706,7 @@ static int resp_inquiry(struct scsi_cmnd * scp, int target,
arr[n++] = 0x88; /* SCSI ports */
arr[n++] = 0x89; /* ATA information */
arr[n++] = 0xb0; /* Block limits (SBC) */
arr[n++] = 0xb1; /* Block characteristics (SBC) */
arr[3] = n - 4; /* number of supported VPD pages */
} else if (0x80 == cmd[2]) { /* unit serial number */
arr[1] = cmd[2]; /*sanity */
@ -740,6 +746,9 @@ static int resp_inquiry(struct scsi_cmnd * scp, int target,
} else if (0xb0 == cmd[2]) { /* Block limits (SBC) */
arr[1] = cmd[2]; /*sanity */
arr[3] = inquiry_evpd_b0(&arr[4]);
} else if (0xb1 == cmd[2]) { /* Block characteristics (SBC) */
arr[1] = cmd[2]; /*sanity */
arr[3] = inquiry_evpd_b1(&arr[4]);
} else {
/* Illegal request, invalid field in cdb */
mk_sense_buffer(devip, ILLEGAL_REQUEST,
@ -878,8 +887,8 @@ static int resp_readcap(struct scsi_cmnd * scp,
arr[2] = 0xff;
arr[3] = 0xff;
}
arr[6] = (SECT_SIZE_PER(target) >> 8) & 0xff;
arr[7] = SECT_SIZE_PER(target) & 0xff;
arr[6] = (scsi_debug_sector_size >> 8) & 0xff;
arr[7] = scsi_debug_sector_size & 0xff;
return fill_from_dev_buffer(scp, arr, SDEBUG_READCAP_ARR_SZ);
}
@ -902,10 +911,10 @@ static int resp_readcap16(struct scsi_cmnd * scp,
capac = sdebug_capacity - 1;
for (k = 0; k < 8; ++k, capac >>= 8)
arr[7 - k] = capac & 0xff;
arr[8] = (SECT_SIZE_PER(target) >> 24) & 0xff;
arr[9] = (SECT_SIZE_PER(target) >> 16) & 0xff;
arr[10] = (SECT_SIZE_PER(target) >> 8) & 0xff;
arr[11] = SECT_SIZE_PER(target) & 0xff;
arr[8] = (scsi_debug_sector_size >> 24) & 0xff;
arr[9] = (scsi_debug_sector_size >> 16) & 0xff;
arr[10] = (scsi_debug_sector_size >> 8) & 0xff;
arr[11] = scsi_debug_sector_size & 0xff;
return fill_from_dev_buffer(scp, arr,
min(alloc_len, SDEBUG_READCAP16_ARR_SZ));
}
@ -1019,20 +1028,20 @@ static int resp_disconnect_pg(unsigned char * p, int pcontrol, int target)
static int resp_format_pg(unsigned char * p, int pcontrol, int target)
{ /* Format device page for mode_sense */
unsigned char format_pg[] = {0x3, 0x16, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0x40, 0, 0, 0};
unsigned char format_pg[] = {0x3, 0x16, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0x40, 0, 0, 0};
memcpy(p, format_pg, sizeof(format_pg));
p[10] = (sdebug_sectors_per >> 8) & 0xff;
p[11] = sdebug_sectors_per & 0xff;
p[12] = (SECT_SIZE >> 8) & 0xff;
p[13] = SECT_SIZE & 0xff;
if (DEV_REMOVEABLE(target))
p[20] |= 0x20; /* should agree with INQUIRY */
if (1 == pcontrol)
memset(p + 2, 0, sizeof(format_pg) - 2);
return sizeof(format_pg);
memcpy(p, format_pg, sizeof(format_pg));
p[10] = (sdebug_sectors_per >> 8) & 0xff;
p[11] = sdebug_sectors_per & 0xff;
p[12] = (scsi_debug_sector_size >> 8) & 0xff;
p[13] = scsi_debug_sector_size & 0xff;
if (DEV_REMOVEABLE(target))
p[20] |= 0x20; /* should agree with INQUIRY */
if (1 == pcontrol)
memset(p + 2, 0, sizeof(format_pg) - 2);
return sizeof(format_pg);
}
static int resp_caching_pg(unsigned char * p, int pcontrol, int target)
@ -1206,8 +1215,8 @@ static int resp_mode_sense(struct scsi_cmnd * scp, int target,
ap[2] = (sdebug_capacity >> 8) & 0xff;
ap[3] = sdebug_capacity & 0xff;
}
ap[6] = (SECT_SIZE_PER(target) >> 8) & 0xff;
ap[7] = SECT_SIZE_PER(target) & 0xff;
ap[6] = (scsi_debug_sector_size >> 8) & 0xff;
ap[7] = scsi_debug_sector_size & 0xff;
offset += bd_len;
ap = arr + offset;
} else if (16 == bd_len) {
@ -1215,10 +1224,10 @@ static int resp_mode_sense(struct scsi_cmnd * scp, int target,
for (k = 0; k < 8; ++k, capac >>= 8)
ap[7 - k] = capac & 0xff;
ap[12] = (SECT_SIZE_PER(target) >> 24) & 0xff;
ap[13] = (SECT_SIZE_PER(target) >> 16) & 0xff;
ap[14] = (SECT_SIZE_PER(target) >> 8) & 0xff;
ap[15] = SECT_SIZE_PER(target) & 0xff;
ap[12] = (scsi_debug_sector_size >> 24) & 0xff;
ap[13] = (scsi_debug_sector_size >> 16) & 0xff;
ap[14] = (scsi_debug_sector_size >> 8) & 0xff;
ap[15] = scsi_debug_sector_size & 0xff;
offset += bd_len;
ap = arr + offset;
}
@ -1519,10 +1528,10 @@ static int do_device_access(struct scsi_cmnd *scmd,
if (block + num > sdebug_store_sectors)
rest = block + num - sdebug_store_sectors;
ret = func(scmd, fake_storep + (block * SECT_SIZE),
(num - rest) * SECT_SIZE);
ret = func(scmd, fake_storep + (block * scsi_debug_sector_size),
(num - rest) * scsi_debug_sector_size);
if (!ret && rest)
ret = func(scmd, fake_storep, rest * SECT_SIZE);
ret = func(scmd, fake_storep, rest * scsi_debug_sector_size);
return ret;
}
@ -1575,10 +1584,10 @@ static int resp_write(struct scsi_cmnd *SCpnt, unsigned long long lba,
write_unlock_irqrestore(&atomic_rw, iflags);
if (-1 == ret)
return (DID_ERROR << 16);
else if ((ret < (num * SECT_SIZE)) &&
else if ((ret < (num * scsi_debug_sector_size)) &&
(SCSI_DEBUG_OPT_NOISE & scsi_debug_opts))
printk(KERN_INFO "scsi_debug: write: cdb indicated=%u, "
" IO sent=%d bytes\n", num * SECT_SIZE, ret);
" IO sent=%d bytes\n", num * scsi_debug_sector_size, ret);
return 0;
}
@ -2085,6 +2094,7 @@ module_param_named(scsi_level, scsi_debug_scsi_level, int, S_IRUGO);
module_param_named(virtual_gb, scsi_debug_virtual_gb, int, S_IRUGO | S_IWUSR);
module_param_named(vpd_use_hostno, scsi_debug_vpd_use_hostno, int,
S_IRUGO | S_IWUSR);
module_param_named(sector_size, scsi_debug_sector_size, int, S_IRUGO);
MODULE_AUTHOR("Eric Youngdale + Douglas Gilbert");
MODULE_DESCRIPTION("SCSI debug adapter driver");
@ -2106,6 +2116,7 @@ MODULE_PARM_DESC(ptype, "SCSI peripheral type(def=0[disk])");
MODULE_PARM_DESC(scsi_level, "SCSI level to simulate(def=5[SPC-3])");
MODULE_PARM_DESC(virtual_gb, "virtual gigabyte size (def=0 -> use dev_size_mb)");
MODULE_PARM_DESC(vpd_use_hostno, "0 -> dev ids ignore hostno (def=1 -> unique dev ids)");
MODULE_PARM_DESC(sector_size, "hardware sector size in bytes (def=512)");
static char sdebug_info[256];
@ -2158,8 +2169,9 @@ static int scsi_debug_proc_info(struct Scsi_Host *host, char *buffer, char **sta
scsi_debug_dev_size_mb, scsi_debug_opts, scsi_debug_every_nth,
scsi_debug_cmnd_count, scsi_debug_delay,
scsi_debug_max_luns, scsi_debug_scsi_level,
SECT_SIZE, sdebug_cylinders_per, sdebug_heads, sdebug_sectors_per,
num_aborts, num_dev_resets, num_bus_resets, num_host_resets);
scsi_debug_sector_size, sdebug_cylinders_per, sdebug_heads,
sdebug_sectors_per, num_aborts, num_dev_resets, num_bus_resets,
num_host_resets);
if (pos < offset) {
len = 0;
begin = pos;
@ -2434,6 +2446,12 @@ static ssize_t sdebug_vpd_use_hostno_store(struct device_driver * ddp,
DRIVER_ATTR(vpd_use_hostno, S_IRUGO | S_IWUSR, sdebug_vpd_use_hostno_show,
sdebug_vpd_use_hostno_store);
static ssize_t sdebug_sector_size_show(struct device_driver * ddp, char * buf)
{
return scnprintf(buf, PAGE_SIZE, "%u\n", scsi_debug_sector_size);
}
DRIVER_ATTR(sector_size, S_IRUGO, sdebug_sector_size_show, NULL);
/* Note: The following function creates attribute files in the
/sys/bus/pseudo/drivers/scsi_debug directory. The advantage of these
files (over those found in the /sys/module/scsi_debug/parameters
@ -2459,11 +2477,13 @@ static int do_create_driverfs_files(void)
ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_scsi_level);
ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_virtual_gb);
ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_vpd_use_hostno);
ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_sector_size);
return ret;
}
static void do_remove_driverfs_files(void)
{
driver_remove_file(&sdebug_driverfs_driver, &driver_attr_sector_size);
driver_remove_file(&sdebug_driverfs_driver, &driver_attr_vpd_use_hostno);
driver_remove_file(&sdebug_driverfs_driver, &driver_attr_virtual_gb);
driver_remove_file(&sdebug_driverfs_driver, &driver_attr_scsi_level);
@ -2499,10 +2519,22 @@ static int __init scsi_debug_init(void)
int k;
int ret;
switch (scsi_debug_sector_size) {
case 512:
case 1024:
case 2048:
case 4096:
break;
default:
printk(KERN_ERR "scsi_debug_init: invalid sector_size %u\n",
scsi_debug_sector_size);
return -EINVAL;
}
if (scsi_debug_dev_size_mb < 1)
scsi_debug_dev_size_mb = 1; /* force minimum 1 MB ramdisk */
sz = (unsigned long)scsi_debug_dev_size_mb * 1048576;
sdebug_store_sectors = sz / SECT_SIZE;
sdebug_store_sectors = sz / scsi_debug_sector_size;
sdebug_capacity = get_sdebug_capacity();
/* play around with geometry, don't waste too much on track 0 */

View File

@ -298,6 +298,7 @@ static inline void scsi_eh_prt_fail_stats(struct Scsi_Host *shost,
*/
static int scsi_check_sense(struct scsi_cmnd *scmd)
{
struct scsi_device *sdev = scmd->device;
struct scsi_sense_hdr sshdr;
if (! scsi_command_normalize_sense(scmd, &sshdr))
@ -306,6 +307,16 @@ static int scsi_check_sense(struct scsi_cmnd *scmd)
if (scsi_sense_is_deferred(&sshdr))
return NEEDS_RETRY;
if (sdev->scsi_dh_data && sdev->scsi_dh_data->scsi_dh &&
sdev->scsi_dh_data->scsi_dh->check_sense) {
int rc;
rc = sdev->scsi_dh_data->scsi_dh->check_sense(sdev, &sshdr);
if (rc != SCSI_RETURN_NOT_HANDLED)
return rc;
/* handler does not care. Drop down to default handling */
}
/*
* Previous logic looked for FILEMARK, EOM or ILI which are
* mainly associated with tapes and returned SUCCESS.

View File

@ -65,7 +65,7 @@ static struct scsi_host_sg_pool scsi_sg_pools[] = {
};
#undef SP
static struct kmem_cache *scsi_bidi_sdb_cache;
static struct kmem_cache *scsi_sdb_cache;
static void scsi_run_queue(struct request_queue *q);
@ -784,7 +784,7 @@ void scsi_release_buffers(struct scsi_cmnd *cmd)
struct scsi_data_buffer *bidi_sdb =
cmd->request->next_rq->special;
scsi_free_sgtable(bidi_sdb);
kmem_cache_free(scsi_bidi_sdb_cache, bidi_sdb);
kmem_cache_free(scsi_sdb_cache, bidi_sdb);
cmd->request->next_rq->special = NULL;
}
}
@ -1059,7 +1059,7 @@ int scsi_init_io(struct scsi_cmnd *cmd, gfp_t gfp_mask)
if (blk_bidi_rq(cmd->request)) {
struct scsi_data_buffer *bidi_sdb = kmem_cache_zalloc(
scsi_bidi_sdb_cache, GFP_ATOMIC);
scsi_sdb_cache, GFP_ATOMIC);
if (!bidi_sdb) {
error = BLKPREP_DEFER;
goto err_exit;
@ -1169,6 +1169,14 @@ int scsi_setup_fs_cmnd(struct scsi_device *sdev, struct request *req)
if (ret != BLKPREP_OK)
return ret;
if (unlikely(sdev->scsi_dh_data && sdev->scsi_dh_data->scsi_dh
&& sdev->scsi_dh_data->scsi_dh->prep_fn)) {
ret = sdev->scsi_dh_data->scsi_dh->prep_fn(sdev, req);
if (ret != BLKPREP_OK)
return ret;
}
/*
* Filesystem requests must transfer data.
*/
@ -1329,7 +1337,6 @@ static inline int scsi_host_queue_ready(struct request_queue *q,
printk("scsi%d unblocking host at zero depth\n",
shost->host_no));
} else {
blk_plug_device(q);
return 0;
}
}
@ -1693,11 +1700,11 @@ int __init scsi_init_queue(void)
return -ENOMEM;
}
scsi_bidi_sdb_cache = kmem_cache_create("scsi_bidi_sdb",
sizeof(struct scsi_data_buffer),
0, 0, NULL);
if (!scsi_bidi_sdb_cache) {
printk(KERN_ERR "SCSI: can't init scsi bidi sdb cache\n");
scsi_sdb_cache = kmem_cache_create("scsi_data_buffer",
sizeof(struct scsi_data_buffer),
0, 0, NULL);
if (!scsi_sdb_cache) {
printk(KERN_ERR "SCSI: can't init scsi sdb cache\n");
goto cleanup_io_context;
}
@ -1710,7 +1717,7 @@ int __init scsi_init_queue(void)
if (!sgp->slab) {
printk(KERN_ERR "SCSI: can't init sg slab %s\n",
sgp->name);
goto cleanup_bidi_sdb;
goto cleanup_sdb;
}
sgp->pool = mempool_create_slab_pool(SG_MEMPOOL_SIZE,
@ -1718,13 +1725,13 @@ int __init scsi_init_queue(void)
if (!sgp->pool) {
printk(KERN_ERR "SCSI: can't init sg mempool %s\n",
sgp->name);
goto cleanup_bidi_sdb;
goto cleanup_sdb;
}
}
return 0;
cleanup_bidi_sdb:
cleanup_sdb:
for (i = 0; i < SG_MEMPOOL_NR; i++) {
struct scsi_host_sg_pool *sgp = scsi_sg_pools + i;
if (sgp->pool)
@ -1732,7 +1739,7 @@ int __init scsi_init_queue(void)
if (sgp->slab)
kmem_cache_destroy(sgp->slab);
}
kmem_cache_destroy(scsi_bidi_sdb_cache);
kmem_cache_destroy(scsi_sdb_cache);
cleanup_io_context:
kmem_cache_destroy(scsi_io_context_cache);
@ -1744,7 +1751,7 @@ void scsi_exit_queue(void)
int i;
kmem_cache_destroy(scsi_io_context_cache);
kmem_cache_destroy(scsi_bidi_sdb_cache);
kmem_cache_destroy(scsi_sdb_cache);
for (i = 0; i < SG_MEMPOOL_NR; i++) {
struct scsi_host_sg_pool *sgp = scsi_sg_pools + i;

View File

@ -346,7 +346,7 @@ static void scsi_target_dev_release(struct device *dev)
put_device(parent);
}
struct device_type scsi_target_type = {
static struct device_type scsi_target_type = {
.name = "scsi_target",
.release = scsi_target_dev_release,
};

View File

@ -439,6 +439,7 @@ struct bus_type scsi_bus_type = {
.resume = scsi_bus_resume,
.remove = scsi_bus_remove,
};
EXPORT_SYMBOL_GPL(scsi_bus_type);
int scsi_sysfs_register(void)
{

View File

@ -30,10 +30,11 @@
#include <scsi/scsi_transport_iscsi.h>
#include <scsi/iscsi_if.h>
#define ISCSI_SESSION_ATTRS 19
#define ISCSI_SESSION_ATTRS 21
#define ISCSI_CONN_ATTRS 13
#define ISCSI_HOST_ATTRS 4
#define ISCSI_TRANSPORT_VERSION "2.0-869"
#define ISCSI_TRANSPORT_VERSION "2.0-870"
struct iscsi_internal {
int daemon_pid;
@ -101,16 +102,10 @@ show_transport_##name(struct device *dev, \
static DEVICE_ATTR(name, S_IRUGO, show_transport_##name, NULL);
show_transport_attr(caps, "0x%x");
show_transport_attr(max_lun, "%d");
show_transport_attr(max_conn, "%d");
show_transport_attr(max_cmd_len, "%d");
static struct attribute *iscsi_transport_attrs[] = {
&dev_attr_handle.attr,
&dev_attr_caps.attr,
&dev_attr_max_lun.attr,
&dev_attr_max_conn.attr,
&dev_attr_max_cmd_len.attr,
NULL,
};
@ -118,18 +113,139 @@ static struct attribute_group iscsi_transport_group = {
.attrs = iscsi_transport_attrs,
};
/*
* iSCSI endpoint attrs
*/
#define iscsi_dev_to_endpoint(_dev) \
container_of(_dev, struct iscsi_endpoint, dev)
#define ISCSI_ATTR(_prefix,_name,_mode,_show,_store) \
struct device_attribute dev_attr_##_prefix##_##_name = \
__ATTR(_name,_mode,_show,_store)
static void iscsi_endpoint_release(struct device *dev)
{
struct iscsi_endpoint *ep = iscsi_dev_to_endpoint(dev);
kfree(ep);
}
static struct class iscsi_endpoint_class = {
.name = "iscsi_endpoint",
.dev_release = iscsi_endpoint_release,
};
static ssize_t
show_ep_handle(struct device *dev, struct device_attribute *attr, char *buf)
{
struct iscsi_endpoint *ep = iscsi_dev_to_endpoint(dev);
return sprintf(buf, "%u\n", ep->id);
}
static ISCSI_ATTR(ep, handle, S_IRUGO, show_ep_handle, NULL);
static struct attribute *iscsi_endpoint_attrs[] = {
&dev_attr_ep_handle.attr,
NULL,
};
static struct attribute_group iscsi_endpoint_group = {
.attrs = iscsi_endpoint_attrs,
};
#define ISCSI_MAX_EPID -1
static int iscsi_match_epid(struct device *dev, void *data)
{
struct iscsi_endpoint *ep = iscsi_dev_to_endpoint(dev);
unsigned int *epid = (unsigned int *) data;
return *epid == ep->id;
}
struct iscsi_endpoint *
iscsi_create_endpoint(int dd_size)
{
struct device *dev;
struct iscsi_endpoint *ep;
unsigned int id;
int err;
for (id = 1; id < ISCSI_MAX_EPID; id++) {
dev = class_find_device(&iscsi_endpoint_class, &id,
iscsi_match_epid);
if (!dev)
break;
}
if (id == ISCSI_MAX_EPID) {
printk(KERN_ERR "Too many connections. Max supported %u\n",
ISCSI_MAX_EPID - 1);
return NULL;
}
ep = kzalloc(sizeof(*ep) + dd_size, GFP_KERNEL);
if (!ep)
return NULL;
ep->id = id;
ep->dev.class = &iscsi_endpoint_class;
snprintf(ep->dev.bus_id, BUS_ID_SIZE, "ep-%u", id);
err = device_register(&ep->dev);
if (err)
goto free_ep;
err = sysfs_create_group(&ep->dev.kobj, &iscsi_endpoint_group);
if (err)
goto unregister_dev;
if (dd_size)
ep->dd_data = &ep[1];
return ep;
unregister_dev:
device_unregister(&ep->dev);
return NULL;
free_ep:
kfree(ep);
return NULL;
}
EXPORT_SYMBOL_GPL(iscsi_create_endpoint);
void iscsi_destroy_endpoint(struct iscsi_endpoint *ep)
{
sysfs_remove_group(&ep->dev.kobj, &iscsi_endpoint_group);
device_unregister(&ep->dev);
}
EXPORT_SYMBOL_GPL(iscsi_destroy_endpoint);
struct iscsi_endpoint *iscsi_lookup_endpoint(u64 handle)
{
struct iscsi_endpoint *ep;
struct device *dev;
dev = class_find_device(&iscsi_endpoint_class, &handle,
iscsi_match_epid);
if (!dev)
return NULL;
ep = iscsi_dev_to_endpoint(dev);
/*
* we can drop this now because the interface will prevent
* removals and lookups from racing.
*/
put_device(dev);
return ep;
}
EXPORT_SYMBOL_GPL(iscsi_lookup_endpoint);
static int iscsi_setup_host(struct transport_container *tc, struct device *dev,
struct device *cdev)
{
struct Scsi_Host *shost = dev_to_shost(dev);
struct iscsi_host *ihost = shost->shost_data;
struct iscsi_cls_host *ihost = shost->shost_data;
memset(ihost, 0, sizeof(*ihost));
INIT_LIST_HEAD(&ihost->sessions);
mutex_init(&ihost->mutex);
atomic_set(&ihost->nr_scans, 0);
mutex_init(&ihost->mutex);
snprintf(ihost->scan_workq_name, KOBJ_NAME_LEN, "iscsi_scan_%d",
shost->host_no);
@ -144,7 +260,7 @@ static int iscsi_remove_host(struct transport_container *tc, struct device *dev,
struct device *cdev)
{
struct Scsi_Host *shost = dev_to_shost(dev);
struct iscsi_host *ihost = shost->shost_data;
struct iscsi_cls_host *ihost = shost->shost_data;
destroy_workqueue(ihost->scan_workq);
return 0;
@ -287,6 +403,24 @@ static int iscsi_is_session_dev(const struct device *dev)
return dev->release == iscsi_session_release;
}
static int iscsi_iter_session_fn(struct device *dev, void *data)
{
void (* fn) (struct iscsi_cls_session *) = data;
if (!iscsi_is_session_dev(dev))
return 0;
fn(iscsi_dev_to_session(dev));
return 0;
}
void iscsi_host_for_each_session(struct Scsi_Host *shost,
void (*fn)(struct iscsi_cls_session *))
{
device_for_each_child(&shost->shost_gendev, fn,
iscsi_iter_session_fn);
}
EXPORT_SYMBOL_GPL(iscsi_host_for_each_session);
/**
* iscsi_scan_finished - helper to report when running scans are done
* @shost: scsi host
@ -297,7 +431,7 @@ static int iscsi_is_session_dev(const struct device *dev)
*/
int iscsi_scan_finished(struct Scsi_Host *shost, unsigned long time)
{
struct iscsi_host *ihost = shost->shost_data;
struct iscsi_cls_host *ihost = shost->shost_data;
/*
* qla4xxx will have kicked off some session unblocks before calling
* scsi_scan_host, so just wait for them to complete.
@ -306,22 +440,61 @@ int iscsi_scan_finished(struct Scsi_Host *shost, unsigned long time)
}
EXPORT_SYMBOL_GPL(iscsi_scan_finished);
struct iscsi_scan_data {
unsigned int channel;
unsigned int id;
unsigned int lun;
};
static int iscsi_user_scan_session(struct device *dev, void *data)
{
struct iscsi_scan_data *scan_data = data;
struct iscsi_cls_session *session;
struct Scsi_Host *shost;
struct iscsi_cls_host *ihost;
unsigned long flags;
unsigned int id;
if (!iscsi_is_session_dev(dev))
return 0;
session = iscsi_dev_to_session(dev);
shost = iscsi_session_to_shost(session);
ihost = shost->shost_data;
mutex_lock(&ihost->mutex);
spin_lock_irqsave(&session->lock, flags);
if (session->state != ISCSI_SESSION_LOGGED_IN) {
spin_unlock_irqrestore(&session->lock, flags);
mutex_unlock(&ihost->mutex);
return 0;
}
id = session->target_id;
spin_unlock_irqrestore(&session->lock, flags);
if (id != ISCSI_MAX_TARGET) {
if ((scan_data->channel == SCAN_WILD_CARD ||
scan_data->channel == 0) &&
(scan_data->id == SCAN_WILD_CARD ||
scan_data->id == id))
scsi_scan_target(&session->dev, 0, id,
scan_data->lun, 1);
}
mutex_unlock(&ihost->mutex);
return 0;
}
static int iscsi_user_scan(struct Scsi_Host *shost, uint channel,
uint id, uint lun)
{
struct iscsi_host *ihost = shost->shost_data;
struct iscsi_cls_session *session;
struct iscsi_scan_data scan_data;
mutex_lock(&ihost->mutex);
list_for_each_entry(session, &ihost->sessions, host_list) {
if ((channel == SCAN_WILD_CARD || channel == 0) &&
(id == SCAN_WILD_CARD || id == session->target_id))
scsi_scan_target(&session->dev, 0,
session->target_id, lun, 1);
}
mutex_unlock(&ihost->mutex);
scan_data.channel = channel;
scan_data.id = id;
scan_data.lun = lun;
return 0;
return device_for_each_child(&shost->shost_gendev, &scan_data,
iscsi_user_scan_session);
}
static void iscsi_scan_session(struct work_struct *work)
@ -329,19 +502,14 @@ static void iscsi_scan_session(struct work_struct *work)
struct iscsi_cls_session *session =
container_of(work, struct iscsi_cls_session, scan_work);
struct Scsi_Host *shost = iscsi_session_to_shost(session);
struct iscsi_host *ihost = shost->shost_data;
unsigned long flags;
struct iscsi_cls_host *ihost = shost->shost_data;
struct iscsi_scan_data scan_data;
spin_lock_irqsave(&session->lock, flags);
if (session->state != ISCSI_SESSION_LOGGED_IN) {
spin_unlock_irqrestore(&session->lock, flags);
goto done;
}
spin_unlock_irqrestore(&session->lock, flags);
scan_data.channel = 0;
scan_data.id = SCAN_WILD_CARD;
scan_data.lun = SCAN_WILD_CARD;
scsi_scan_target(&session->dev, 0, session->target_id,
SCAN_WILD_CARD, 1);
done:
iscsi_user_scan_session(&session->dev, &scan_data);
atomic_dec(&ihost->nr_scans);
}
@ -381,7 +549,7 @@ static void __iscsi_unblock_session(struct work_struct *work)
container_of(work, struct iscsi_cls_session,
unblock_work);
struct Scsi_Host *shost = iscsi_session_to_shost(session);
struct iscsi_host *ihost = shost->shost_data;
struct iscsi_cls_host *ihost = shost->shost_data;
unsigned long flags;
/*
@ -449,15 +617,19 @@ static void __iscsi_unbind_session(struct work_struct *work)
container_of(work, struct iscsi_cls_session,
unbind_work);
struct Scsi_Host *shost = iscsi_session_to_shost(session);
struct iscsi_host *ihost = shost->shost_data;
struct iscsi_cls_host *ihost = shost->shost_data;
unsigned long flags;
/* Prevent new scans and make sure scanning is not in progress */
mutex_lock(&ihost->mutex);
if (list_empty(&session->host_list)) {
spin_lock_irqsave(&session->lock, flags);
if (session->target_id == ISCSI_MAX_TARGET) {
spin_unlock_irqrestore(&session->lock, flags);
mutex_unlock(&ihost->mutex);
return;
}
list_del_init(&session->host_list);
session->target_id = ISCSI_MAX_TARGET;
spin_unlock_irqrestore(&session->lock, flags);
mutex_unlock(&ihost->mutex);
scsi_remove_target(&session->dev);
@ -467,18 +639,18 @@ static void __iscsi_unbind_session(struct work_struct *work)
static int iscsi_unbind_session(struct iscsi_cls_session *session)
{
struct Scsi_Host *shost = iscsi_session_to_shost(session);
struct iscsi_host *ihost = shost->shost_data;
struct iscsi_cls_host *ihost = shost->shost_data;
return queue_work(ihost->scan_workq, &session->unbind_work);
}
struct iscsi_cls_session *
iscsi_alloc_session(struct Scsi_Host *shost,
struct iscsi_transport *transport)
iscsi_alloc_session(struct Scsi_Host *shost, struct iscsi_transport *transport,
int dd_size)
{
struct iscsi_cls_session *session;
session = kzalloc(sizeof(*session) + transport->sessiondata_size,
session = kzalloc(sizeof(*session) + dd_size,
GFP_KERNEL);
if (!session)
return NULL;
@ -487,7 +659,6 @@ iscsi_alloc_session(struct Scsi_Host *shost,
session->recovery_tmo = 120;
session->state = ISCSI_SESSION_FREE;
INIT_DELAYED_WORK(&session->recovery_work, session_recovery_timedout);
INIT_LIST_HEAD(&session->host_list);
INIT_LIST_HEAD(&session->sess_list);
INIT_WORK(&session->unblock_work, __iscsi_unblock_session);
INIT_WORK(&session->block_work, __iscsi_block_session);
@ -500,22 +671,57 @@ iscsi_alloc_session(struct Scsi_Host *shost,
session->dev.parent = &shost->shost_gendev;
session->dev.release = iscsi_session_release;
device_initialize(&session->dev);
if (transport->sessiondata_size)
if (dd_size)
session->dd_data = &session[1];
return session;
}
EXPORT_SYMBOL_GPL(iscsi_alloc_session);
static int iscsi_get_next_target_id(struct device *dev, void *data)
{
struct iscsi_cls_session *session;
unsigned long flags;
int err = 0;
if (!iscsi_is_session_dev(dev))
return 0;
session = iscsi_dev_to_session(dev);
spin_lock_irqsave(&session->lock, flags);
if (*((unsigned int *) data) == session->target_id)
err = -EEXIST;
spin_unlock_irqrestore(&session->lock, flags);
return err;
}
int iscsi_add_session(struct iscsi_cls_session *session, unsigned int target_id)
{
struct Scsi_Host *shost = iscsi_session_to_shost(session);
struct iscsi_host *ihost;
struct iscsi_cls_host *ihost;
unsigned long flags;
unsigned int id = target_id;
int err;
ihost = shost->shost_data;
session->sid = atomic_add_return(1, &iscsi_session_nr);
session->target_id = target_id;
if (id == ISCSI_MAX_TARGET) {
for (id = 0; id < ISCSI_MAX_TARGET; id++) {
err = device_for_each_child(&shost->shost_gendev, &id,
iscsi_get_next_target_id);
if (!err)
break;
}
if (id == ISCSI_MAX_TARGET) {
iscsi_cls_session_printk(KERN_ERR, session,
"Too many iscsi targets. Max "
"number of targets is %d.\n",
ISCSI_MAX_TARGET - 1);
goto release_host;
}
}
session->target_id = id;
snprintf(session->dev.bus_id, BUS_ID_SIZE, "session%u",
session->sid);
@ -531,10 +737,6 @@ int iscsi_add_session(struct iscsi_cls_session *session, unsigned int target_id)
list_add(&session->sess_list, &sesslist);
spin_unlock_irqrestore(&sesslock, flags);
mutex_lock(&ihost->mutex);
list_add(&session->host_list, &ihost->sessions);
mutex_unlock(&ihost->mutex);
iscsi_session_event(session, ISCSI_KEVENT_CREATE_SESSION);
return 0;
@ -548,18 +750,18 @@ EXPORT_SYMBOL_GPL(iscsi_add_session);
* iscsi_create_session - create iscsi class session
* @shost: scsi host
* @transport: iscsi transport
* @dd_size: private driver data size
* @target_id: which target
*
* This can be called from a LLD or iscsi_transport.
*/
struct iscsi_cls_session *
iscsi_create_session(struct Scsi_Host *shost,
struct iscsi_transport *transport,
unsigned int target_id)
iscsi_create_session(struct Scsi_Host *shost, struct iscsi_transport *transport,
int dd_size, unsigned int target_id)
{
struct iscsi_cls_session *session;
session = iscsi_alloc_session(shost, transport);
session = iscsi_alloc_session(shost, transport, dd_size);
if (!session)
return NULL;
@ -595,7 +797,7 @@ static int iscsi_iter_destroy_conn_fn(struct device *dev, void *data)
void iscsi_remove_session(struct iscsi_cls_session *session)
{
struct Scsi_Host *shost = iscsi_session_to_shost(session);
struct iscsi_host *ihost = shost->shost_data;
struct iscsi_cls_host *ihost = shost->shost_data;
unsigned long flags;
int err;
@ -661,6 +863,7 @@ EXPORT_SYMBOL_GPL(iscsi_destroy_session);
/**
* iscsi_create_conn - create iscsi class connection
* @session: iscsi cls session
* @dd_size: private driver data size
* @cid: connection id
*
* This can be called from a LLD or iscsi_transport. The connection
@ -673,18 +876,17 @@ EXPORT_SYMBOL_GPL(iscsi_destroy_session);
* non-zero.
*/
struct iscsi_cls_conn *
iscsi_create_conn(struct iscsi_cls_session *session, uint32_t cid)
iscsi_create_conn(struct iscsi_cls_session *session, int dd_size, uint32_t cid)
{
struct iscsi_transport *transport = session->transport;
struct iscsi_cls_conn *conn;
unsigned long flags;
int err;
conn = kzalloc(sizeof(*conn) + transport->conndata_size, GFP_KERNEL);
conn = kzalloc(sizeof(*conn) + dd_size, GFP_KERNEL);
if (!conn)
return NULL;
if (transport->conndata_size)
if (dd_size)
conn->dd_data = &conn[1];
INIT_LIST_HEAD(&conn->conn_list);
@ -1017,21 +1219,20 @@ int iscsi_session_event(struct iscsi_cls_session *session,
EXPORT_SYMBOL_GPL(iscsi_session_event);
static int
iscsi_if_create_session(struct iscsi_internal *priv, struct iscsi_uevent *ev)
iscsi_if_create_session(struct iscsi_internal *priv, struct iscsi_endpoint *ep,
struct iscsi_uevent *ev, uint32_t initial_cmdsn,
uint16_t cmds_max, uint16_t queue_depth)
{
struct iscsi_transport *transport = priv->iscsi_transport;
struct iscsi_cls_session *session;
uint32_t hostno;
uint32_t host_no;
session = transport->create_session(transport, &priv->t,
ev->u.c_session.cmds_max,
ev->u.c_session.queue_depth,
ev->u.c_session.initial_cmdsn,
&hostno);
session = transport->create_session(ep, cmds_max, queue_depth,
initial_cmdsn, &host_no);
if (!session)
return -ENOMEM;
ev->r.c_session_ret.host_no = hostno;
ev->r.c_session_ret.host_no = host_no;
ev->r.c_session_ret.sid = session->sid;
return 0;
}
@ -1106,6 +1307,7 @@ static int
iscsi_if_transport_ep(struct iscsi_transport *transport,
struct iscsi_uevent *ev, int msg_type)
{
struct iscsi_endpoint *ep;
struct sockaddr *dst_addr;
int rc = 0;
@ -1115,22 +1317,33 @@ iscsi_if_transport_ep(struct iscsi_transport *transport,
return -EINVAL;
dst_addr = (struct sockaddr *)((char*)ev + sizeof(*ev));
rc = transport->ep_connect(dst_addr,
ev->u.ep_connect.non_blocking,
&ev->r.ep_connect_ret.handle);
ep = transport->ep_connect(dst_addr,
ev->u.ep_connect.non_blocking);
if (IS_ERR(ep))
return PTR_ERR(ep);
ev->r.ep_connect_ret.handle = ep->id;
break;
case ISCSI_UEVENT_TRANSPORT_EP_POLL:
if (!transport->ep_poll)
return -EINVAL;
ev->r.retcode = transport->ep_poll(ev->u.ep_poll.ep_handle,
ep = iscsi_lookup_endpoint(ev->u.ep_poll.ep_handle);
if (!ep)
return -EINVAL;
ev->r.retcode = transport->ep_poll(ep,
ev->u.ep_poll.timeout_ms);
break;
case ISCSI_UEVENT_TRANSPORT_EP_DISCONNECT:
if (!transport->ep_disconnect)
return -EINVAL;
transport->ep_disconnect(ev->u.ep_disconnect.ep_handle);
ep = iscsi_lookup_endpoint(ev->u.ep_disconnect.ep_handle);
if (!ep)
return -EINVAL;
transport->ep_disconnect(ep);
break;
}
return rc;
@ -1195,6 +1408,7 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
struct iscsi_internal *priv;
struct iscsi_cls_session *session;
struct iscsi_cls_conn *conn;
struct iscsi_endpoint *ep = NULL;
priv = iscsi_if_transport_lookup(iscsi_ptr(ev->transport_handle));
if (!priv)
@ -1208,7 +1422,22 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
switch (nlh->nlmsg_type) {
case ISCSI_UEVENT_CREATE_SESSION:
err = iscsi_if_create_session(priv, ev);
err = iscsi_if_create_session(priv, ep, ev,
ev->u.c_session.initial_cmdsn,
ev->u.c_session.cmds_max,
ev->u.c_session.queue_depth);
break;
case ISCSI_UEVENT_CREATE_BOUND_SESSION:
ep = iscsi_lookup_endpoint(ev->u.c_bound_session.ep_handle);
if (!ep) {
err = -EINVAL;
break;
}
err = iscsi_if_create_session(priv, ep, ev,
ev->u.c_bound_session.initial_cmdsn,
ev->u.c_bound_session.cmds_max,
ev->u.c_bound_session.queue_depth);
break;
case ISCSI_UEVENT_DESTROY_SESSION:
session = iscsi_session_lookup(ev->u.d_session.sid);
@ -1414,6 +1643,8 @@ iscsi_session_attr(password_in, ISCSI_PARAM_PASSWORD_IN, 1);
iscsi_session_attr(fast_abort, ISCSI_PARAM_FAST_ABORT, 0);
iscsi_session_attr(abort_tmo, ISCSI_PARAM_ABORT_TMO, 0);
iscsi_session_attr(lu_reset_tmo, ISCSI_PARAM_LU_RESET_TMO, 0);
iscsi_session_attr(ifacename, ISCSI_PARAM_IFACE_NAME, 0);
iscsi_session_attr(initiatorname, ISCSI_PARAM_INITIATOR_NAME, 0)
static ssize_t
show_priv_session_state(struct device *dev, struct device_attribute *attr,
@ -1580,6 +1811,8 @@ iscsi_register_transport(struct iscsi_transport *tt)
priv->daemon_pid = -1;
priv->iscsi_transport = tt;
priv->t.user_scan = iscsi_user_scan;
if (!(tt->caps & CAP_DATA_PATH_OFFLOAD))
priv->t.create_work_queue = 1;
priv->dev.class = &iscsi_transport_class;
snprintf(priv->dev.bus_id, BUS_ID_SIZE, "%s", tt->name);
@ -1595,7 +1828,7 @@ iscsi_register_transport(struct iscsi_transport *tt)
priv->t.host_attrs.ac.attrs = &priv->host_attrs[0];
priv->t.host_attrs.ac.class = &iscsi_host_class.class;
priv->t.host_attrs.ac.match = iscsi_host_match;
priv->t.host_size = sizeof(struct iscsi_host);
priv->t.host_size = sizeof(struct iscsi_cls_host);
transport_container_register(&priv->t.host_attrs);
SETUP_HOST_RD_ATTR(netdev, ISCSI_HOST_NETDEV_NAME);
@ -1653,6 +1886,8 @@ iscsi_register_transport(struct iscsi_transport *tt)
SETUP_SESSION_RD_ATTR(fast_abort, ISCSI_FAST_ABORT);
SETUP_SESSION_RD_ATTR(abort_tmo, ISCSI_ABORT_TMO);
SETUP_SESSION_RD_ATTR(lu_reset_tmo,ISCSI_LU_RESET_TMO);
SETUP_SESSION_RD_ATTR(ifacename, ISCSI_IFACE_NAME);
SETUP_SESSION_RD_ATTR(initiatorname, ISCSI_INITIATOR_NAME);
SETUP_PRIV_SESSION_RD_ATTR(recovery_tmo);
SETUP_PRIV_SESSION_RD_ATTR(state);
@ -1668,6 +1903,7 @@ iscsi_register_transport(struct iscsi_transport *tt)
unregister_dev:
device_unregister(&priv->dev);
return NULL;
free_priv:
kfree(priv);
return NULL;
@ -1715,10 +1951,14 @@ static __init int iscsi_transport_init(void)
if (err)
return err;
err = transport_class_register(&iscsi_host_class);
err = class_register(&iscsi_endpoint_class);
if (err)
goto unregister_transport_class;
err = transport_class_register(&iscsi_host_class);
if (err)
goto unregister_endpoint_class;
err = transport_class_register(&iscsi_connection_class);
if (err)
goto unregister_host_class;
@ -1727,8 +1967,8 @@ static __init int iscsi_transport_init(void)
if (err)
goto unregister_conn_class;
nls = netlink_kernel_create(&init_net, NETLINK_ISCSI, 1, iscsi_if_rx, NULL,
THIS_MODULE);
nls = netlink_kernel_create(&init_net, NETLINK_ISCSI, 1, iscsi_if_rx,
NULL, THIS_MODULE);
if (!nls) {
err = -ENOBUFS;
goto unregister_session_class;
@ -1748,6 +1988,8 @@ static __init int iscsi_transport_init(void)
transport_class_unregister(&iscsi_connection_class);
unregister_host_class:
transport_class_unregister(&iscsi_host_class);
unregister_endpoint_class:
class_unregister(&iscsi_endpoint_class);
unregister_transport_class:
class_unregister(&iscsi_transport_class);
return err;
@ -1760,6 +2002,7 @@ static void __exit iscsi_transport_exit(void)
transport_class_unregister(&iscsi_connection_class);
transport_class_unregister(&iscsi_session_class);
transport_class_unregister(&iscsi_host_class);
class_unregister(&iscsi_endpoint_class);
class_unregister(&iscsi_transport_class);
}

View File

@ -58,8 +58,8 @@
#include <scsi/scsi_host.h>
#include <scsi/scsi_ioctl.h>
#include <scsi/scsicam.h>
#include <scsi/sd.h>
#include "sd.h"
#include "scsi_logging.h"
MODULE_AUTHOR("Eric Youngdale");
@ -295,11 +295,6 @@ static int sd_major(int major_idx)
}
}
static inline struct scsi_disk *scsi_disk(struct gendisk *disk)
{
return container_of(disk->private_data, struct scsi_disk, driver);
}
static struct scsi_disk *__scsi_disk_get(struct gendisk *disk)
{
struct scsi_disk *sdkp = NULL;

View File

@ -48,6 +48,11 @@ struct scsi_disk {
};
#define to_scsi_disk(obj) container_of(obj,struct scsi_disk,dev)
static inline struct scsi_disk *scsi_disk(struct gendisk *disk)
{
return container_of(disk->private_data, struct scsi_disk, driver);
}
#define sd_printk(prefix, sdsk, fmt, a...) \
(sdsk)->disk ? \
sdev_printk(prefix, (sdsk)->device, "[%s] " fmt, \

View File

@ -1036,6 +1036,9 @@ sg_ioctl(struct inode *inode, struct file *filp,
case SG_SCSI_RESET_DEVICE:
val = SCSI_TRY_RESET_DEVICE;
break;
case SG_SCSI_RESET_TARGET:
val = SCSI_TRY_RESET_TARGET;
break;
case SG_SCSI_RESET_BUS:
val = SCSI_TRY_RESET_BUS;
break;

View File

@ -121,9 +121,7 @@ static __inline void sym_que_move(struct sym_quehead *orig,
}
}
#define sym_que_entry(ptr, type, member) \
((type *)((char *)(ptr)-(unsigned int)(&((type *)0)->member)))
#define sym_que_entry(ptr, type, member) container_of(ptr, type, member)
#define sym_insque(new, pos) __sym_que_add(new, pos, (pos)->flink)

View File

@ -0,0 +1,8 @@
#ifndef _LINUX_CRC_T10DIF_H
#define _LINUX_CRC_T10DIF_H
#include <linux/types.h>
__u16 crc_t10dif(unsigned char const *, size_t);
#endif

View File

@ -50,6 +50,7 @@ enum iscsi_uevent_e {
ISCSI_UEVENT_TGT_DSCVR = UEVENT_BASE + 15,
ISCSI_UEVENT_SET_HOST_PARAM = UEVENT_BASE + 16,
ISCSI_UEVENT_UNBIND_SESSION = UEVENT_BASE + 17,
ISCSI_UEVENT_CREATE_BOUND_SESSION = UEVENT_BASE + 18,
/* up events */
ISCSI_KEVENT_RECV_PDU = KEVENT_BASE + 1,
@ -78,6 +79,12 @@ struct iscsi_uevent {
uint16_t cmds_max;
uint16_t queue_depth;
} c_session;
struct msg_create_bound_session {
uint64_t ep_handle;
uint32_t initial_cmdsn;
uint16_t cmds_max;
uint16_t queue_depth;
} c_bound_session;
struct msg_destroy_session {
uint32_t sid;
} d_session;
@ -250,42 +257,49 @@ enum iscsi_param {
ISCSI_PARAM_PING_TMO,
ISCSI_PARAM_RECV_TMO,
ISCSI_PARAM_IFACE_NAME,
ISCSI_PARAM_ISID,
ISCSI_PARAM_INITIATOR_NAME,
/* must always be last */
ISCSI_PARAM_MAX,
};
#define ISCSI_MAX_RECV_DLENGTH (1 << ISCSI_PARAM_MAX_RECV_DLENGTH)
#define ISCSI_MAX_XMIT_DLENGTH (1 << ISCSI_PARAM_MAX_XMIT_DLENGTH)
#define ISCSI_HDRDGST_EN (1 << ISCSI_PARAM_HDRDGST_EN)
#define ISCSI_DATADGST_EN (1 << ISCSI_PARAM_DATADGST_EN)
#define ISCSI_INITIAL_R2T_EN (1 << ISCSI_PARAM_INITIAL_R2T_EN)
#define ISCSI_MAX_R2T (1 << ISCSI_PARAM_MAX_R2T)
#define ISCSI_IMM_DATA_EN (1 << ISCSI_PARAM_IMM_DATA_EN)
#define ISCSI_FIRST_BURST (1 << ISCSI_PARAM_FIRST_BURST)
#define ISCSI_MAX_BURST (1 << ISCSI_PARAM_MAX_BURST)
#define ISCSI_PDU_INORDER_EN (1 << ISCSI_PARAM_PDU_INORDER_EN)
#define ISCSI_DATASEQ_INORDER_EN (1 << ISCSI_PARAM_DATASEQ_INORDER_EN)
#define ISCSI_ERL (1 << ISCSI_PARAM_ERL)
#define ISCSI_IFMARKER_EN (1 << ISCSI_PARAM_IFMARKER_EN)
#define ISCSI_OFMARKER_EN (1 << ISCSI_PARAM_OFMARKER_EN)
#define ISCSI_EXP_STATSN (1 << ISCSI_PARAM_EXP_STATSN)
#define ISCSI_TARGET_NAME (1 << ISCSI_PARAM_TARGET_NAME)
#define ISCSI_TPGT (1 << ISCSI_PARAM_TPGT)
#define ISCSI_PERSISTENT_ADDRESS (1 << ISCSI_PARAM_PERSISTENT_ADDRESS)
#define ISCSI_PERSISTENT_PORT (1 << ISCSI_PARAM_PERSISTENT_PORT)
#define ISCSI_SESS_RECOVERY_TMO (1 << ISCSI_PARAM_SESS_RECOVERY_TMO)
#define ISCSI_CONN_PORT (1 << ISCSI_PARAM_CONN_PORT)
#define ISCSI_CONN_ADDRESS (1 << ISCSI_PARAM_CONN_ADDRESS)
#define ISCSI_USERNAME (1 << ISCSI_PARAM_USERNAME)
#define ISCSI_USERNAME_IN (1 << ISCSI_PARAM_USERNAME_IN)
#define ISCSI_PASSWORD (1 << ISCSI_PARAM_PASSWORD)
#define ISCSI_PASSWORD_IN (1 << ISCSI_PARAM_PASSWORD_IN)
#define ISCSI_FAST_ABORT (1 << ISCSI_PARAM_FAST_ABORT)
#define ISCSI_ABORT_TMO (1 << ISCSI_PARAM_ABORT_TMO)
#define ISCSI_LU_RESET_TMO (1 << ISCSI_PARAM_LU_RESET_TMO)
#define ISCSI_HOST_RESET_TMO (1 << ISCSI_PARAM_HOST_RESET_TMO)
#define ISCSI_PING_TMO (1 << ISCSI_PARAM_PING_TMO)
#define ISCSI_RECV_TMO (1 << ISCSI_PARAM_RECV_TMO)
#define ISCSI_MAX_RECV_DLENGTH (1ULL << ISCSI_PARAM_MAX_RECV_DLENGTH)
#define ISCSI_MAX_XMIT_DLENGTH (1ULL << ISCSI_PARAM_MAX_XMIT_DLENGTH)
#define ISCSI_HDRDGST_EN (1ULL << ISCSI_PARAM_HDRDGST_EN)
#define ISCSI_DATADGST_EN (1ULL << ISCSI_PARAM_DATADGST_EN)
#define ISCSI_INITIAL_R2T_EN (1ULL << ISCSI_PARAM_INITIAL_R2T_EN)
#define ISCSI_MAX_R2T (1ULL << ISCSI_PARAM_MAX_R2T)
#define ISCSI_IMM_DATA_EN (1ULL << ISCSI_PARAM_IMM_DATA_EN)
#define ISCSI_FIRST_BURST (1ULL << ISCSI_PARAM_FIRST_BURST)
#define ISCSI_MAX_BURST (1ULL << ISCSI_PARAM_MAX_BURST)
#define ISCSI_PDU_INORDER_EN (1ULL << ISCSI_PARAM_PDU_INORDER_EN)
#define ISCSI_DATASEQ_INORDER_EN (1ULL << ISCSI_PARAM_DATASEQ_INORDER_EN)
#define ISCSI_ERL (1ULL << ISCSI_PARAM_ERL)
#define ISCSI_IFMARKER_EN (1ULL << ISCSI_PARAM_IFMARKER_EN)
#define ISCSI_OFMARKER_EN (1ULL << ISCSI_PARAM_OFMARKER_EN)
#define ISCSI_EXP_STATSN (1ULL << ISCSI_PARAM_EXP_STATSN)
#define ISCSI_TARGET_NAME (1ULL << ISCSI_PARAM_TARGET_NAME)
#define ISCSI_TPGT (1ULL << ISCSI_PARAM_TPGT)
#define ISCSI_PERSISTENT_ADDRESS (1ULL << ISCSI_PARAM_PERSISTENT_ADDRESS)
#define ISCSI_PERSISTENT_PORT (1ULL << ISCSI_PARAM_PERSISTENT_PORT)
#define ISCSI_SESS_RECOVERY_TMO (1ULL << ISCSI_PARAM_SESS_RECOVERY_TMO)
#define ISCSI_CONN_PORT (1ULL << ISCSI_PARAM_CONN_PORT)
#define ISCSI_CONN_ADDRESS (1ULL << ISCSI_PARAM_CONN_ADDRESS)
#define ISCSI_USERNAME (1ULL << ISCSI_PARAM_USERNAME)
#define ISCSI_USERNAME_IN (1ULL << ISCSI_PARAM_USERNAME_IN)
#define ISCSI_PASSWORD (1ULL << ISCSI_PARAM_PASSWORD)
#define ISCSI_PASSWORD_IN (1ULL << ISCSI_PARAM_PASSWORD_IN)
#define ISCSI_FAST_ABORT (1ULL << ISCSI_PARAM_FAST_ABORT)
#define ISCSI_ABORT_TMO (1ULL << ISCSI_PARAM_ABORT_TMO)
#define ISCSI_LU_RESET_TMO (1ULL << ISCSI_PARAM_LU_RESET_TMO)
#define ISCSI_HOST_RESET_TMO (1ULL << ISCSI_PARAM_HOST_RESET_TMO)
#define ISCSI_PING_TMO (1ULL << ISCSI_PARAM_PING_TMO)
#define ISCSI_RECV_TMO (1ULL << ISCSI_PARAM_RECV_TMO)
#define ISCSI_IFACE_NAME (1ULL << ISCSI_PARAM_IFACE_NAME)
#define ISCSI_ISID (1ULL << ISCSI_PARAM_ISID)
#define ISCSI_INITIATOR_NAME (1ULL << ISCSI_PARAM_INITIATOR_NAME)
/* iSCSI HBA params */
enum iscsi_host_param {
@ -296,20 +310,13 @@ enum iscsi_host_param {
ISCSI_HOST_PARAM_MAX,
};
#define ISCSI_HOST_HWADDRESS (1 << ISCSI_HOST_PARAM_HWADDRESS)
#define ISCSI_HOST_INITIATOR_NAME (1 << ISCSI_HOST_PARAM_INITIATOR_NAME)
#define ISCSI_HOST_NETDEV_NAME (1 << ISCSI_HOST_PARAM_NETDEV_NAME)
#define ISCSI_HOST_IPADDRESS (1 << ISCSI_HOST_PARAM_IPADDRESS)
#define ISCSI_HOST_HWADDRESS (1ULL << ISCSI_HOST_PARAM_HWADDRESS)
#define ISCSI_HOST_INITIATOR_NAME (1ULL << ISCSI_HOST_PARAM_INITIATOR_NAME)
#define ISCSI_HOST_NETDEV_NAME (1ULL << ISCSI_HOST_PARAM_NETDEV_NAME)
#define ISCSI_HOST_IPADDRESS (1ULL << ISCSI_HOST_PARAM_IPADDRESS)
#define iscsi_ptr(_handle) ((void*)(unsigned long)_handle)
#define iscsi_handle(_ptr) ((uint64_t)(unsigned long)_ptr)
#define hostdata_session(_hostdata) (iscsi_ptr(*(unsigned long *)_hostdata))
/**
* iscsi_hostdata - get LLD hostdata from scsi_host
* @_hostdata: pointer to scsi host's hostdata
**/
#define iscsi_hostdata(_hostdata) ((void*)_hostdata + sizeof(unsigned long))
/*
* These flags presents iSCSI Data-Path capabilities.

View File

@ -22,6 +22,7 @@
#define ISCSI_PROTO_H
#include <linux/types.h>
#include <scsi/scsi.h>
#define ISCSI_DRAFT20_VERSION 0x00
@ -156,7 +157,7 @@ struct iscsi_ecdb_ahdr {
uint8_t ahstype;
uint8_t reserved;
/* 4-byte aligned extended CDB spillover */
uint8_t ecdb[260 - ISCSI_CDB_SIZE];
uint8_t ecdb[SCSI_MAX_VARLEN_CDB_SIZE - ISCSI_CDB_SIZE];
};
/* SCSI Response Header */

View File

@ -24,6 +24,7 @@
#define LIBISCSI_H
#include <linux/types.h>
#include <linux/wait.h>
#include <linux/mutex.h>
#include <linux/timer.h>
#include <linux/workqueue.h>
@ -31,6 +32,7 @@
#include <scsi/iscsi_if.h>
struct scsi_transport_template;
struct scsi_host_template;
struct scsi_device;
struct Scsi_Host;
struct scsi_cmnd;
@ -40,6 +42,7 @@ struct iscsi_cls_session;
struct iscsi_cls_conn;
struct iscsi_session;
struct iscsi_nopin;
struct device;
/* #define DEBUG_SCSI */
#ifdef DEBUG_SCSI
@ -49,9 +52,7 @@ struct iscsi_nopin;
#endif
#define ISCSI_DEF_XMIT_CMDS_MAX 128 /* must be power of 2 */
#define ISCSI_MGMT_CMDS_MAX 16 /* must be power of 2 */
#define ISCSI_MGMT_ITT_OFFSET 0xa00
#define ISCSI_MGMT_CMDS_MAX 15
#define ISCSI_DEF_CMD_PER_LUN 32
#define ISCSI_MAX_CMD_PER_LUN 128
@ -69,7 +70,10 @@ enum {
/* Connection suspend "bit" */
#define ISCSI_SUSPEND_BIT 1
#define ISCSI_ITT_MASK (0xfff)
#define ISCSI_ITT_MASK (0x1fff)
#define ISCSI_TOTAL_CMDS_MAX 4096
/* this must be a power of two greater than ISCSI_MGMT_CMDS_MAX */
#define ISCSI_TOTAL_CMDS_MIN 16
#define ISCSI_AGE_SHIFT 28
#define ISCSI_AGE_MASK (0xf << ISCSI_AGE_SHIFT)
@ -82,18 +86,6 @@ enum {
ISCSI_DIGEST_SIZE = sizeof(__u32),
};
struct iscsi_mgmt_task {
/*
* Becuae LLDs allocate their hdr differently, this is a pointer to
* that storage. It must be setup at session creation time.
*/
struct iscsi_hdr *hdr;
char *data; /* mgmt payload */
unsigned data_count; /* counts data to be sent */
uint32_t itt; /* this ITT */
void *dd_data; /* driver/transport data */
struct list_head running;
};
enum {
ISCSI_TASK_COMPLETED,
@ -101,7 +93,7 @@ enum {
ISCSI_TASK_RUNNING,
};
struct iscsi_cmd_task {
struct iscsi_task {
/*
* Because LLDs allocate their hdr differently, this is a pointer
* and length to that storage. It must be setup at session
@ -118,6 +110,7 @@ struct iscsi_cmd_task {
/* offset in unsolicited stream (bytes); */
unsigned unsol_offset;
unsigned data_count; /* remaining Data-Out */
char *data; /* mgmt payload */
struct scsi_cmnd *sc; /* associated SCSI cmd*/
struct iscsi_conn *conn; /* used connection */
@ -128,9 +121,9 @@ struct iscsi_cmd_task {
void *dd_data; /* driver/transport data */
};
static inline void* iscsi_next_hdr(struct iscsi_cmd_task *ctask)
static inline void* iscsi_next_hdr(struct iscsi_task *task)
{
return (void*)ctask->hdr + ctask->hdr_len;
return (void*)task->hdr + task->hdr_len;
}
/* Connection's states */
@ -145,11 +138,6 @@ struct iscsi_conn {
struct iscsi_cls_conn *cls_conn; /* ptr to class connection */
void *dd_data; /* iscsi_transport data */
struct iscsi_session *session; /* parent session */
/*
* LLDs should set this lock. It protects the transport recv
* code
*/
rwlock_t *recv_lock;
/*
* conn_stop() flag: stop to recover, stop to terminate
*/
@ -159,7 +147,7 @@ struct iscsi_conn {
unsigned long last_ping;
int ping_timeout;
int recv_timeout;
struct iscsi_mgmt_task *ping_mtask;
struct iscsi_task *ping_task;
/* iSCSI connection-wide sequencing */
uint32_t exp_statsn;
@ -175,9 +163,8 @@ struct iscsi_conn {
* should always fit in this buffer
*/
char *data;
struct iscsi_mgmt_task *login_mtask; /* mtask used for login/text */
struct iscsi_mgmt_task *mtask; /* xmit mtask in progress */
struct iscsi_cmd_task *ctask; /* xmit ctask in progress */
struct iscsi_task *login_task; /* mtask used for login/text */
struct iscsi_task *task; /* xmit task in progress */
/* xmit */
struct list_head mgmtqueue; /* mgmt (control) xmit queue */
@ -208,9 +195,6 @@ struct iscsi_conn {
/* remote portal currently connected to */
int portal_port;
char portal_address[ISCSI_ADDRESS_BUF_LEN];
/* local address */
int local_port;
char local_address[ISCSI_ADDRESS_BUF_LEN];
/* MIB-statistics */
uint64_t txdata_octets;
@ -246,6 +230,7 @@ enum {
};
struct iscsi_session {
struct iscsi_cls_session *cls_session;
/*
* Syncs up the scsi eh thread with the iscsi eh thread when sending
* task management functions. This must be taken before the session
@ -281,10 +266,8 @@ struct iscsi_session {
char *password;
char *password_in;
char *targetname;
char *ifacename;
char *initiatorname;
/* hw address or netdev iscsi connection is bound to */
char *hwaddress;
char *netdev;
/* control data */
struct iscsi_transport *tt;
struct Scsi_Host *host;
@ -298,12 +281,20 @@ struct iscsi_session {
int state; /* session state */
int age; /* counts session re-opens */
int scsi_cmds_max; /* max scsi commands */
int cmds_max; /* size of cmds array */
struct iscsi_cmd_task **cmds; /* Original Cmds arr */
struct iscsi_task **cmds; /* Original Cmds arr */
struct iscsi_pool cmdpool; /* PDU's pool */
int mgmtpool_max; /* size of mgmt array */
struct iscsi_mgmt_task **mgmt_cmds; /* Original mgmt arr */
struct iscsi_pool mgmtpool; /* Mgmt PDU's pool */
};
struct iscsi_host {
char *initiatorname;
/* hw address or netdev iscsi connection is bound to */
char *hwaddress;
char *netdev;
/* local address */
int local_port;
char local_address[ISCSI_ADDRESS_BUF_LEN];
};
/*
@ -316,42 +307,44 @@ extern int iscsi_eh_device_reset(struct scsi_cmnd *sc);
extern int iscsi_queuecommand(struct scsi_cmnd *sc,
void (*done)(struct scsi_cmnd *));
/*
* iSCSI host helpers.
*/
#define iscsi_host_priv(_shost) \
(shost_priv(_shost) + sizeof(struct iscsi_host))
extern int iscsi_host_set_param(struct Scsi_Host *shost,
enum iscsi_host_param param, char *buf,
int buflen);
extern int iscsi_host_get_param(struct Scsi_Host *shost,
enum iscsi_host_param param, char *buf);
extern int iscsi_host_add(struct Scsi_Host *shost, struct device *pdev);
extern struct Scsi_Host *iscsi_host_alloc(struct scsi_host_template *sht,
int dd_data_size, uint16_t qdepth);
extern void iscsi_host_remove(struct Scsi_Host *shost);
extern void iscsi_host_free(struct Scsi_Host *shost);
/*
* session management
*/
extern struct iscsi_cls_session *
iscsi_session_setup(struct iscsi_transport *, struct scsi_transport_template *,
uint16_t, uint16_t, int, int, uint32_t, uint32_t *);
iscsi_session_setup(struct iscsi_transport *, struct Scsi_Host *shost,
uint16_t, int, uint32_t, unsigned int);
extern void iscsi_session_teardown(struct iscsi_cls_session *);
extern struct iscsi_session *class_to_transport_session(struct iscsi_cls_session *);
extern void iscsi_session_recovery_timedout(struct iscsi_cls_session *);
extern int iscsi_set_param(struct iscsi_cls_conn *cls_conn,
enum iscsi_param param, char *buf, int buflen);
extern int iscsi_session_get_param(struct iscsi_cls_session *cls_session,
enum iscsi_param param, char *buf);
#define session_to_cls(_sess) \
hostdata_session(_sess->host->hostdata)
#define iscsi_session_printk(prefix, _sess, fmt, a...) \
iscsi_cls_session_printk(prefix, \
(struct iscsi_cls_session *)session_to_cls(_sess), fmt, ##a)
iscsi_cls_session_printk(prefix, _sess->cls_session, fmt, ##a)
/*
* connection management
*/
extern struct iscsi_cls_conn *iscsi_conn_setup(struct iscsi_cls_session *,
uint32_t);
int, uint32_t);
extern void iscsi_conn_teardown(struct iscsi_cls_conn *);
extern int iscsi_conn_start(struct iscsi_cls_conn *);
extern void iscsi_conn_stop(struct iscsi_cls_conn *, int);
@ -360,25 +353,29 @@ extern int iscsi_conn_bind(struct iscsi_cls_session *, struct iscsi_cls_conn *,
extern void iscsi_conn_failure(struct iscsi_conn *conn, enum iscsi_err err);
extern int iscsi_conn_get_param(struct iscsi_cls_conn *cls_conn,
enum iscsi_param param, char *buf);
extern void iscsi_suspend_tx(struct iscsi_conn *conn);
#define iscsi_conn_printk(prefix, _c, fmt, a...) \
iscsi_cls_conn_printk(prefix, _c->cls_conn, fmt, ##a)
iscsi_cls_conn_printk(prefix, ((struct iscsi_conn *)_c)->cls_conn, \
fmt, ##a)
/*
* pdu and task processing
*/
extern void iscsi_update_cmdsn(struct iscsi_session *, struct iscsi_nopin *);
extern void iscsi_prep_unsolicit_data_pdu(struct iscsi_cmd_task *,
extern void iscsi_prep_unsolicit_data_pdu(struct iscsi_task *,
struct iscsi_data *hdr);
extern int iscsi_conn_send_pdu(struct iscsi_cls_conn *, struct iscsi_hdr *,
char *, uint32_t);
extern int iscsi_complete_pdu(struct iscsi_conn *, struct iscsi_hdr *,
char *, int);
extern int iscsi_verify_itt(struct iscsi_conn *, struct iscsi_hdr *,
uint32_t *);
extern void iscsi_requeue_ctask(struct iscsi_cmd_task *ctask);
extern void iscsi_free_mgmt_task(struct iscsi_conn *conn,
struct iscsi_mgmt_task *mtask);
extern int __iscsi_complete_pdu(struct iscsi_conn *, struct iscsi_hdr *,
char *, int);
extern int iscsi_verify_itt(struct iscsi_conn *, itt_t);
extern struct iscsi_task *iscsi_itt_to_ctask(struct iscsi_conn *, itt_t);
extern void iscsi_requeue_task(struct iscsi_task *task);
extern void iscsi_put_task(struct iscsi_task *task);
extern void __iscsi_get_task(struct iscsi_task *task);
/*
* generic helpers

View File

@ -9,6 +9,7 @@
#define _SCSI_SCSI_H
#include <linux/types.h>
#include <scsi/scsi_cmnd.h>
/*
* The maximum number of SG segments that we will put inside a
@ -400,6 +401,7 @@ struct scsi_lun {
#define SOFT_ERROR 0x2005
#define ADD_TO_MLQUEUE 0x2006
#define TIMEOUT_ERROR 0x2007
#define SCSI_RETURN_NOT_HANDLED 0x2008
/*
* Midlevel queue return values.
@ -424,6 +426,22 @@ struct scsi_lun {
#define driver_byte(result) (((result) >> 24) & 0xff)
#define suggestion(result) (driver_byte(result) & SUGGEST_MASK)
static inline void set_msg_byte(struct scsi_cmnd *cmd, char status)
{
cmd->result |= status << 8;
}
static inline void set_host_byte(struct scsi_cmnd *cmd, char status)
{
cmd->result |= status << 16;
}
static inline void set_driver_byte(struct scsi_cmnd *cmd, char status)
{
cmd->result |= status << 24;
}
#define sense_class(sense) (((sense) >> 4) & 0x7)
#define sense_error(sense) ((sense) & 0xf)
#define sense_valid(sense) ((sense) & 0x80);

View File

@ -7,7 +7,6 @@
#include <linux/types.h>
#include <linux/timer.h>
#include <linux/scatterlist.h>
#include <linux/blkdev.h>
struct Scsi_Host;
struct scsi_device;

View File

@ -162,9 +162,29 @@ struct scsi_device {
struct execute_work ew; /* used to get process context on put */
struct scsi_dh_data *scsi_dh_data;
enum scsi_device_state sdev_state;
unsigned long sdev_data[0];
} __attribute__((aligned(sizeof(unsigned long))));
struct scsi_device_handler {
/* Used by the infrastructure */
struct list_head list; /* list of scsi_device_handlers */
struct notifier_block nb;
/* Filled by the hardware handler */
struct module *module;
const char *name;
int (*check_sense)(struct scsi_device *, struct scsi_sense_hdr *);
int (*activate)(struct scsi_device *);
int (*prep_fn)(struct scsi_device *, struct request *);
};
struct scsi_dh_data {
struct scsi_device_handler *scsi_dh;
char buf[0];
};
#define to_scsi_device(d) \
container_of(d, struct scsi_device, sdev_gendev)
#define class_to_sdev(d) \
@ -231,7 +251,9 @@ extern struct scsi_device *__scsi_add_device(struct Scsi_Host *,
uint, uint, uint, void *hostdata);
extern int scsi_add_device(struct Scsi_Host *host, uint channel,
uint target, uint lun);
extern int scsi_register_device_handler(struct scsi_device_handler *scsi_dh);
extern void scsi_remove_device(struct scsi_device *);
extern int scsi_unregister_device_handler(struct scsi_device_handler *scsi_dh);
extern int scsi_device_get(struct scsi_device *);
extern void scsi_device_put(struct scsi_device *);

Some files were not shown because too many files have changed in this diff Show More