diff options
Diffstat (limited to 'drivers/scsi/ibmvscsi')
| -rw-r--r-- | drivers/scsi/ibmvscsi/Makefile | 7 | ||||
| -rw-r--r-- | drivers/scsi/ibmvscsi/ibmvfc.c | 1443 | ||||
| -rw-r--r-- | drivers/scsi/ibmvscsi/ibmvfc.h | 61 | ||||
| -rw-r--r-- | drivers/scsi/ibmvscsi/ibmvscsi.c | 1151 | ||||
| -rw-r--r-- | drivers/scsi/ibmvscsi/ibmvscsi.h | 30 | ||||
| -rw-r--r-- | drivers/scsi/ibmvscsi/ibmvstgt.c | 16 | ||||
| -rw-r--r-- | drivers/scsi/ibmvscsi/iseries_vscsi.c | 167 | ||||
| -rw-r--r-- | drivers/scsi/ibmvscsi/rpa_vscsi.c | 343 | ||||
| -rw-r--r-- | drivers/scsi/ibmvscsi/viosrp.h | 100 |
9 files changed, 2112 insertions, 1206 deletions
diff --git a/drivers/scsi/ibmvscsi/Makefile b/drivers/scsi/ibmvscsi/Makefile index a423d963362..cb150d1e585 100644 --- a/drivers/scsi/ibmvscsi/Makefile +++ b/drivers/scsi/ibmvscsi/Makefile @@ -1,8 +1,3 @@ -obj-$(CONFIG_SCSI_IBMVSCSI) += ibmvscsic.o - -ibmvscsic-y += ibmvscsi.o -ibmvscsic-$(CONFIG_PPC_ISERIES) += iseries_vscsi.o -ibmvscsic-$(CONFIG_PPC_PSERIES) += rpa_vscsi.o - +obj-$(CONFIG_SCSI_IBMVSCSI) += ibmvscsi.o obj-$(CONFIG_SCSI_IBMVSCSIS) += ibmvstgt.o obj-$(CONFIG_SCSI_IBMVFC) += ibmvfc.o diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc.c index ea4abee7a2a..8dd47689d58 100644 --- a/drivers/scsi/ibmvscsi/ibmvfc.c +++ b/drivers/scsi/ibmvscsi/ibmvfc.c @@ -28,7 +28,9 @@ #include <linux/delay.h> #include <linux/interrupt.h> #include <linux/kthread.h> +#include <linux/slab.h> #include <linux/of.h> +#include <linux/pm.h> #include <linux/stringify.h> #include <asm/firmware.h> #include <asm/irq.h> @@ -39,6 +41,7 @@ #include <scsi/scsi_device.h> #include <scsi/scsi_tcq.h> #include <scsi/scsi_transport_fc.h> +#include <scsi/scsi_bsg_fc.h> #include "ibmvfc.h" static unsigned int init_timeout = IBMVFC_INIT_TIMEOUT; @@ -47,7 +50,6 @@ static unsigned int max_lun = IBMVFC_MAX_LUN; static unsigned int max_targets = IBMVFC_MAX_TARGETS; static unsigned int max_requests = IBMVFC_MAX_REQUESTS_DEFAULT; static unsigned int disc_threads = IBMVFC_MAX_DISC_THREADS; -static unsigned int dev_loss_tmo = IBMVFC_DEV_LOSS_TMO; static unsigned int ibmvfc_debug = IBMVFC_DEBUG; static unsigned int log_level = IBMVFC_DEFAULT_LOG_LEVEL; static LIST_HEAD(ibmvfc_head); @@ -81,11 +83,6 @@ MODULE_PARM_DESC(disc_threads, "Number of device discovery threads to use. " module_param_named(debug, ibmvfc_debug, uint, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(debug, "Enable driver debug information. " "[Default=" __stringify(IBMVFC_DEBUG) "]"); -module_param_named(dev_loss_tmo, dev_loss_tmo, uint, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(dev_loss_tmo, "Maximum number of seconds that the FC " - "transport should insulate the loss of a remote port. Once this " - "value is exceeded, the scsi target is removed. " - "[Default=" __stringify(IBMVFC_DEV_LOSS_TMO) "]"); module_param_named(log_level, log_level, uint, 0); MODULE_PARM_DESC(log_level, "Set to 0 - 4 for increasing verbosity of device driver. " "[Default=" __stringify(IBMVFC_DEFAULT_LOG_LEVEL) "]"); @@ -110,7 +107,7 @@ static const struct { { IBMVFC_FABRIC_MAPPED, IBMVFC_XPORT_DEAD, DID_ERROR, 0, 1, "transport dead" }, { IBMVFC_FABRIC_MAPPED, IBMVFC_CONFIG_ERROR, DID_ERROR, 1, 1, "configuration error" }, { IBMVFC_FABRIC_MAPPED, IBMVFC_NAME_SERVER_FAIL, DID_ERROR, 1, 1, "name server failure" }, - { IBMVFC_FABRIC_MAPPED, IBMVFC_LINK_HALTED, DID_REQUEUE, 0, 0, "link halted" }, + { IBMVFC_FABRIC_MAPPED, IBMVFC_LINK_HALTED, DID_REQUEUE, 1, 0, "link halted" }, { IBMVFC_FABRIC_MAPPED, IBMVFC_XPORT_GENERAL, DID_OK, 1, 0, "general transport error" }, { IBMVFC_VIOS_FAILURE, IBMVFC_CRQ_FAILURE, DID_REQUEUE, 1, 1, "CRQ failure" }, @@ -143,6 +140,7 @@ static void ibmvfc_npiv_login(struct ibmvfc_host *); static void ibmvfc_tgt_send_prli(struct ibmvfc_target *); static void ibmvfc_tgt_send_plogi(struct ibmvfc_target *); static void ibmvfc_tgt_query_target(struct ibmvfc_target *); +static void ibmvfc_npiv_logout(struct ibmvfc_host *); static const char *unknown_error = "unknown error"; @@ -275,7 +273,7 @@ static int ibmvfc_get_err_result(struct ibmvfc_cmd *vfc_cmd) int fc_rsp_len = rsp->fcp_rsp_len; if ((rsp->flags & FCP_RSP_LEN_VALID) && - ((!fc_rsp_len && fc_rsp_len != 4 && fc_rsp_len != 8) || + ((fc_rsp_len && fc_rsp_len != 4 && fc_rsp_len != 8) || rsp->data.info.rsp_code)) return DID_ERROR << 16; @@ -429,8 +427,13 @@ static void ibmvfc_set_tgt_action(struct ibmvfc_target *tgt, { switch (tgt->action) { case IBMVFC_TGT_ACTION_DEL_RPORT: + if (action == IBMVFC_TGT_ACTION_DELETED_RPORT) + tgt->action = action; + case IBMVFC_TGT_ACTION_DELETED_RPORT: break; default: + if (action == IBMVFC_TGT_ACTION_DEL_RPORT) + tgt->add_rport = 0; tgt->action = action; break; } @@ -475,6 +478,10 @@ static void ibmvfc_set_host_action(struct ibmvfc_host *vhost, if (vhost->action == IBMVFC_HOST_ACTION_INIT_WAIT) vhost->action = action; break; + case IBMVFC_HOST_ACTION_LOGO_WAIT: + if (vhost->action == IBMVFC_HOST_ACTION_LOGO) + vhost->action = action; + break; case IBMVFC_HOST_ACTION_INIT_WAIT: if (vhost->action == IBMVFC_HOST_ACTION_INIT) vhost->action = action; @@ -483,7 +490,7 @@ static void ibmvfc_set_host_action(struct ibmvfc_host *vhost, switch (vhost->action) { case IBMVFC_HOST_ACTION_INIT_WAIT: case IBMVFC_HOST_ACTION_NONE: - case IBMVFC_HOST_ACTION_TGT_ADD: + case IBMVFC_HOST_ACTION_TGT_DEL_FAILED: vhost->action = action; break; default: @@ -496,10 +503,21 @@ static void ibmvfc_set_host_action(struct ibmvfc_host *vhost, break; case IBMVFC_HOST_ACTION_INIT: case IBMVFC_HOST_ACTION_TGT_DEL: + switch (vhost->action) { + case IBMVFC_HOST_ACTION_RESET: + case IBMVFC_HOST_ACTION_REENABLE: + break; + default: + vhost->action = action; + break; + }; + break; + case IBMVFC_HOST_ACTION_LOGO: case IBMVFC_HOST_ACTION_QUERY_TGTS: case IBMVFC_HOST_ACTION_TGT_DEL_FAILED: - case IBMVFC_HOST_ACTION_TGT_ADD: case IBMVFC_HOST_ACTION_NONE: + case IBMVFC_HOST_ACTION_RESET: + case IBMVFC_HOST_ACTION_REENABLE: default: vhost->action = action; break; @@ -551,12 +569,11 @@ static void ibmvfc_link_down(struct ibmvfc_host *vhost, /** * ibmvfc_init_host - Start host initialization * @vhost: ibmvfc host struct - * @relogin: is this a re-login? * * Return value: * nothing **/ -static void ibmvfc_init_host(struct ibmvfc_host *vhost, int relogin) +static void ibmvfc_init_host(struct ibmvfc_host *vhost) { struct ibmvfc_target *tgt; @@ -570,13 +587,11 @@ static void ibmvfc_init_host(struct ibmvfc_host *vhost, int relogin) } if (!ibmvfc_set_host_state(vhost, IBMVFC_INITIALIZING)) { - if (!relogin) { - memset(vhost->async_crq.msgs, 0, PAGE_SIZE); - vhost->async_crq.cur = 0; - } + memset(vhost->async_crq.msgs, 0, PAGE_SIZE); + vhost->async_crq.cur = 0; list_for_each_entry(tgt, &vhost->targets, queue) - tgt->need_login = 1; + ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT); scsi_block_requests(vhost->host); ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_INIT); vhost->job_step = ibmvfc_npiv_login; @@ -634,7 +649,7 @@ static int ibmvfc_send_crq_init_complete(struct ibmvfc_host *vhost) **/ static void ibmvfc_release_crq_queue(struct ibmvfc_host *vhost) { - long rc; + long rc = 0; struct vio_dev *vdev = to_vio_dev(vhost->dev); struct ibmvfc_crq_queue *crq = &vhost->crq; @@ -642,10 +657,13 @@ static void ibmvfc_release_crq_queue(struct ibmvfc_host *vhost) free_irq(vdev->irq, vhost); tasklet_kill(&vhost->tasklet); do { + if (rc) + msleep(100); rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address); } while (rc == H_BUSY || H_IS_LONG_BUSY(rc)); vhost->state = IBMVFC_NO_CRQ; + vhost->logged_in = 0; dma_unmap_single(vhost->dev, crq->msg_token, PAGE_SIZE, DMA_BIDIRECTIONAL); free_page((unsigned long)crq->msgs); } @@ -659,11 +677,13 @@ static void ibmvfc_release_crq_queue(struct ibmvfc_host *vhost) **/ static int ibmvfc_reenable_crq_queue(struct ibmvfc_host *vhost) { - int rc; + int rc = 0; struct vio_dev *vdev = to_vio_dev(vhost->dev); /* Re-enable the CRQ */ do { + if (rc) + msleep(100); rc = plpar_hcall_norets(H_ENABLE_CRQ, vdev->unit_address); } while (rc == H_IN_PROGRESS || rc == H_BUSY || H_IS_LONG_BUSY(rc)); @@ -682,16 +702,21 @@ static int ibmvfc_reenable_crq_queue(struct ibmvfc_host *vhost) **/ static int ibmvfc_reset_crq(struct ibmvfc_host *vhost) { - int rc; + int rc = 0; + unsigned long flags; struct vio_dev *vdev = to_vio_dev(vhost->dev); struct ibmvfc_crq_queue *crq = &vhost->crq; /* Close the CRQ */ do { + if (rc) + msleep(100); rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address); } while (rc == H_BUSY || H_IS_LONG_BUSY(rc)); + spin_lock_irqsave(vhost->host->host_lock, flags); vhost->state = IBMVFC_NO_CRQ; + vhost->logged_in = 0; ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_NONE); /* Clean out the queue */ @@ -707,6 +732,7 @@ static int ibmvfc_reset_crq(struct ibmvfc_host *vhost) dev_warn(vhost->dev, "Partner adapter not ready\n"); else if (rc != 0) dev_warn(vhost->dev, "Couldn't register crq (rc=%d)\n", rc); + spin_unlock_irqrestore(vhost->host->host_lock, flags); return rc; } @@ -807,27 +833,35 @@ static void ibmvfc_purge_requests(struct ibmvfc_host *vhost, int error_code) } /** + * ibmvfc_hard_reset_host - Reset the connection to the server by breaking the CRQ + * @vhost: struct ibmvfc host to reset + **/ +static void ibmvfc_hard_reset_host(struct ibmvfc_host *vhost) +{ + ibmvfc_purge_requests(vhost, DID_ERROR); + ibmvfc_link_down(vhost, IBMVFC_LINK_DOWN); + ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_RESET); +} + +/** * __ibmvfc_reset_host - Reset the connection to the server (no locking) * @vhost: struct ibmvfc host to reset **/ static void __ibmvfc_reset_host(struct ibmvfc_host *vhost) { - int rc; - - scsi_block_requests(vhost->host); - ibmvfc_purge_requests(vhost, DID_ERROR); - if ((rc = ibmvfc_reset_crq(vhost)) || - (rc = ibmvfc_send_crq_init(vhost)) || - (rc = vio_enable_interrupts(to_vio_dev(vhost->dev)))) { - dev_err(vhost->dev, "Error after reset rc=%d\n", rc); - ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD); + if (vhost->logged_in && vhost->action != IBMVFC_HOST_ACTION_LOGO_WAIT && + !ibmvfc_set_host_state(vhost, IBMVFC_INITIALIZING)) { + scsi_block_requests(vhost->host); + ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_LOGO); + vhost->job_step = ibmvfc_npiv_logout; + wake_up(&vhost->work_wait_q); } else - ibmvfc_link_down(vhost, IBMVFC_LINK_DOWN); + ibmvfc_hard_reset_host(vhost); } /** * ibmvfc_reset_host - Reset the connection to the server - * @vhost: struct ibmvfc host to reset + * @vhost: ibmvfc host struct **/ static void ibmvfc_reset_host(struct ibmvfc_host *vhost) { @@ -842,9 +876,13 @@ static void ibmvfc_reset_host(struct ibmvfc_host *vhost) * ibmvfc_retry_host_init - Retry host initialization if allowed * @vhost: ibmvfc host struct * + * Returns: 1 if init will be retried / 0 if not + * **/ -static void ibmvfc_retry_host_init(struct ibmvfc_host *vhost) +static int ibmvfc_retry_host_init(struct ibmvfc_host *vhost) { + int retry = 0; + if (vhost->action == IBMVFC_HOST_ACTION_INIT_WAIT) { vhost->delay_init = 1; if (++vhost->init_retries > IBMVFC_MAX_HOST_INIT_RETRIES) { @@ -853,11 +891,14 @@ static void ibmvfc_retry_host_init(struct ibmvfc_host *vhost) ibmvfc_link_down(vhost, IBMVFC_HOST_OFFLINE); } else if (vhost->init_retries == IBMVFC_MAX_HOST_INIT_RETRIES) __ibmvfc_reset_host(vhost); - else + else { ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_INIT); + retry = 1; + } } wake_up(&vhost->work_wait_q); + return retry; } /** @@ -1125,7 +1166,7 @@ static void ibmvfc_gather_partition_info(struct ibmvfc_host *vhost) static void ibmvfc_set_login_info(struct ibmvfc_host *vhost) { struct ibmvfc_npiv_login *login_info = &vhost->login_info; - struct device_node *of_node = vhost->dev->archdata.of_node; + struct device_node *of_node = vhost->dev->of_node; const char *location; memset(login_info, 0, sizeof(*login_info)); @@ -1137,8 +1178,9 @@ static void ibmvfc_set_login_info(struct ibmvfc_host *vhost) login_info->partition_num = vhost->partition_number; login_info->vfc_frame_version = 1; login_info->fcp_version = 3; + login_info->flags = IBMVFC_FLUSH_ON_HALT; if (vhost->client_migrated) - login_info->flags = IBMVFC_CLIENT_MIGRATED; + login_info->flags |= IBMVFC_CLIENT_MIGRATED; login_info->max_cmds = max_requests + IBMVFC_NUM_INTERNAL_REQ; login_info->capabilities = IBMVFC_CAN_MIGRATE; @@ -1452,6 +1494,27 @@ static void ibmvfc_log_error(struct ibmvfc_event *evt) } /** + * ibmvfc_relogin - Log back into the specified device + * @sdev: scsi device struct + * + **/ +static void ibmvfc_relogin(struct scsi_device *sdev) +{ + struct ibmvfc_host *vhost = shost_priv(sdev->host); + struct fc_rport *rport = starget_to_rport(scsi_target(sdev)); + struct ibmvfc_target *tgt; + + list_for_each_entry(tgt, &vhost->targets, queue) { + if (rport == tgt->rport) { + ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT); + break; + } + } + + ibmvfc_reinit_host(vhost); +} + +/** * ibmvfc_scsi_done - Handle responses from commands * @evt: ibmvfc event to be handled * @@ -1483,7 +1546,7 @@ static void ibmvfc_scsi_done(struct ibmvfc_event *evt) if ((rsp->flags & FCP_SNS_LEN_VALID) && rsp->fcp_sense_len && rsp_len <= 8) memcpy(cmnd->sense_buffer, rsp->data.sense + rsp_len, sense_len); if ((vfc_cmd->status & IBMVFC_VIOS_FAILURE) && (vfc_cmd->error == IBMVFC_PLOGI_REQUIRED)) - ibmvfc_reinit_host(evt->vhost); + ibmvfc_relogin(cmnd->device); if (!cmnd->result && (!scsi_get_resid(cmnd) || (rsp->flags & FCP_RESID_OVER))) cmnd->result = (DID_ERROR << 16); @@ -1543,7 +1606,7 @@ static inline int ibmvfc_host_chkready(struct ibmvfc_host *vhost) * Returns: * 0 on success / other on failure **/ -static int ibmvfc_queuecommand(struct scsi_cmnd *cmnd, +static int ibmvfc_queuecommand_lck(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *)) { struct ibmvfc_host *vhost = shost_priv(cmnd->device->host); @@ -1609,6 +1672,8 @@ static int ibmvfc_queuecommand(struct scsi_cmnd *cmnd, return 0; } +static DEF_SCSI_QCMD(ibmvfc_queuecommand) + /** * ibmvfc_sync_completion - Signal that a synchronous command has completed * @evt: ibmvfc event struct @@ -1624,6 +1689,276 @@ static void ibmvfc_sync_completion(struct ibmvfc_event *evt) } /** + * ibmvfc_bsg_timeout_done - Completion handler for cancelling BSG commands + * @evt: struct ibmvfc_event + * + **/ +static void ibmvfc_bsg_timeout_done(struct ibmvfc_event *evt) +{ + struct ibmvfc_host *vhost = evt->vhost; + + ibmvfc_free_event(evt); + vhost->aborting_passthru = 0; + dev_info(vhost->dev, "Passthru command cancelled\n"); +} + +/** + * ibmvfc_bsg_timeout - Handle a BSG timeout + * @job: struct fc_bsg_job that timed out + * + * Returns: + * 0 on success / other on failure + **/ +static int ibmvfc_bsg_timeout(struct fc_bsg_job *job) +{ + struct ibmvfc_host *vhost = shost_priv(job->shost); + unsigned long port_id = (unsigned long)job->dd_data; + struct ibmvfc_event *evt; + struct ibmvfc_tmf *tmf; + unsigned long flags; + int rc; + + ENTER; + spin_lock_irqsave(vhost->host->host_lock, flags); + if (vhost->aborting_passthru || vhost->state != IBMVFC_ACTIVE) { + __ibmvfc_reset_host(vhost); + spin_unlock_irqrestore(vhost->host->host_lock, flags); + return 0; + } + + vhost->aborting_passthru = 1; + evt = ibmvfc_get_event(vhost); + ibmvfc_init_event(evt, ibmvfc_bsg_timeout_done, IBMVFC_MAD_FORMAT); + + tmf = &evt->iu.tmf; + memset(tmf, 0, sizeof(*tmf)); + tmf->common.version = 1; + tmf->common.opcode = IBMVFC_TMF_MAD; + tmf->common.length = sizeof(*tmf); + tmf->scsi_id = port_id; + tmf->cancel_key = IBMVFC_PASSTHRU_CANCEL_KEY; + tmf->my_cancel_key = IBMVFC_INTERNAL_CANCEL_KEY; + rc = ibmvfc_send_event(evt, vhost, default_timeout); + + if (rc != 0) { + vhost->aborting_passthru = 0; + dev_err(vhost->dev, "Failed to send cancel event. rc=%d\n", rc); + rc = -EIO; + } else + dev_info(vhost->dev, "Cancelling passthru command to port id 0x%lx\n", + port_id); + + spin_unlock_irqrestore(vhost->host->host_lock, flags); + + LEAVE; + return rc; +} + +/** + * ibmvfc_bsg_plogi - PLOGI into a target to handle a BSG command + * @vhost: struct ibmvfc_host to send command + * @port_id: port ID to send command + * + * Returns: + * 0 on success / other on failure + **/ +static int ibmvfc_bsg_plogi(struct ibmvfc_host *vhost, unsigned int port_id) +{ + struct ibmvfc_port_login *plogi; + struct ibmvfc_target *tgt; + struct ibmvfc_event *evt; + union ibmvfc_iu rsp_iu; + unsigned long flags; + int rc = 0, issue_login = 1; + + ENTER; + spin_lock_irqsave(vhost->host->host_lock, flags); + list_for_each_entry(tgt, &vhost->targets, queue) { + if (tgt->scsi_id == port_id) { + issue_login = 0; + break; + } + } + + if (!issue_login) + goto unlock_out; + if (unlikely((rc = ibmvfc_host_chkready(vhost)))) + goto unlock_out; + + evt = ibmvfc_get_event(vhost); + ibmvfc_init_event(evt, ibmvfc_sync_completion, IBMVFC_MAD_FORMAT); + plogi = &evt->iu.plogi; + memset(plogi, 0, sizeof(*plogi)); + plogi->common.version = 1; + plogi->common.opcode = IBMVFC_PORT_LOGIN; + plogi->common.length = sizeof(*plogi); + plogi->scsi_id = port_id; + evt->sync_iu = &rsp_iu; + init_completion(&evt->comp); + + rc = ibmvfc_send_event(evt, vhost, default_timeout); + spin_unlock_irqrestore(vhost->host->host_lock, flags); + + if (rc) + return -EIO; + + wait_for_completion(&evt->comp); + + if (rsp_iu.plogi.common.status) + rc = -EIO; + + spin_lock_irqsave(vhost->host->host_lock, flags); + ibmvfc_free_event(evt); +unlock_out: + spin_unlock_irqrestore(vhost->host->host_lock, flags); + LEAVE; + return rc; +} + +/** + * ibmvfc_bsg_request - Handle a BSG request + * @job: struct fc_bsg_job to be executed + * + * Returns: + * 0 on success / other on failure + **/ +static int ibmvfc_bsg_request(struct fc_bsg_job *job) +{ + struct ibmvfc_host *vhost = shost_priv(job->shost); + struct fc_rport *rport = job->rport; + struct ibmvfc_passthru_mad *mad; + struct ibmvfc_event *evt; + union ibmvfc_iu rsp_iu; + unsigned long flags, port_id = -1; + unsigned int code = job->request->msgcode; + int rc = 0, req_seg, rsp_seg, issue_login = 0; + u32 fc_flags, rsp_len; + + ENTER; + job->reply->reply_payload_rcv_len = 0; + if (rport) + port_id = rport->port_id; + + switch (code) { + case FC_BSG_HST_ELS_NOLOGIN: + port_id = (job->request->rqst_data.h_els.port_id[0] << 16) | + (job->request->rqst_data.h_els.port_id[1] << 8) | + job->request->rqst_data.h_els.port_id[2]; + case FC_BSG_RPT_ELS: + fc_flags = IBMVFC_FC_ELS; + break; + case FC_BSG_HST_CT: + issue_login = 1; + port_id = (job->request->rqst_data.h_ct.port_id[0] << 16) | + (job->request->rqst_data.h_ct.port_id[1] << 8) | + job->request->rqst_data.h_ct.port_id[2]; + case FC_BSG_RPT_CT: + fc_flags = IBMVFC_FC_CT_IU; + break; + default: + return -ENOTSUPP; + }; + + if (port_id == -1) + return -EINVAL; + if (!mutex_trylock(&vhost->passthru_mutex)) + return -EBUSY; + + job->dd_data = (void *)port_id; + req_seg = dma_map_sg(vhost->dev, job->request_payload.sg_list, + job->request_payload.sg_cnt, DMA_TO_DEVICE); + + if (!req_seg) { + mutex_unlock(&vhost->passthru_mutex); + return -ENOMEM; + } + + rsp_seg = dma_map_sg(vhost->dev, job->reply_payload.sg_list, + job->reply_payload.sg_cnt, DMA_FROM_DEVICE); + + if (!rsp_seg) { + dma_unmap_sg(vhost->dev, job->request_payload.sg_list, + job->request_payload.sg_cnt, DMA_TO_DEVICE); + mutex_unlock(&vhost->passthru_mutex); + return -ENOMEM; + } + + if (req_seg > 1 || rsp_seg > 1) { + rc = -EINVAL; + goto out; + } + + if (issue_login) + rc = ibmvfc_bsg_plogi(vhost, port_id); + + spin_lock_irqsave(vhost->host->host_lock, flags); + + if (unlikely(rc || (rport && (rc = fc_remote_port_chkready(rport)))) || + unlikely((rc = ibmvfc_host_chkready(vhost)))) { + spin_unlock_irqrestore(vhost->host->host_lock, flags); + goto out; + } + + evt = ibmvfc_get_event(vhost); + ibmvfc_init_event(evt, ibmvfc_sync_completion, IBMVFC_MAD_FORMAT); + mad = &evt->iu.passthru; + + memset(mad, 0, sizeof(*mad)); + mad->common.version = 1; + mad->common.opcode = IBMVFC_PASSTHRU; + mad->common.length = sizeof(*mad) - sizeof(mad->fc_iu) - sizeof(mad->iu); + + mad->cmd_ioba.va = (u64)evt->crq.ioba + + offsetof(struct ibmvfc_passthru_mad, iu); + mad->cmd_ioba.len = sizeof(mad->iu); + + mad->iu.cmd_len = job->request_payload.payload_len; + mad->iu.rsp_len = job->reply_payload.payload_len; + mad->iu.flags = fc_flags; + mad->iu.cancel_key = IBMVFC_PASSTHRU_CANCEL_KEY; + + mad->iu.cmd.va = sg_dma_address(job->request_payload.sg_list); + mad->iu.cmd.len = sg_dma_len(job->request_payload.sg_list); + mad->iu.rsp.va = sg_dma_address(job->reply_payload.sg_list); + mad->iu.rsp.len = sg_dma_len(job->reply_payload.sg_list); + mad->iu.scsi_id = port_id; + mad->iu.tag = (u64)evt; + rsp_len = mad->iu.rsp.len; + + evt->sync_iu = &rsp_iu; + init_completion(&evt->comp); + rc = ibmvfc_send_event(evt, vhost, 0); + spin_unlock_irqrestore(vhost->host->host_lock, flags); + + if (rc) { + rc = -EIO; + goto out; + } + + wait_for_completion(&evt->comp); + + if (rsp_iu.passthru.common.status) + rc = -EIO; + else + job->reply->reply_payload_rcv_len = rsp_len; + + spin_lock_irqsave(vhost->host->host_lock, flags); + ibmvfc_free_event(evt); + spin_unlock_irqrestore(vhost->host->host_lock, flags); + job->reply->result = rc; + job->job_done(job); + rc = 0; +out: + dma_unmap_sg(vhost->dev, job->request_payload.sg_list, + job->request_payload.sg_cnt, DMA_TO_DEVICE); + dma_unmap_sg(vhost->dev, job->reply_payload.sg_list, + job->reply_payload.sg_cnt, DMA_FROM_DEVICE); + mutex_unlock(&vhost->passthru_mutex); + LEAVE; + return rc; +} + +/** * ibmvfc_reset_device - Reset the device with the specified reset type * @sdev: scsi device to reset * @type: reset type @@ -1677,7 +2012,10 @@ static int ibmvfc_reset_device(struct scsi_device *sdev, int type, char *desc) sdev_printk(KERN_INFO, sdev, "Resetting %s\n", desc); wait_for_completion(&evt->comp); - if (rsp_iu.cmd.status) { + if (rsp_iu.cmd.status) + rsp_code = ibmvfc_get_err_result(&rsp_iu.cmd); + + if (rsp_code) { if (fc_rsp->flags & FCP_RSP_LEN_VALID) rsp_code = fc_rsp->data.info.rsp_code; @@ -1697,92 +2035,108 @@ static int ibmvfc_reset_device(struct scsi_device *sdev, int type, char *desc) } /** - * ibmvfc_abort_task_set - Abort outstanding commands to the device - * @sdev: scsi device to abort commands - * - * This sends an Abort Task Set to the VIOS for the specified device. This does - * NOT send any cancel to the VIOS. That must be done separately. + * ibmvfc_match_rport - Match function for specified remote port + * @evt: ibmvfc event struct + * @device: device to match (rport) * * Returns: - * 0 on success / other on failure + * 1 if event matches rport / 0 if event does not match rport **/ -static int ibmvfc_abort_task_set(struct scsi_device *sdev) +static int ibmvfc_match_rport(struct ibmvfc_event *evt, void *rport) { - struct ibmvfc_host *vhost = shost_priv(sdev->host); - struct fc_rport *rport = starget_to_rport(scsi_target(sdev)); - struct ibmvfc_cmd *tmf; - struct ibmvfc_event *evt, *found_evt; - union ibmvfc_iu rsp_iu; - struct ibmvfc_fcp_rsp *fc_rsp = &rsp_iu.cmd.rsp; - int rsp_rc = -EBUSY; - unsigned long flags; - int rsp_code = 0; - - spin_lock_irqsave(vhost->host->host_lock, flags); - found_evt = NULL; - list_for_each_entry(evt, &vhost->sent, queue) { - if (evt->cmnd && evt->cmnd->device == sdev) { - found_evt = evt; - break; - } - } + struct fc_rport *cmd_rport; - if (!found_evt) { - if (vhost->log_level > IBMVFC_DEFAULT_LOG_LEVEL) - sdev_printk(KERN_INFO, sdev, "No events found to abort\n"); - spin_unlock_irqrestore(vhost->host->host_lock, flags); - return 0; + if (evt->cmnd) { + cmd_rport = starget_to_rport(scsi_target(evt->cmnd->device)); + if (cmd_rport == rport) + return 1; } + return 0; +} - if (vhost->state == IBMVFC_ACTIVE) { - evt = ibmvfc_get_event(vhost); - ibmvfc_init_event(evt, ibmvfc_sync_completion, IBMVFC_CMD_FORMAT); - - tmf = &evt->iu.cmd; - memset(tmf, 0, sizeof(*tmf)); - tmf->resp.va = (u64)evt->crq.ioba + offsetof(struct ibmvfc_cmd, rsp); - tmf->resp.len = sizeof(tmf->rsp); - tmf->frame_type = IBMVFC_SCSI_FCP_TYPE; - tmf->payload_len = sizeof(tmf->iu); - tmf->resp_len = sizeof(tmf->rsp); - tmf->cancel_key = (unsigned long)sdev->hostdata; - tmf->tgt_scsi_id = rport->port_id; - int_to_scsilun(sdev->lun, &tmf->iu.lun); - tmf->flags = (IBMVFC_NO_MEM_DESC | IBMVFC_TMF); - tmf->iu.tmf_flags = IBMVFC_ABORT_TASK_SET; - evt->sync_iu = &rsp_iu; - - init_completion(&evt->comp); - rsp_rc = ibmvfc_send_event(evt, vhost, default_timeout); - } +/** + * ibmvfc_match_target - Match function for specified target + * @evt: ibmvfc event struct + * @device: device to match (starget) + * + * Returns: + * 1 if event matches starget / 0 if event does not match starget + **/ +static int ibmvfc_match_target(struct ibmvfc_event *evt, void *device) +{ + if (evt->cmnd && scsi_target(evt->cmnd->device) == device) + return 1; + return 0; +} - spin_unlock_irqrestore(vhost->host->host_lock, flags); +/** + * ibmvfc_match_lun - Match function for specified LUN + * @evt: ibmvfc event struct + * @device: device to match (sdev) + * + * Returns: + * 1 if event matches sdev / 0 if event does not match sdev + **/ +static int ibmvfc_match_lun(struct ibmvfc_event *evt, void *device) +{ + if (evt->cmnd && evt->cmnd->device == device) + return 1; + return 0; +} - if (rsp_rc != 0) { - sdev_printk(KERN_ERR, sdev, "Failed to send abort. rc=%d\n", rsp_rc); - return -EIO; - } +/** + * ibmvfc_wait_for_ops - Wait for ops to complete + * @vhost: ibmvfc host struct + * @device: device to match (starget or sdev) + * @match: match function + * + * Returns: + * SUCCESS / FAILED + **/ +static int ibmvfc_wait_for_ops(struct ibmvfc_host *vhost, void *device, + int (*match) (struct ibmvfc_event *, void *)) +{ + struct ibmvfc_event *evt; + DECLARE_COMPLETION_ONSTACK(comp); + int wait; + unsigned long flags; + signed long timeout = IBMVFC_ABORT_WAIT_TIMEOUT * HZ; - sdev_printk(KERN_INFO, sdev, "Aborting outstanding commands\n"); - wait_for_completion(&evt->comp); + ENTER; + do { + wait = 0; + spin_lock_irqsave(vhost->host->host_lock, flags); + list_for_each_entry(evt, &vhost->sent, queue) { + if (match(evt, device)) { + evt->eh_comp = ∁ + wait++; + } + } + spin_unlock_irqrestore(vhost->host->host_lock, flags); - if (rsp_iu.cmd.status) { - if (fc_rsp->flags & FCP_RSP_LEN_VALID) - rsp_code = fc_rsp->data.info.rsp_code; + if (wait) { + timeout = wait_for_completion_timeout(&comp, timeout); - sdev_printk(KERN_ERR, sdev, "Abort failed: %s (%x:%x) " - "flags: %x fcp_rsp: %x, scsi_status: %x\n", - ibmvfc_get_cmd_error(rsp_iu.cmd.status, rsp_iu.cmd.error), - rsp_iu.cmd.status, rsp_iu.cmd.error, fc_rsp->flags, rsp_code, - fc_rsp->scsi_status); - rsp_rc = -EIO; - } else - sdev_printk(KERN_INFO, sdev, "Abort successful\n"); + if (!timeout) { + wait = 0; + spin_lock_irqsave(vhost->host->host_lock, flags); + list_for_each_entry(evt, &vhost->sent, queue) { + if (match(evt, device)) { + evt->eh_comp = NULL; + wait++; + } + } + spin_unlock_irqrestore(vhost->host->host_lock, flags); + if (wait) + dev_err(vhost->dev, "Timed out waiting for aborted commands\n"); + LEAVE; + return wait ? FAILED : SUCCESS; + } + } + } while (wait); - spin_lock_irqsave(vhost->host->host_lock, flags); - ibmvfc_free_event(evt); - spin_unlock_irqrestore(vhost->host->host_lock, flags); - return rsp_rc; + LEAVE; + return SUCCESS; } /** @@ -1825,7 +2179,7 @@ static int ibmvfc_cancel_all(struct scsi_device *sdev, int type) return 0; } - if (vhost->state == IBMVFC_ACTIVE) { + if (vhost->logged_in) { evt = ibmvfc_get_event(vhost); ibmvfc_init_event(evt, ibmvfc_sync_completion, IBMVFC_MAD_FORMAT); @@ -1836,7 +2190,12 @@ static int ibmvfc_cancel_all(struct scsi_device *sdev, int type) tmf->common.length = sizeof(*tmf); tmf->scsi_id = rport->port_id; int_to_scsilun(sdev->lun, &tmf->lun); - tmf->flags = (type | IBMVFC_TMF_LUA_VALID); + if (!(vhost->login_buf->resp.capabilities & IBMVFC_CAN_SUPPRESS_ABTS)) + type &= ~IBMVFC_TMF_SUPPRESS_ABTS; + if (vhost->state == IBMVFC_ACTIVE) + tmf->flags = (type | IBMVFC_TMF_LUA_VALID); + else + tmf->flags = ((type & IBMVFC_TMF_SUPPRESS_ABTS) | IBMVFC_TMF_LUA_VALID); tmf->cancel_key = (unsigned long)sdev->hostdata; tmf->my_cancel_key = (unsigned long)starget->hostdata; @@ -1849,7 +2208,10 @@ static int ibmvfc_cancel_all(struct scsi_device *sdev, int type) if (rsp_rc != 0) { sdev_printk(KERN_ERR, sdev, "Failed to send cancel event. rc=%d\n", rsp_rc); - return -EIO; + /* If failure is received, the host adapter is most likely going + through reset, return success so the caller will wait for the command + being cancelled to get returned */ + return 0; } sdev_printk(KERN_INFO, sdev, "Cancelling outstanding commands.\n"); @@ -1862,7 +2224,15 @@ static int ibmvfc_cancel_all(struct scsi_device *sdev, int type) if (status != IBMVFC_MAD_SUCCESS) { sdev_printk(KERN_WARNING, sdev, "Cancel failed with rc=%x\n", status); - return -EIO; + switch (status) { + case IBMVFC_MAD_DRIVER_FAILED: + case IBMVFC_MAD_CRQ_ERROR: + /* Host adapter most likely going through reset, return success to + the caller will wait for the command being cancelled to get returned */ + return 0; + default: + return -EIO; + }; } sdev_printk(KERN_INFO, sdev, "Successfully cancelled outstanding commands\n"); @@ -1870,88 +2240,158 @@ static int ibmvfc_cancel_all(struct scsi_device *sdev, int type) } /** - * ibmvfc_match_target - Match function for specified target + * ibmvfc_match_key - Match function for specified cancel key * @evt: ibmvfc event struct - * @device: device to match (starget) + * @key: cancel key to match * * Returns: - * 1 if event matches starget / 0 if event does not match starget + * 1 if event matches key / 0 if event does not match key **/ -static int ibmvfc_match_target(struct ibmvfc_event *evt, void *device) +static int ibmvfc_match_key(struct ibmvfc_event *evt, void *key) { - if (evt->cmnd && scsi_target(evt->cmnd->device) == device) + unsigned long cancel_key = (unsigned long)key; + + if (evt->crq.format == IBMVFC_CMD_FORMAT && + evt->iu.cmd.cancel_key == cancel_key) return 1; return 0; } /** - * ibmvfc_match_lun - Match function for specified LUN + * ibmvfc_match_evt - Match function for specified event * @evt: ibmvfc event struct - * @device: device to match (sdev) + * @match: event to match * * Returns: - * 1 if event matches sdev / 0 if event does not match sdev + * 1 if event matches key / 0 if event does not match key **/ -static int ibmvfc_match_lun(struct ibmvfc_event *evt, void *device) +static int ibmvfc_match_evt(struct ibmvfc_event *evt, void *match) { - if (evt->cmnd && evt->cmnd->device == device) + if (evt == match) return 1; return 0; } /** - * ibmvfc_wait_for_ops - Wait for ops to complete - * @vhost: ibmvfc host struct - * @device: device to match (starget or sdev) - * @match: match function + * ibmvfc_abort_task_set - Abort outstanding commands to the device + * @sdev: scsi device to abort commands + * + * This sends an Abort Task Set to the VIOS for the specified device. This does + * NOT send any cancel to the VIOS. That must be done separately. * * Returns: - * SUCCESS / FAILED + * 0 on success / other on failure **/ -static int ibmvfc_wait_for_ops(struct ibmvfc_host *vhost, void *device, - int (*match) (struct ibmvfc_event *, void *)) +static int ibmvfc_abort_task_set(struct scsi_device *sdev) { - struct ibmvfc_event *evt; - DECLARE_COMPLETION_ONSTACK(comp); - int wait; - unsigned long flags; - signed long timeout = init_timeout * HZ; + struct ibmvfc_host *vhost = shost_priv(sdev->host); + struct fc_rport *rport = starget_to_rport(scsi_target(sdev)); + struct ibmvfc_cmd *tmf; + struct ibmvfc_event *evt, *found_evt; + union ibmvfc_iu rsp_iu; + struct ibmvfc_fcp_rsp *fc_rsp = &rsp_iu.cmd.rsp; + int rc, rsp_rc = -EBUSY; + unsigned long flags, timeout = IBMVFC_ABORT_TIMEOUT; + int rsp_code = 0; - ENTER; - do { - wait = 0; - spin_lock_irqsave(vhost->host->host_lock, flags); - list_for_each_entry(evt, &vhost->sent, queue) { - if (match(evt, device)) { - evt->eh_comp = ∁ - wait++; - } + spin_lock_irqsave(vhost->host->host_lock, flags); + found_evt = NULL; + list_for_each_entry(evt, &vhost->sent, queue) { + if (evt->cmnd && evt->cmnd->device == sdev) { + found_evt = evt; + break; } + } + + if (!found_evt) { + if (vhost->log_level > IBMVFC_DEFAULT_LOG_LEVEL) + sdev_printk(KERN_INFO, sdev, "No events found to abort\n"); spin_unlock_irqrestore(vhost->host->host_lock, flags); + return 0; + } - if (wait) { - timeout = wait_for_completion_timeout(&comp, timeout); + if (vhost->state == IBMVFC_ACTIVE) { + evt = ibmvfc_get_event(vhost); + ibmvfc_init_event(evt, ibmvfc_sync_completion, IBMVFC_CMD_FORMAT); - if (!timeout) { - wait = 0; + tmf = &evt->iu.cmd; + memset(tmf, 0, sizeof(*tmf)); + tmf->resp.va = (u64)evt->crq.ioba + offsetof(struct ibmvfc_cmd, rsp); + tmf->resp.len = sizeof(tmf->rsp); + tmf->frame_type = IBMVFC_SCSI_FCP_TYPE; + tmf->payload_len = sizeof(tmf->iu); + tmf->resp_len = sizeof(tmf->rsp); + tmf->cancel_key = (unsigned long)sdev->hostdata; + tmf->tgt_scsi_id = rport->port_id; + int_to_scsilun(sdev->lun, &tmf->iu.lun); + tmf->flags = (IBMVFC_NO_MEM_DESC | IBMVFC_TMF); + tmf->iu.tmf_flags = IBMVFC_ABORT_TASK_SET; + evt->sync_iu = &rsp_iu; + + init_completion(&evt->comp); + rsp_rc = ibmvfc_send_event(evt, vhost, default_timeout); + } + + spin_unlock_irqrestore(vhost->host->host_lock, flags); + + if (rsp_rc != 0) { + sdev_printk(KERN_ERR, sdev, "Failed to send abort. rc=%d\n", rsp_rc); + return -EIO; + } + + sdev_printk(KERN_INFO, sdev, "Aborting outstanding commands\n"); + timeout = wait_for_completion_timeout(&evt->comp, timeout); + + if (!timeout) { + rc = ibmvfc_cancel_all(sdev, 0); + if (!rc) { + rc = ibmvfc_wait_for_ops(vhost, sdev->hostdata, ibmvfc_match_key); + if (rc == SUCCESS) + rc = 0; + } + + if (rc) { + sdev_printk(KERN_INFO, sdev, "Cancel failed, resetting host\n"); + ibmvfc_reset_host(vhost); + rsp_rc = -EIO; + rc = ibmvfc_wait_for_ops(vhost, sdev->hostdata, ibmvfc_match_key); + + if (rc == SUCCESS) + rsp_rc = 0; + + rc = ibmvfc_wait_for_ops(vhost, evt, ibmvfc_match_evt); + if (rc != SUCCESS) { spin_lock_irqsave(vhost->host->host_lock, flags); - list_for_each_entry(evt, &vhost->sent, queue) { - if (match(evt, device)) { - evt->eh_comp = NULL; - wait++; - } - } + ibmvfc_hard_reset_host(vhost); spin_unlock_irqrestore(vhost->host->host_lock, flags); - if (wait) - dev_err(vhost->dev, "Timed out waiting for aborted commands\n"); - LEAVE; - return wait ? FAILED : SUCCESS; + rsp_rc = 0; } + + goto out; } - } while (wait); + } - LEAVE; - return SUCCESS; + if (rsp_iu.cmd.status) + rsp_code = ibmvfc_get_err_result(&rsp_iu.cmd); + + if (rsp_code) { + if (fc_rsp->flags & FCP_RSP_LEN_VALID) + rsp_code = fc_rsp->data.info.rsp_code; + + sdev_printk(KERN_ERR, sdev, "Abort failed: %s (%x:%x) " + "flags: %x fcp_rsp: %x, scsi_status: %x\n", + ibmvfc_get_cmd_error(rsp_iu.cmd.status, rsp_iu.cmd.error), + rsp_iu.cmd.status, rsp_iu.cmd.error, fc_rsp->flags, rsp_code, + fc_rsp->scsi_status); + rsp_rc = -EIO; + } else + sdev_printk(KERN_INFO, sdev, "Abort successful\n"); + +out: + spin_lock_irqsave(vhost->host->host_lock, flags); + ibmvfc_free_event(evt); + spin_unlock_irqrestore(vhost->host->host_lock, flags); + return rsp_rc; } /** @@ -1959,23 +2399,30 @@ static int ibmvfc_wait_for_ops(struct ibmvfc_host *vhost, void *device, * @cmd: scsi command to abort * * Returns: - * SUCCESS / FAILED + * SUCCESS / FAST_IO_FAIL / FAILED **/ static int ibmvfc_eh_abort_handler(struct scsi_cmnd *cmd) { struct scsi_device *sdev = cmd->device; struct ibmvfc_host *vhost = shost_priv(sdev->host); - int cancel_rc, abort_rc; + int cancel_rc, block_rc; int rc = FAILED; ENTER; + block_rc = fc_block_scsi_eh(cmd); ibmvfc_wait_while_resetting(vhost); - cancel_rc = ibmvfc_cancel_all(sdev, IBMVFC_TMF_ABORT_TASK_SET); - abort_rc = ibmvfc_abort_task_set(sdev); + if (block_rc != FAST_IO_FAIL) { + cancel_rc = ibmvfc_cancel_all(sdev, IBMVFC_TMF_ABORT_TASK_SET); + ibmvfc_abort_task_set(sdev); + } else + cancel_rc = ibmvfc_cancel_all(sdev, IBMVFC_TMF_SUPPRESS_ABTS); - if (!cancel_rc && !abort_rc) + if (!cancel_rc) rc = ibmvfc_wait_for_ops(vhost, sdev, ibmvfc_match_lun); + if (block_rc == FAST_IO_FAIL && rc != FAILED) + rc = FAST_IO_FAIL; + LEAVE; return rc; } @@ -1985,49 +2432,56 @@ static int ibmvfc_eh_abort_handler(struct scsi_cmnd *cmd) * @cmd: scsi command struct * * Returns: - * SUCCESS / FAILED + * SUCCESS / FAST_IO_FAIL / FAILED **/ static int ibmvfc_eh_device_reset_handler(struct scsi_cmnd *cmd) { struct scsi_device *sdev = cmd->device; struct ibmvfc_host *vhost = shost_priv(sdev->host); - int cancel_rc, reset_rc; + int cancel_rc, block_rc, reset_rc = 0; int rc = FAILED; ENTER; + block_rc = fc_block_scsi_eh(cmd); ibmvfc_wait_while_resetting(vhost); - cancel_rc = ibmvfc_cancel_all(sdev, IBMVFC_TMF_LUN_RESET); - reset_rc = ibmvfc_reset_device(sdev, IBMVFC_LUN_RESET, "LUN"); + if (block_rc != FAST_IO_FAIL) { + cancel_rc = ibmvfc_cancel_all(sdev, IBMVFC_TMF_LUN_RESET); + reset_rc = ibmvfc_reset_device(sdev, IBMVFC_LUN_RESET, "LUN"); + } else + cancel_rc = ibmvfc_cancel_all(sdev, IBMVFC_TMF_SUPPRESS_ABTS); if (!cancel_rc && !reset_rc) rc = ibmvfc_wait_for_ops(vhost, sdev, ibmvfc_match_lun); + if (block_rc == FAST_IO_FAIL && rc != FAILED) + rc = FAST_IO_FAIL; + LEAVE; return rc; } /** - * ibmvfc_dev_cancel_all - Device iterated cancel all function + * ibmvfc_dev_cancel_all_noreset - Device iterated cancel all function * @sdev: scsi device struct * @data: return code * **/ -static void ibmvfc_dev_cancel_all(struct scsi_device *sdev, void *data) +static void ibmvfc_dev_cancel_all_noreset(struct scsi_device *sdev, void *data) { unsigned long *rc = data; - *rc |= ibmvfc_cancel_all(sdev, IBMVFC_TMF_TGT_RESET); + *rc |= ibmvfc_cancel_all(sdev, IBMVFC_TMF_SUPPRESS_ABTS); } /** - * ibmvfc_dev_abort_all - Device iterated abort task set function + * ibmvfc_dev_cancel_all_reset - Device iterated cancel all function * @sdev: scsi device struct * @data: return code * **/ -static void ibmvfc_dev_abort_all(struct scsi_device *sdev, void *data) +static void ibmvfc_dev_cancel_all_reset(struct scsi_device *sdev, void *data) { unsigned long *rc = data; - *rc |= ibmvfc_abort_task_set(sdev); + *rc |= ibmvfc_cancel_all(sdev, IBMVFC_TMF_TGT_RESET); } /** @@ -2035,25 +2489,33 @@ static void ibmvfc_dev_abort_all(struct scsi_device *sdev, void *data) * @cmd: scsi command struct * * Returns: - * SUCCESS / FAILED + * SUCCESS / FAST_IO_FAIL / FAILED **/ static int ibmvfc_eh_target_reset_handler(struct scsi_cmnd *cmd) { struct scsi_device *sdev = cmd->device; struct ibmvfc_host *vhost = shost_priv(sdev->host); struct scsi_target *starget = scsi_target(sdev); - int reset_rc; + int block_rc; + int reset_rc = 0; int rc = FAILED; unsigned long cancel_rc = 0; ENTER; + block_rc = fc_block_scsi_eh(cmd); ibmvfc_wait_while_resetting(vhost); - starget_for_each_device(starget, &cancel_rc, ibmvfc_dev_cancel_all); - reset_rc = ibmvfc_reset_device(sdev, IBMVFC_TARGET_RESET, "target"); + if (block_rc != FAST_IO_FAIL) { + starget_for_each_device(starget, &cancel_rc, ibmvfc_dev_cancel_all_reset); + reset_rc = ibmvfc_reset_device(sdev, IBMVFC_TARGET_RESET, "target"); + } else + starget_for_each_device(starget, &cancel_rc, ibmvfc_dev_cancel_all_noreset); if (!cancel_rc && !reset_rc) rc = ibmvfc_wait_for_ops(vhost, starget, ibmvfc_match_target); + if (block_rc == FAST_IO_FAIL && rc != FAILED) + rc = FAST_IO_FAIL; + LEAVE; return rc; } @@ -2065,11 +2527,16 @@ static int ibmvfc_eh_target_reset_handler(struct scsi_cmnd *cmd) **/ static int ibmvfc_eh_host_reset_handler(struct scsi_cmnd *cmd) { - int rc; + int rc, block_rc; struct ibmvfc_host *vhost = shost_priv(cmd->device->host); + block_rc = fc_block_scsi_eh(cmd); dev_err(vhost->dev, "Resetting connection due to error recovery\n"); rc = ibmvfc_issue_fc_host_lip(vhost->host); + + if (block_rc == FAST_IO_FAIL) + return FAST_IO_FAIL; + return rc ? FAILED : SUCCESS; } @@ -2082,60 +2549,87 @@ static int ibmvfc_eh_host_reset_handler(struct scsi_cmnd *cmd) **/ static void ibmvfc_terminate_rport_io(struct fc_rport *rport) { - struct scsi_target *starget = to_scsi_target(&rport->dev); - struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); + struct Scsi_Host *shost = rport_to_shost(rport); struct ibmvfc_host *vhost = shost_priv(shost); - unsigned long cancel_rc = 0; - unsigned long abort_rc = 0; - int rc = FAILED; + struct fc_rport *dev_rport; + struct scsi_device *sdev; + unsigned long rc; ENTER; - starget_for_each_device(starget, &cancel_rc, ibmvfc_dev_cancel_all); - starget_for_each_device(starget, &abort_rc, ibmvfc_dev_abort_all); + shost_for_each_device(sdev, shost) { + dev_rport = starget_to_rport(scsi_target(sdev)); + if (dev_rport != rport) + continue; + ibmvfc_cancel_all(sdev, IBMVFC_TMF_SUPPRESS_ABTS); + } - if (!cancel_rc && !abort_rc) - rc = ibmvfc_wait_for_ops(vhost, starget, ibmvfc_match_target); + rc = ibmvfc_wait_for_ops(vhost, rport, ibmvfc_match_rport); if (rc == FAILED) ibmvfc_issue_fc_host_lip(shost); LEAVE; } -static const struct { - enum ibmvfc_async_event ae; - const char *desc; -} ae_desc [] = { - { IBMVFC_AE_ELS_PLOGI, "PLOGI" }, - { IBMVFC_AE_ELS_LOGO, "LOGO" }, - { IBMVFC_AE_ELS_PRLO, "PRLO" }, - { IBMVFC_AE_SCN_NPORT, "N-Port SCN" }, - { IBMVFC_AE_SCN_GROUP, "Group SCN" }, - { IBMVFC_AE_SCN_DOMAIN, "Domain SCN" }, - { IBMVFC_AE_SCN_FABRIC, "Fabric SCN" }, - { IBMVFC_AE_LINK_UP, "Link Up" }, - { IBMVFC_AE_LINK_DOWN, "Link Down" }, - { IBMVFC_AE_LINK_DEAD, "Link Dead" }, - { IBMVFC_AE_HALT, "Halt" }, - { IBMVFC_AE_RESUME, "Resume" }, - { IBMVFC_AE_ADAPTER_FAILED, "Adapter Failed" }, +static const struct ibmvfc_async_desc ae_desc [] = { + { "PLOGI", IBMVFC_AE_ELS_PLOGI, IBMVFC_DEFAULT_LOG_LEVEL + 1 }, + { "LOGO", IBMVFC_AE_ELS_LOGO, IBMVFC_DEFAULT_LOG_LEVEL + 1 }, + { "PRLO", IBMVFC_AE_ELS_PRLO, IBMVFC_DEFAULT_LOG_LEVEL + 1 }, + { "N-Port SCN", IBMVFC_AE_SCN_NPORT, IBMVFC_DEFAULT_LOG_LEVEL + 1 }, + { "Group SCN", IBMVFC_AE_SCN_GROUP, IBMVFC_DEFAULT_LOG_LEVEL + 1 }, + { "Domain SCN", IBMVFC_AE_SCN_DOMAIN, IBMVFC_DEFAULT_LOG_LEVEL }, + { "Fabric SCN", IBMVFC_AE_SCN_FABRIC, IBMVFC_DEFAULT_LOG_LEVEL }, + { "Link Up", IBMVFC_AE_LINK_UP, IBMVFC_DEFAULT_LOG_LEVEL }, + { "Link Down", IBMVFC_AE_LINK_DOWN, IBMVFC_DEFAULT_LOG_LEVEL }, + { "Link Dead", IBMVFC_AE_LINK_DEAD, IBMVFC_DEFAULT_LOG_LEVEL }, + { "Halt", IBMVFC_AE_HALT, IBMVFC_DEFAULT_LOG_LEVEL }, + { "Resume", IBMVFC_AE_RESUME, IBMVFC_DEFAULT_LOG_LEVEL }, + { "Adapter Failed", IBMVFC_AE_ADAPTER_FAILED, IBMVFC_DEFAULT_LOG_LEVEL }, }; -static const char *unknown_ae = "Unknown async"; +static const struct ibmvfc_async_desc unknown_ae = { + "Unknown async", 0, IBMVFC_DEFAULT_LOG_LEVEL +}; /** * ibmvfc_get_ae_desc - Get text description for async event * @ae: async event * **/ -static const char *ibmvfc_get_ae_desc(u64 ae) +static const struct ibmvfc_async_desc *ibmvfc_get_ae_desc(u64 ae) { int i; for (i = 0; i < ARRAY_SIZE(ae_desc); i++) if (ae_desc[i].ae == ae) - return ae_desc[i].desc; + return &ae_desc[i]; + + return &unknown_ae; +} + +static const struct { + enum ibmvfc_ae_link_state state; + const char *desc; +} link_desc [] = { + { IBMVFC_AE_LS_LINK_UP, " link up" }, + { IBMVFC_AE_LS_LINK_BOUNCED, " link bounced" }, + { IBMVFC_AE_LS_LINK_DOWN, " link down" }, + { IBMVFC_AE_LS_LINK_DEAD, " link dead" }, +}; - return unknown_ae; +/** + * ibmvfc_get_link_state - Get text description for link state + * @state: link state + * + **/ +static const char *ibmvfc_get_link_state(enum ibmvfc_ae_link_state state) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(link_desc); i++) + if (link_desc[i].state == state) + return link_desc[i].desc; + + return ""; } /** @@ -2147,14 +2641,33 @@ static const char *ibmvfc_get_ae_desc(u64 ae) static void ibmvfc_handle_async(struct ibmvfc_async_crq *crq, struct ibmvfc_host *vhost) { - const char *desc = ibmvfc_get_ae_desc(crq->event); + const struct ibmvfc_async_desc *desc = ibmvfc_get_ae_desc(crq->event); + struct ibmvfc_target *tgt; - ibmvfc_log(vhost, 3, "%s event received. scsi_id: %llx, wwpn: %llx," - " node_name: %llx\n", desc, crq->scsi_id, crq->wwpn, crq->node_name); + ibmvfc_log(vhost, desc->log_level, "%s event received. scsi_id: %llx, wwpn: %llx," + " node_name: %llx%s\n", desc->desc, crq->scsi_id, crq->wwpn, crq->node_name, + ibmvfc_get_link_state(crq->link_state)); switch (crq->event) { - case IBMVFC_AE_LINK_UP: case IBMVFC_AE_RESUME: + switch (crq->link_state) { + case IBMVFC_AE_LS_LINK_DOWN: + ibmvfc_link_down(vhost, IBMVFC_LINK_DOWN); + break; + case IBMVFC_AE_LS_LINK_DEAD: + ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD); + break; + case IBMVFC_AE_LS_LINK_UP: + case IBMVFC_AE_LS_LINK_BOUNCED: + default: + vhost->events_to_log |= IBMVFC_AE_LINKUP; + vhost->delay_init = 1; + __ibmvfc_reset_host(vhost); + break; + }; + + break; + case IBMVFC_AE_LINK_UP: vhost->events_to_log |= IBMVFC_AE_LINKUP; vhost->delay_init = 1; __ibmvfc_reset_host(vhost); @@ -2162,16 +2675,35 @@ static void ibmvfc_handle_async(struct ibmvfc_async_crq *crq, case IBMVFC_AE_SCN_FABRIC: case IBMVFC_AE_SCN_DOMAIN: vhost->events_to_log |= IBMVFC_AE_RSCN; - vhost->delay_init = 1; - __ibmvfc_reset_host(vhost); + if (vhost->state < IBMVFC_HALTED) { + vhost->delay_init = 1; + __ibmvfc_reset_host(vhost); + } break; case IBMVFC_AE_SCN_NPORT: case IBMVFC_AE_SCN_GROUP: vhost->events_to_log |= IBMVFC_AE_RSCN; + ibmvfc_reinit_host(vhost); + break; case IBMVFC_AE_ELS_LOGO: case IBMVFC_AE_ELS_PRLO: case IBMVFC_AE_ELS_PLOGI: - ibmvfc_reinit_host(vhost); + list_for_each_entry(tgt, &vhost->targets, queue) { + if (!crq->scsi_id && !crq->wwpn && !crq->node_name) + break; + if (crq->scsi_id && tgt->scsi_id != crq->scsi_id) + continue; + if (crq->wwpn && tgt->ids.port_name != crq->wwpn) + continue; + if (crq->node_name && tgt->ids.node_name != crq->node_name) + continue; + if (tgt->need_login && crq->event == IBMVFC_AE_ELS_LOGO) + tgt->logo_rcvd = 1; + if (!tgt->need_login || crq->event == IBMVFC_AE_ELS_PLOGI) { + ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT); + ibmvfc_reinit_host(vhost); + } + } break; case IBMVFC_AE_LINK_DOWN: case IBMVFC_AE_ADAPTER_FAILED: @@ -2208,13 +2740,13 @@ static void ibmvfc_handle_crq(struct ibmvfc_crq *crq, struct ibmvfc_host *vhost) /* Send back a response */ rc = ibmvfc_send_crq_init_complete(vhost); if (rc == 0) - ibmvfc_init_host(vhost, 0); + ibmvfc_init_host(vhost); else dev_err(vhost->dev, "Unable to send init rsp. rc=%ld\n", rc); break; case IBMVFC_CRQ_INIT_COMPLETE: dev_info(vhost->dev, "Partner initialization complete\n"); - ibmvfc_init_host(vhost, 0); + ibmvfc_init_host(vhost); break; default: dev_err(vhost->dev, "Unknown crq message type: %d\n", crq->format); @@ -2222,28 +2754,20 @@ static void ibmvfc_handle_crq(struct ibmvfc_crq *crq, struct ibmvfc_host *vhost) return; case IBMVFC_CRQ_XPORT_EVENT: vhost->state = IBMVFC_NO_CRQ; + vhost->logged_in = 0; ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_NONE); if (crq->format == IBMVFC_PARTITION_MIGRATED) { /* We need to re-setup the interpartition connection */ dev_info(vhost->dev, "Re-enabling adapter\n"); vhost->client_migrated = 1; ibmvfc_purge_requests(vhost, DID_REQUEUE); - if ((rc = ibmvfc_reenable_crq_queue(vhost)) || - (rc = ibmvfc_send_crq_init(vhost))) { - ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD); - dev_err(vhost->dev, "Error after enable (rc=%ld)\n", rc); - } else - ibmvfc_link_down(vhost, IBMVFC_LINK_DOWN); + ibmvfc_link_down(vhost, IBMVFC_LINK_DOWN); + ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_REENABLE); } else { dev_err(vhost->dev, "Virtual adapter failed (rc=%d)\n", crq->format); - ibmvfc_purge_requests(vhost, DID_ERROR); - if ((rc = ibmvfc_reset_crq(vhost)) || - (rc = ibmvfc_send_crq_init(vhost))) { - ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD); - dev_err(vhost->dev, "Error after reset (rc=%ld)\n", rc); - } else - ibmvfc_link_down(vhost, IBMVFC_LINK_DOWN); + ibmvfc_link_down(vhost, IBMVFC_LINK_DOWN); + ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_RESET); } return; case IBMVFC_CRQ_CMD_RSP: @@ -2299,7 +2823,7 @@ static int ibmvfc_scan_finished(struct Scsi_Host *shost, unsigned long time) done = 1; } - if (vhost->state != IBMVFC_NO_CRQ && vhost->action == IBMVFC_HOST_ACTION_NONE) + if (vhost->scan_complete) done = 1; spin_unlock_irqrestore(shost->host_lock, flags); return done; @@ -2366,7 +2890,6 @@ static int ibmvfc_target_alloc(struct scsi_target *starget) static int ibmvfc_slave_configure(struct scsi_device *sdev) { struct Scsi_Host *shost = sdev->host; - struct fc_rport *rport = starget_to_rport(sdev->sdev_target); unsigned long flags = 0; spin_lock_irqsave(shost->host_lock, flags); @@ -2378,8 +2901,6 @@ static int ibmvfc_slave_configure(struct scsi_device *sdev) scsi_activate_tcq(sdev, sdev->queue_depth); } else scsi_deactivate_tcq(sdev, sdev->queue_depth); - - rport->dev_loss_tmo = dev_loss_tmo; spin_unlock_irqrestore(shost->host_lock, flags); return 0; } @@ -2388,12 +2909,17 @@ static int ibmvfc_slave_configure(struct scsi_device *sdev) * ibmvfc_change_queue_depth - Change the device's queue depth * @sdev: scsi device struct * @qdepth: depth to set + * @reason: calling context * * Return value: * actual depth set **/ -static int ibmvfc_change_queue_depth(struct scsi_device *sdev, int qdepth) +static int ibmvfc_change_queue_depth(struct scsi_device *sdev, int qdepth, + int reason) { + if (reason != SCSI_QDEPTH_DEFAULT) + return -EOPNOTSUPP; + if (qdepth > IBMVFC_MAX_CMDS_PER_LUN) qdepth = IBMVFC_MAX_CMDS_PER_LUN; @@ -2434,14 +2960,6 @@ static ssize_t ibmvfc_show_host_partition_name(struct device *dev, vhost->login_buf->resp.partition_name); } -static struct device_attribute ibmvfc_host_partition_name = { - .attr = { - .name = "partition_name", - .mode = S_IRUGO, - }, - .show = ibmvfc_show_host_partition_name, -}; - static ssize_t ibmvfc_show_host_device_name(struct device *dev, struct device_attribute *attr, char *buf) { @@ -2452,14 +2970,6 @@ static ssize_t ibmvfc_show_host_device_name(struct device *dev, vhost->login_buf->resp.device_name); } -static struct device_attribute ibmvfc_host_device_name = { - .attr = { - .name = "device_name", - .mode = S_IRUGO, - }, - .show = ibmvfc_show_host_device_name, -}; - static ssize_t ibmvfc_show_host_loc_code(struct device *dev, struct device_attribute *attr, char *buf) { @@ -2470,14 +2980,6 @@ static ssize_t ibmvfc_show_host_loc_code(struct device *dev, vhost->login_buf->resp.port_loc_code); } -static struct device_attribute ibmvfc_host_loc_code = { - .attr = { - .name = "port_loc_code", - .mode = S_IRUGO, - }, - .show = ibmvfc_show_host_loc_code, -}; - static ssize_t ibmvfc_show_host_drc_name(struct device *dev, struct device_attribute *attr, char *buf) { @@ -2488,14 +2990,6 @@ static ssize_t ibmvfc_show_host_drc_name(struct device *dev, vhost->login_buf->resp.drc_name); } -static struct device_attribute ibmvfc_host_drc_name = { - .attr = { - .name = "drc_name", - .mode = S_IRUGO, - }, - .show = ibmvfc_show_host_drc_name, -}; - static ssize_t ibmvfc_show_host_npiv_version(struct device *dev, struct device_attribute *attr, char *buf) { @@ -2504,13 +2998,13 @@ static ssize_t ibmvfc_show_host_npiv_version(struct device *dev, return snprintf(buf, PAGE_SIZE, "%d\n", vhost->login_buf->resp.version); } -static struct device_attribute ibmvfc_host_npiv_version = { - .attr = { - .name = "npiv_version", - .mode = S_IRUGO, - }, - .show = ibmvfc_show_host_npiv_version, -}; +static ssize_t ibmvfc_show_host_capabilities(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct Scsi_Host *shost = class_to_shost(dev); + struct ibmvfc_host *vhost = shost_priv(shost); + return snprintf(buf, PAGE_SIZE, "%llx\n", vhost->login_buf->resp.capabilities); +} /** * ibmvfc_show_log_level - Show the adapter's error logging level @@ -2556,18 +3050,19 @@ static ssize_t ibmvfc_store_log_level(struct device *dev, return strlen(buf); } -static struct device_attribute ibmvfc_log_level_attr = { - .attr = { - .name = "log_level", - .mode = S_IRUGO | S_IWUSR, - }, - .show = ibmvfc_show_log_level, - .store = ibmvfc_store_log_level -}; +static DEVICE_ATTR(partition_name, S_IRUGO, ibmvfc_show_host_partition_name, NULL); +static DEVICE_ATTR(device_name, S_IRUGO, ibmvfc_show_host_device_name, NULL); +static DEVICE_ATTR(port_loc_code, S_IRUGO, ibmvfc_show_host_loc_code, NULL); +static DEVICE_ATTR(drc_name, S_IRUGO, ibmvfc_show_host_drc_name, NULL); +static DEVICE_ATTR(npiv_version, S_IRUGO, ibmvfc_show_host_npiv_version, NULL); +static DEVICE_ATTR(capabilities, S_IRUGO, ibmvfc_show_host_capabilities, NULL); +static DEVICE_ATTR(log_level, S_IRUGO | S_IWUSR, + ibmvfc_show_log_level, ibmvfc_store_log_level); #ifdef CONFIG_SCSI_IBMVFC_TRACE /** * ibmvfc_read_trace - Dump the adapter trace + * @filp: open sysfs file * @kobj: kobject struct * @bin_attr: bin_attribute struct * @buf: buffer @@ -2577,7 +3072,7 @@ static struct device_attribute ibmvfc_log_level_attr = { * Return value: * number of bytes printed to buffer **/ -static ssize_t ibmvfc_read_trace(struct kobject *kobj, +static ssize_t ibmvfc_read_trace(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { @@ -2612,12 +3107,13 @@ static struct bin_attribute ibmvfc_trace_attr = { #endif static struct device_attribute *ibmvfc_attrs[] = { - &ibmvfc_host_partition_name, - &ibmvfc_host_device_name, - &ibmvfc_host_loc_code, - &ibmvfc_host_drc_name, - &ibmvfc_host_npiv_version, - &ibmvfc_log_level_attr, + &dev_attr_partition_name, + &dev_attr_device_name, + &dev_attr_port_loc_code, + &dev_attr_drc_name, + &dev_attr_npiv_version, + &dev_attr_capabilities, + &dev_attr_log_level, NULL }; @@ -2661,6 +3157,7 @@ static struct ibmvfc_async_crq *ibmvfc_next_async_crq(struct ibmvfc_host *vhost) if (crq->valid & 0x80) { if (++async_crq->cur == async_crq->size) async_crq->cur = 0; + rmb(); } else crq = NULL; @@ -2683,6 +3180,7 @@ static struct ibmvfc_crq *ibmvfc_next_crq(struct ibmvfc_host *vhost) if (crq->valid & 0x80) { if (++queue->cur == queue->size) queue->cur = 0; + rmb(); } else crq = NULL; @@ -2727,27 +3225,31 @@ static void ibmvfc_tasklet(void *data) spin_lock_irqsave(vhost->host->host_lock, flags); while (!done) { - /* Pull all the valid messages off the CRQ */ - while ((crq = ibmvfc_next_crq(vhost)) != NULL) { - ibmvfc_handle_crq(crq, vhost); - crq->valid = 0; - } - /* Pull all the valid messages off the async CRQ */ while ((async = ibmvfc_next_async_crq(vhost)) != NULL) { ibmvfc_handle_async(async, vhost); async->valid = 0; + wmb(); } - vio_enable_interrupts(vdev); - if ((crq = ibmvfc_next_crq(vhost)) != NULL) { - vio_disable_interrupts(vdev); + /* Pull all the valid messages off the CRQ */ + while ((crq = ibmvfc_next_crq(vhost)) != NULL) { ibmvfc_handle_crq(crq, vhost); crq->valid = 0; - } else if ((async = ibmvfc_next_async_crq(vhost)) != NULL) { + wmb(); + } + + vio_enable_interrupts(vdev); + if ((async = ibmvfc_next_async_crq(vhost)) != NULL) { vio_disable_interrupts(vdev); ibmvfc_handle_async(async, vhost); async->valid = 0; + wmb(); + } else if ((crq = ibmvfc_next_crq(vhost)) != NULL) { + vio_disable_interrupts(vdev); + ibmvfc_handle_crq(crq, vhost); + crq->valid = 0; + wmb(); } else done = 1; } @@ -2774,15 +3276,19 @@ static void ibmvfc_init_tgt(struct ibmvfc_target *tgt, * @tgt: ibmvfc target struct * @job_step: initialization job step * + * Returns: 1 if step will be retried / 0 if not + * **/ -static void ibmvfc_retry_tgt_init(struct ibmvfc_target *tgt, +static int ibmvfc_retry_tgt_init(struct ibmvfc_target *tgt, void (*job_step) (struct ibmvfc_target *)) { if (++tgt->init_retries > IBMVFC_MAX_TGT_INIT_RETRIES) { ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT); wake_up(&tgt->vhost->work_wait_q); + return 0; } else ibmvfc_init_tgt(tgt, job_step); + return 1; } /* Defined in FC-LS */ @@ -2831,7 +3337,7 @@ static void ibmvfc_tgt_prli_done(struct ibmvfc_event *evt) struct ibmvfc_process_login *rsp = &evt->xfer_iu->prli; struct ibmvfc_prli_svc_parms *parms = &rsp->parms; u32 status = rsp->common.status; - int index; + int index, level = IBMVFC_DEFAULT_LOG_LEVEL; vhost->discovery_threads--; ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE); @@ -2850,7 +3356,7 @@ static void ibmvfc_tgt_prli_done(struct ibmvfc_event *evt) tgt->ids.roles |= FC_PORT_ROLE_FCP_TARGET; if (parms->service_parms & IBMVFC_PRLI_INITIATOR_FUNC) tgt->ids.roles |= FC_PORT_ROLE_FCP_INITIATOR; - ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_ADD_RPORT); + tgt->add_rport = 1; } else ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT); } else if (prli_rsp[index].retry) @@ -2867,13 +3373,18 @@ static void ibmvfc_tgt_prli_done(struct ibmvfc_event *evt) break; case IBMVFC_MAD_FAILED: default: - tgt_err(tgt, "Process Login failed: %s (%x:%x) rc=0x%02X\n", - ibmvfc_get_cmd_error(rsp->status, rsp->error), - rsp->status, rsp->error, status); - if (ibmvfc_retry_cmd(rsp->status, rsp->error)) - ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_send_prli); + if ((rsp->status & IBMVFC_VIOS_FAILURE) && rsp->error == IBMVFC_PLOGI_REQUIRED) + level += ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_send_plogi); + else if (tgt->logo_rcvd) + level += ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_send_plogi); + else if (ibmvfc_retry_cmd(rsp->status, rsp->error)) + level += ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_send_prli); else ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT); + + tgt_log(tgt, level, "Process Login failed: %s (%x:%x) rc=0x%02X\n", + ibmvfc_get_cmd_error(rsp->status, rsp->error), + rsp->status, rsp->error, status); break; }; @@ -2932,6 +3443,7 @@ static void ibmvfc_tgt_plogi_done(struct ibmvfc_event *evt) struct ibmvfc_host *vhost = evt->vhost; struct ibmvfc_port_login *rsp = &evt->xfer_iu->plogi; u32 status = rsp->common.status; + int level = IBMVFC_DEFAULT_LOG_LEVEL; vhost->discovery_threads--; ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE); @@ -2960,15 +3472,15 @@ static void ibmvfc_tgt_plogi_done(struct ibmvfc_event *evt) break; case IBMVFC_MAD_FAILED: default: - tgt_err(tgt, "Port Login failed: %s (%x:%x) %s (%x) %s (%x) rc=0x%02X\n", - ibmvfc_get_cmd_error(rsp->status, rsp->error), rsp->status, rsp->error, - ibmvfc_get_fc_type(rsp->fc_type), rsp->fc_type, - ibmvfc_get_ls_explain(rsp->fc_explain), rsp->fc_explain, status); - if (ibmvfc_retry_cmd(rsp->status, rsp->error)) - ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_send_plogi); + level += ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_send_plogi); else ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT); + + tgt_log(tgt, level, "Port Login failed: %s (%x:%x) %s (%x) %s (%x) rc=0x%02X\n", + ibmvfc_get_cmd_error(rsp->status, rsp->error), rsp->status, rsp->error, + ibmvfc_get_fc_type(rsp->fc_type), rsp->fc_type, + ibmvfc_get_ls_explain(rsp->fc_explain), rsp->fc_explain, status); break; }; @@ -2992,6 +3504,7 @@ static void ibmvfc_tgt_send_plogi(struct ibmvfc_target *tgt) return; kref_get(&tgt->kref); + tgt->logo_rcvd = 0; evt = ibmvfc_get_event(vhost); vhost->discovery_threads++; ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_INIT_WAIT); @@ -3129,13 +3642,13 @@ static void ibmvfc_tgt_adisc_done(struct ibmvfc_event *evt) case IBMVFC_MAD_SUCCESS: tgt_dbg(tgt, "ADISC succeeded\n"); if (ibmvfc_adisc_needs_plogi(mad, tgt)) - tgt->need_login = 1; + ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT); break; case IBMVFC_MAD_DRIVER_FAILED: break; case IBMVFC_MAD_FAILED: default: - tgt->need_login = 1; + ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT); fc_reason = (mad->fc_iu.response[1] & 0x00ff0000) >> 16; fc_explain = (mad->fc_iu.response[1] & 0x0000ff00) >> 8; tgt_info(tgt, "ADISC failed: %s (%x:%x) %s (%x) %s (%x) rc=0x%02X\n", @@ -3322,6 +3835,7 @@ static void ibmvfc_tgt_query_target_done(struct ibmvfc_event *evt) struct ibmvfc_host *vhost = evt->vhost; struct ibmvfc_query_tgt *rsp = &evt->xfer_iu->query_tgt; u32 status = rsp->common.status; + int level = IBMVFC_DEFAULT_LOG_LEVEL; vhost->discovery_threads--; ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE); @@ -3341,19 +3855,19 @@ static void ibmvfc_tgt_query_target_done(struct ibmvfc_event *evt) break; case IBMVFC_MAD_FAILED: default: - tgt_err(tgt, "Query Target failed: %s (%x:%x) %s (%x) %s (%x) rc=0x%02X\n", - ibmvfc_get_cmd_error(rsp->status, rsp->error), rsp->status, rsp->error, - ibmvfc_get_fc_type(rsp->fc_type), rsp->fc_type, - ibmvfc_get_gs_explain(rsp->fc_explain), rsp->fc_explain, status); - if ((rsp->status & IBMVFC_FABRIC_MAPPED) == IBMVFC_FABRIC_MAPPED && rsp->error == IBMVFC_UNABLE_TO_PERFORM_REQ && rsp->fc_explain == IBMVFC_PORT_NAME_NOT_REG) ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT); else if (ibmvfc_retry_cmd(rsp->status, rsp->error)) - ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_query_target); + level += ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_query_target); else ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT); + + tgt_log(tgt, level, "Query Target failed: %s (%x:%x) %s (%x) %s (%x) rc=0x%02X\n", + ibmvfc_get_cmd_error(rsp->status, rsp->error), rsp->status, rsp->error, + ibmvfc_get_fc_type(rsp->fc_type), rsp->fc_type, + ibmvfc_get_gs_explain(rsp->fc_explain), rsp->fc_explain, status); break; }; @@ -3420,7 +3934,7 @@ static int ibmvfc_alloc_target(struct ibmvfc_host *vhost, u64 scsi_id) } spin_unlock_irqrestore(vhost->host->host_lock, flags); - tgt = mempool_alloc(vhost->tgt_pool, GFP_KERNEL); + tgt = mempool_alloc(vhost->tgt_pool, GFP_NOIO); if (!tgt) { dev_err(vhost->dev, "Target allocation failure for scsi id %08llx\n", scsi_id); @@ -3472,6 +3986,7 @@ static void ibmvfc_discover_targets_done(struct ibmvfc_event *evt) struct ibmvfc_host *vhost = evt->vhost; struct ibmvfc_discover_targets *rsp = &evt->xfer_iu->discover_targets; u32 mad_status = rsp->common.status; + int level = IBMVFC_DEFAULT_LOG_LEVEL; switch (mad_status) { case IBMVFC_MAD_SUCCESS: @@ -3480,9 +3995,9 @@ static void ibmvfc_discover_targets_done(struct ibmvfc_event *evt) ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_ALLOC_TGTS); break; case IBMVFC_MAD_FAILED: - dev_err(vhost->dev, "Discover Targets failed: %s (%x:%x)\n", - ibmvfc_get_cmd_error(rsp->status, rsp->error), rsp->status, rsp->error); - ibmvfc_retry_host_init(vhost); + level += ibmvfc_retry_host_init(vhost); + ibmvfc_log(vhost, level, "Discover Targets failed: %s (%x:%x)\n", + ibmvfc_get_cmd_error(rsp->status, rsp->error), rsp->status, rsp->error); break; case IBMVFC_MAD_DRIVER_FAILED: break; @@ -3534,18 +4049,19 @@ static void ibmvfc_npiv_login_done(struct ibmvfc_event *evt) u32 mad_status = evt->xfer_iu->npiv_login.common.status; struct ibmvfc_npiv_login_resp *rsp = &vhost->login_buf->resp; unsigned int npiv_max_sectors; + int level = IBMVFC_DEFAULT_LOG_LEVEL; switch (mad_status) { case IBMVFC_MAD_SUCCESS: ibmvfc_free_event(evt); break; case IBMVFC_MAD_FAILED: - dev_err(vhost->dev, "NPIV Login failed: %s (%x:%x)\n", - ibmvfc_get_cmd_error(rsp->status, rsp->error), rsp->status, rsp->error); if (ibmvfc_retry_cmd(rsp->status, rsp->error)) - ibmvfc_retry_host_init(vhost); + level += ibmvfc_retry_host_init(vhost); else ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD); + ibmvfc_log(vhost, level, "NPIV Login failed: %s (%x:%x)\n", + ibmvfc_get_cmd_error(rsp->status, rsp->error), rsp->status, rsp->error); ibmvfc_free_event(evt); return; case IBMVFC_MAD_CRQ_ERROR: @@ -3578,6 +4094,7 @@ static void ibmvfc_npiv_login_done(struct ibmvfc_event *evt) return; } + vhost->logged_in = 1; npiv_max_sectors = min((uint)(rsp->max_dma_len >> 9), IBMVFC_MAX_SECTORS); dev_info(vhost->dev, "Host partition: %s, device: %s %s %s max sectors %u\n", rsp->partition_name, rsp->device_name, rsp->port_loc_code, @@ -3636,6 +4153,65 @@ static void ibmvfc_npiv_login(struct ibmvfc_host *vhost) }; /** + * ibmvfc_npiv_logout_done - Completion handler for NPIV Logout + * @vhost: ibmvfc host struct + * + **/ +static void ibmvfc_npiv_logout_done(struct ibmvfc_event *evt) +{ + struct ibmvfc_host *vhost = evt->vhost; + u32 mad_status = evt->xfer_iu->npiv_logout.common.status; + + ibmvfc_free_event(evt); + + switch (mad_status) { + case IBMVFC_MAD_SUCCESS: + if (list_empty(&vhost->sent) && + vhost->action == IBMVFC_HOST_ACTION_LOGO_WAIT) { + ibmvfc_init_host(vhost); + return; + } + break; + case IBMVFC_MAD_FAILED: + case IBMVFC_MAD_NOT_SUPPORTED: + case IBMVFC_MAD_CRQ_ERROR: + case IBMVFC_MAD_DRIVER_FAILED: + default: + ibmvfc_dbg(vhost, "NPIV Logout failed. 0x%X\n", mad_status); + break; + } + + ibmvfc_hard_reset_host(vhost); +} + +/** + * ibmvfc_npiv_logout - Issue an NPIV Logout + * @vhost: ibmvfc host struct + * + **/ +static void ibmvfc_npiv_logout(struct ibmvfc_host *vhost) +{ + struct ibmvfc_npiv_logout_mad *mad; + struct ibmvfc_event *evt; + + evt = ibmvfc_get_event(vhost); + ibmvfc_init_event(evt, ibmvfc_npiv_logout_done, IBMVFC_MAD_FORMAT); + + mad = &evt->iu.npiv_logout; + memset(mad, 0, sizeof(*mad)); + mad->common.version = 1; + mad->common.opcode = IBMVFC_NPIV_LOGOUT; + mad->common.length = sizeof(struct ibmvfc_npiv_logout_mad); + + ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_LOGO_WAIT); + + if (!ibmvfc_send_event(evt, vhost, default_timeout)) + ibmvfc_dbg(vhost, "Sent NPIV logout\n"); + else + ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD); +} + +/** * ibmvfc_dev_init_to_do - Is there target initialization work to do? * @vhost: ibmvfc host struct * @@ -3671,6 +4247,7 @@ static int __ibmvfc_work_to_do(struct ibmvfc_host *vhost) switch (vhost->action) { case IBMVFC_HOST_ACTION_NONE: case IBMVFC_HOST_ACTION_INIT_WAIT: + case IBMVFC_HOST_ACTION_LOGO_WAIT: return 0; case IBMVFC_HOST_ACTION_TGT_INIT: case IBMVFC_HOST_ACTION_QUERY_TGTS: @@ -3683,12 +4260,14 @@ static int __ibmvfc_work_to_do(struct ibmvfc_host *vhost) if (tgt->action == IBMVFC_TGT_ACTION_INIT_WAIT) return 0; return 1; + case IBMVFC_HOST_ACTION_LOGO: case IBMVFC_HOST_ACTION_INIT: case IBMVFC_HOST_ACTION_ALLOC_TGTS: - case IBMVFC_HOST_ACTION_TGT_ADD: case IBMVFC_HOST_ACTION_TGT_DEL: case IBMVFC_HOST_ACTION_TGT_DEL_FAILED: case IBMVFC_HOST_ACTION_QUERY: + case IBMVFC_HOST_ACTION_RESET: + case IBMVFC_HOST_ACTION_REENABLE: default: break; }; @@ -3740,25 +4319,30 @@ static void ibmvfc_log_ae(struct ibmvfc_host *vhost, int events) static void ibmvfc_tgt_add_rport(struct ibmvfc_target *tgt) { struct ibmvfc_host *vhost = tgt->vhost; - struct fc_rport *rport = tgt->rport; + struct fc_rport *rport; unsigned long flags; - if (rport) { - tgt_dbg(tgt, "Setting rport roles\n"); - fc_remote_port_rolechg(rport, tgt->ids.roles); - spin_lock_irqsave(vhost->host->host_lock, flags); - ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE); + tgt_dbg(tgt, "Adding rport\n"); + rport = fc_remote_port_add(vhost->host, 0, &tgt->ids); + spin_lock_irqsave(vhost->host->host_lock, flags); + + if (rport && tgt->action == IBMVFC_TGT_ACTION_DEL_RPORT) { + tgt_dbg(tgt, "Deleting rport\n"); + list_del(&tgt->queue); + ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DELETED_RPORT); + spin_unlock_irqrestore(vhost->host->host_lock, flags); + fc_remote_port_delete(rport); + del_timer_sync(&tgt->timer); + kref_put(&tgt->kref, ibmvfc_release_tgt); + return; + } else if (rport && tgt->action == IBMVFC_TGT_ACTION_DELETED_RPORT) { spin_unlock_irqrestore(vhost->host->host_lock, flags); return; } - tgt_dbg(tgt, "Adding rport\n"); - rport = fc_remote_port_add(vhost->host, 0, &tgt->ids); - spin_lock_irqsave(vhost->host->host_lock, flags); - tgt->rport = rport; - ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE); if (rport) { tgt_dbg(tgt, "rport add succeeded\n"); + tgt->rport = rport; rport->maxframe_size = tgt->service_parms.common.bb_rcv_sz & 0x0fff; rport->supported_classes = 0; tgt->target_id = rport->scsi_target_id; @@ -3768,6 +4352,8 @@ static void ibmvfc_tgt_add_rport(struct ibmvfc_target *tgt) rport->supported_classes |= FC_COS_CLASS2; if (tgt->service_parms.class3_parms[0] & 0x80000000) rport->supported_classes |= FC_COS_CLASS3; + if (rport->rqst_q) + blk_queue_max_segments(rport->rqst_q, 1); } else tgt_dbg(tgt, "rport add failed\n"); spin_unlock_irqrestore(vhost->host->host_lock, flags); @@ -3783,14 +4369,42 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost) struct ibmvfc_target *tgt; unsigned long flags; struct fc_rport *rport; + int rc; ibmvfc_log_ae(vhost, vhost->events_to_log); spin_lock_irqsave(vhost->host->host_lock, flags); vhost->events_to_log = 0; switch (vhost->action) { case IBMVFC_HOST_ACTION_NONE: + case IBMVFC_HOST_ACTION_LOGO_WAIT: case IBMVFC_HOST_ACTION_INIT_WAIT: break; + case IBMVFC_HOST_ACTION_RESET: + vhost->action = IBMVFC_HOST_ACTION_TGT_DEL; + spin_unlock_irqrestore(vhost->host->host_lock, flags); + rc = ibmvfc_reset_crq(vhost); + spin_lock_irqsave(vhost->host->host_lock, flags); + if (rc == H_CLOSED) + vio_enable_interrupts(to_vio_dev(vhost->dev)); + if (rc || (rc = ibmvfc_send_crq_init(vhost)) || + (rc = vio_enable_interrupts(to_vio_dev(vhost->dev)))) { + ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD); + dev_err(vhost->dev, "Error after reset (rc=%d)\n", rc); + } + break; + case IBMVFC_HOST_ACTION_REENABLE: + vhost->action = IBMVFC_HOST_ACTION_TGT_DEL; + spin_unlock_irqrestore(vhost->host->host_lock, flags); + rc = ibmvfc_reenable_crq_queue(vhost); + spin_lock_irqsave(vhost->host->host_lock, flags); + if (rc || (rc = ibmvfc_send_crq_init(vhost))) { + ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD); + dev_err(vhost->dev, "Error after enable (rc=%d)\n", rc); + } + break; + case IBMVFC_HOST_ACTION_LOGO: + vhost->job_step(vhost); + break; case IBMVFC_HOST_ACTION_INIT: BUG_ON(vhost->state != IBMVFC_INITIALIZING); if (vhost->delay_init) { @@ -3825,6 +4439,7 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost) rport = tgt->rport; tgt->rport = NULL; list_del(&tgt->queue); + ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DELETED_RPORT); spin_unlock_irqrestore(vhost->host->host_lock, flags); if (rport) fc_remote_port_delete(rport); @@ -3836,11 +4451,21 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost) if (vhost->state == IBMVFC_INITIALIZING) { if (vhost->action == IBMVFC_HOST_ACTION_TGT_DEL_FAILED) { - ibmvfc_set_host_state(vhost, IBMVFC_ACTIVE); - ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_TGT_ADD); - vhost->init_retries = 0; - spin_unlock_irqrestore(vhost->host->host_lock, flags); - scsi_unblock_requests(vhost->host); + if (vhost->reinit) { + vhost->reinit = 0; + scsi_block_requests(vhost->host); + ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_QUERY); + spin_unlock_irqrestore(vhost->host->host_lock, flags); + } else { + ibmvfc_set_host_state(vhost, IBMVFC_ACTIVE); + ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_NONE); + wake_up(&vhost->init_wait_q); + schedule_work(&vhost->rport_add_work_q); + vhost->init_retries = 0; + spin_unlock_irqrestore(vhost->host->host_lock, flags); + scsi_unblock_requests(vhost->host); + } + return; } else { ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_INIT); @@ -3871,24 +4496,6 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost) if (!ibmvfc_dev_init_to_do(vhost)) ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_TGT_DEL_FAILED); break; - case IBMVFC_HOST_ACTION_TGT_ADD: - list_for_each_entry(tgt, &vhost->targets, queue) { - if (tgt->action == IBMVFC_TGT_ACTION_ADD_RPORT) { - spin_unlock_irqrestore(vhost->host->host_lock, flags); - ibmvfc_tgt_add_rport(tgt); - return; - } - } - - if (vhost->reinit && !ibmvfc_set_host_state(vhost, IBMVFC_INITIALIZING)) { - vhost->reinit = 0; - scsi_block_requests(vhost->host); - ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_QUERY); - } else { - ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_NONE); - wake_up(&vhost->init_wait_q); - } - break; default: break; }; @@ -3908,7 +4515,7 @@ static int ibmvfc_work(void *data) struct ibmvfc_host *vhost = data; int rc; - set_user_nice(current, -20); + set_user_nice(current, MIN_NICE); while (1) { rc = wait_event_interruptible(vhost->work_wait_q, @@ -4086,7 +4693,7 @@ static int ibmvfc_alloc_mem(struct ibmvfc_host *vhost) if (!vhost->trace) goto free_disc_buffer; - vhost->tgt_pool = mempool_create_kzalloc_pool(IBMVFC_TGT_MEMPOOL_SZ, + vhost->tgt_pool = mempool_create_kmalloc_pool(IBMVFC_TGT_MEMPOOL_SZ, sizeof(struct ibmvfc_target)); if (!vhost->tgt_pool) { @@ -4118,6 +4725,56 @@ nomem: } /** + * ibmvfc_rport_add_thread - Worker thread for rport adds + * @work: work struct + * + **/ +static void ibmvfc_rport_add_thread(struct work_struct *work) +{ + struct ibmvfc_host *vhost = container_of(work, struct ibmvfc_host, + rport_add_work_q); + struct ibmvfc_target *tgt; + struct fc_rport *rport; + unsigned long flags; + int did_work; + + ENTER; + spin_lock_irqsave(vhost->host->host_lock, flags); + do { + did_work = 0; + if (vhost->state != IBMVFC_ACTIVE) + break; + + list_for_each_entry(tgt, &vhost->targets, queue) { + if (tgt->add_rport) { + did_work = 1; + tgt->add_rport = 0; + kref_get(&tgt->kref); + rport = tgt->rport; + if (!rport) { + spin_unlock_irqrestore(vhost->host->host_lock, flags); + ibmvfc_tgt_add_rport(tgt); + } else if (get_device(&rport->dev)) { + spin_unlock_irqrestore(vhost->host->host_lock, flags); + tgt_dbg(tgt, "Setting rport roles\n"); + fc_remote_port_rolechg(rport, tgt->ids.roles); + put_device(&rport->dev); + } + + kref_put(&tgt->kref, ibmvfc_release_tgt); + spin_lock_irqsave(vhost->host->host_lock, flags); + break; + } + } + } while(did_work); + + if (vhost->state == IBMVFC_ACTIVE) + vhost->scan_complete = 1; + spin_unlock_irqrestore(vhost->host->host_lock, flags); + LEAVE; +} + +/** * ibmvfc_probe - Adapter hot plug add entry point * @vdev: vio device struct * @id: vio device id struct @@ -4160,6 +4817,8 @@ static int ibmvfc_probe(struct vio_dev *vdev, const struct vio_device_id *id) strcpy(vhost->partition_name, "UNKNOWN"); init_waitqueue_head(&vhost->work_wait_q); init_waitqueue_head(&vhost->init_wait_q); + INIT_WORK(&vhost->rport_add_work_q, ibmvfc_rport_add_thread); + mutex_init(&vhost->passthru_mutex); if ((rc = ibmvfc_alloc_mem(vhost))) goto free_scsi_host; @@ -4186,12 +4845,16 @@ static int ibmvfc_probe(struct vio_dev *vdev, const struct vio_device_id *id) if ((rc = scsi_add_host(shost, dev))) goto release_event_pool; + fc_host_dev_loss_tmo(shost) = IBMVFC_DEV_LOSS_TMO; + if ((rc = ibmvfc_create_trace_file(&shost->shost_dev.kobj, &ibmvfc_trace_attr))) { dev_err(dev, "Failed to create trace file. rc=%d\n", rc); goto remove_shost; } + if (shost_to_fc_host(shost)->rqst_q) + blk_queue_max_segments(shost_to_fc_host(shost)->rqst_q, 1); dev_set_drvdata(dev, vhost); spin_lock(&ibmvfc_driver_lock); list_add_tail(&vhost->queue, &ibmvfc_head); @@ -4232,7 +4895,11 @@ static int ibmvfc_remove(struct vio_dev *vdev) ENTER; ibmvfc_remove_trace_file(&vhost->host->shost_dev.kobj, &ibmvfc_trace_attr); + + spin_lock_irqsave(vhost->host->host_lock, flags); ibmvfc_link_down(vhost, IBMVFC_HOST_OFFLINE); + spin_unlock_irqrestore(vhost->host->host_lock, flags); + ibmvfc_wait_while_resetting(vhost); ibmvfc_release_crq_queue(vhost); kthread_stop(vhost->work_thread); @@ -4254,6 +4921,27 @@ static int ibmvfc_remove(struct vio_dev *vdev) } /** + * ibmvfc_resume - Resume from suspend + * @dev: device struct + * + * We may have lost an interrupt across suspend/resume, so kick the + * interrupt handler + * + */ +static int ibmvfc_resume(struct device *dev) +{ + unsigned long flags; + struct ibmvfc_host *vhost = dev_get_drvdata(dev); + struct vio_dev *vdev = to_vio_dev(dev); + + spin_lock_irqsave(vhost->host->host_lock, flags); + vio_disable_interrupts(vdev); + tasklet_schedule(&vhost->tasklet); + spin_unlock_irqrestore(vhost->host->host_lock, flags); + return 0; +} + +/** * ibmvfc_get_desired_dma - Calculate DMA resources needed by the driver * @vdev: vio device struct * @@ -4267,21 +4955,23 @@ static unsigned long ibmvfc_get_desired_dma(struct vio_dev *vdev) return pool_dma + ((512 * 1024) * driver_template.cmd_per_lun); } -static struct vio_device_id ibmvfc_device_table[] __devinitdata = { +static struct vio_device_id ibmvfc_device_table[] = { {"fcp", "IBM,vfc-client"}, { "", "" } }; MODULE_DEVICE_TABLE(vio, ibmvfc_device_table); +static struct dev_pm_ops ibmvfc_pm_ops = { + .resume = ibmvfc_resume +}; + static struct vio_driver ibmvfc_driver = { .id_table = ibmvfc_device_table, .probe = ibmvfc_probe, .remove = ibmvfc_remove, .get_desired_dma = ibmvfc_get_desired_dma, - .driver = { - .name = IBMVFC_NAME, - .owner = THIS_MODULE, - } + .name = IBMVFC_NAME, + .pm = &ibmvfc_pm_ops, }; static struct fc_function_template ibmvfc_transport_functions = { @@ -4316,6 +5006,9 @@ static struct fc_function_template ibmvfc_transport_functions = { .get_starget_port_id = ibmvfc_get_starget_port_id, .show_starget_port_id = 1, + + .bsg_request = ibmvfc_bsg_request, + .bsg_timeout = ibmvfc_bsg_timeout, }; /** diff --git a/drivers/scsi/ibmvscsi/ibmvfc.h b/drivers/scsi/ibmvscsi/ibmvfc.h index ca1dcf7a756..017a5290e8c 100644 --- a/drivers/scsi/ibmvscsi/ibmvfc.h +++ b/drivers/scsi/ibmvscsi/ibmvfc.h @@ -29,8 +29,8 @@ #include "viosrp.h" #define IBMVFC_NAME "ibmvfc" -#define IBMVFC_DRIVER_VERSION "1.0.5" -#define IBMVFC_DRIVER_DATE "(March 19, 2009)" +#define IBMVFC_DRIVER_VERSION "1.0.11" +#define IBMVFC_DRIVER_DATE "(April 12, 2013)" #define IBMVFC_DEFAULT_TIMEOUT 60 #define IBMVFC_ADISC_CANCEL_TIMEOUT 45 @@ -38,6 +38,8 @@ #define IBMVFC_ADISC_PLUS_CANCEL_TIMEOUT \ (IBMVFC_ADISC_TIMEOUT + IBMVFC_ADISC_CANCEL_TIMEOUT) #define IBMVFC_INIT_TIMEOUT 120 +#define IBMVFC_ABORT_TIMEOUT 8 +#define IBMVFC_ABORT_WAIT_TIMEOUT 40 #define IBMVFC_MAX_REQUESTS_DEFAULT 100 #define IBMVFC_DEBUG 0 @@ -57,9 +59,11 @@ * Ensure we have resources for ERP and initialization: * 1 for ERP * 1 for initialization + * 1 for NPIV Logout + * 2 for BSG passthru * 2 for each discovery thread */ -#define IBMVFC_NUM_INTERNAL_REQ (1 + 1 + (disc_threads * 2)) +#define IBMVFC_NUM_INTERNAL_REQ (1 + 1 + 1 + 2 + (disc_threads * 2)) #define IBMVFC_MAD_SUCCESS 0x00 #define IBMVFC_MAD_NOT_SUPPORTED 0xF1 @@ -127,6 +131,7 @@ enum ibmvfc_mad_types { IBMVFC_IMPLICIT_LOGOUT = 0x0040, IBMVFC_PASSTHRU = 0x0200, IBMVFC_TMF_MAD = 0x0100, + IBMVFC_NPIV_LOGOUT = 0x0800, }; struct ibmvfc_mad_common { @@ -143,6 +148,10 @@ struct ibmvfc_npiv_login_mad { struct srp_direct_buf buffer; }__attribute__((packed, aligned (8))); +struct ibmvfc_npiv_logout_mad { + struct ibmvfc_mad_common common; +}__attribute__((packed, aligned (8))); + #define IBMVFC_MAX_NAME 256 struct ibmvfc_npiv_login { @@ -199,9 +208,10 @@ struct ibmvfc_npiv_login_resp { u16 error; u32 flags; #define IBMVFC_NATIVE_FC 0x01 -#define IBMVFC_CAN_FLUSH_ON_HALT 0x08 u32 reserved; - u64 capabilites; + u64 capabilities; +#define IBMVFC_CAN_FLUSH_ON_HALT 0x08 +#define IBMVFC_CAN_SUPPRESS_ABTS 0x10 u32 max_cmds; u32 scsi_id_sz; u64 max_dma_len; @@ -341,6 +351,7 @@ struct ibmvfc_tmf { #define IBMVFC_TMF_LUN_RESET 0x10 #define IBMVFC_TMF_TGT_RESET 0x20 #define IBMVFC_TMF_LUA_VALID 0x40 +#define IBMVFC_TMF_SUPPRESS_ABTS 0x80 u32 cancel_key; u32 my_cancel_key; u32 pad; @@ -459,7 +470,10 @@ struct ibmvfc_passthru_iu { u16 error; u32 flags; #define IBMVFC_FC_ELS 0x01 +#define IBMVFC_FC_CT_IU 0x02 u32 cancel_key; +#define IBMVFC_PASSTHRU_CANCEL_KEY 0x80000000 +#define IBMVFC_INTERNAL_CANCEL_KEY 0x80000001 u32 reserved; struct srp_direct_buf cmd; struct srp_direct_buf rsp; @@ -528,6 +542,12 @@ enum ibmvfc_async_event { IBMVFC_AE_ADAPTER_FAILED = 0x1000, }; +struct ibmvfc_async_desc { + const char *desc; + enum ibmvfc_async_event ae; + int log_level; +}; + struct ibmvfc_crq { volatile u8 valid; volatile u8 format; @@ -541,9 +561,17 @@ struct ibmvfc_crq_queue { dma_addr_t msg_token; }; +enum ibmvfc_ae_link_state { + IBMVFC_AE_LS_LINK_UP = 0x01, + IBMVFC_AE_LS_LINK_BOUNCED = 0x02, + IBMVFC_AE_LS_LINK_DOWN = 0x04, + IBMVFC_AE_LS_LINK_DEAD = 0x08, +}; + struct ibmvfc_async_crq { volatile u8 valid; - u8 pad[3]; + u8 link_state; + u8 pad[2]; u32 pad2; volatile u64 event; volatile u64 scsi_id; @@ -561,6 +589,7 @@ struct ibmvfc_async_crq_queue { union ibmvfc_iu { struct ibmvfc_mad_common mad_common; struct ibmvfc_npiv_login_mad npiv_login; + struct ibmvfc_npiv_logout_mad npiv_logout; struct ibmvfc_discover_targets discover_targets; struct ibmvfc_port_login plogi; struct ibmvfc_process_login prli; @@ -575,8 +604,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, + IBMVFC_TGT_ACTION_DELETED_RPORT, }; struct ibmvfc_target { @@ -588,7 +617,9 @@ struct ibmvfc_target { int target_id; enum ibmvfc_target_action action; int need_login; + int add_rport; int init_retries; + int logo_rcvd; u32 cancel_key; struct ibmvfc_service_parms service_parms; struct ibmvfc_service_parms service_parms_change; @@ -627,6 +658,10 @@ struct ibmvfc_event_pool { enum ibmvfc_host_action { IBMVFC_HOST_ACTION_NONE = 0, + IBMVFC_HOST_ACTION_RESET, + IBMVFC_HOST_ACTION_REENABLE, + IBMVFC_HOST_ACTION_LOGO, + IBMVFC_HOST_ACTION_LOGO_WAIT, IBMVFC_HOST_ACTION_INIT, IBMVFC_HOST_ACTION_INIT_WAIT, IBMVFC_HOST_ACTION_QUERY, @@ -635,7 +670,6 @@ enum ibmvfc_host_action { IBMVFC_HOST_ACTION_ALLOC_TGTS, IBMVFC_HOST_ACTION_TGT_INIT, IBMVFC_HOST_ACTION_TGT_DEL_FAILED, - IBMVFC_HOST_ACTION_TGT_ADD, }; enum ibmvfc_host_state { @@ -675,6 +709,7 @@ struct ibmvfc_host { int disc_buf_sz; int log_level; struct ibmvfc_discover_targets_buf *disc_buf; + struct mutex passthru_mutex; int task_set; int init_retries; int discovery_threads; @@ -682,6 +717,9 @@ struct ibmvfc_host { int client_migrated; int reinit; int delay_init; + int scan_complete; + int logged_in; + int aborting_passthru; int events_to_log; #define IBMVFC_AE_LINKUP 0x0001 #define IBMVFC_AE_LINKDOWN 0x0002 @@ -692,6 +730,7 @@ struct ibmvfc_host { void (*job_step) (struct ibmvfc_host *); struct task_struct *work_thread; struct tasklet_struct tasklet; + struct work_struct rport_add_work_q; wait_queue_head_t init_wait_q; wait_queue_head_t work_wait_q; }; @@ -707,6 +746,12 @@ struct ibmvfc_host { #define tgt_err(t, fmt, ...) \ dev_err((t)->vhost->dev, "%llX: " fmt, (t)->scsi_id, ##__VA_ARGS__) +#define tgt_log(t, level, fmt, ...) \ + do { \ + if ((t)->vhost->log_level >= level) \ + tgt_err(t, fmt, ##__VA_ARGS__); \ + } while (0) + #define ibmvfc_dbg(vhost, ...) \ DBG_CMD(dev_info((vhost)->dev, ##__VA_ARGS__)) diff --git a/drivers/scsi/ibmvscsi/ibmvscsi.c b/drivers/scsi/ibmvscsi/ibmvscsi.c index c9aa7611e40..7b23f21f22f 100644 --- a/drivers/scsi/ibmvscsi/ibmvscsi.c +++ b/drivers/scsi/ibmvscsi/ibmvscsi.c @@ -40,7 +40,7 @@ * (CRQ), which is just a buffer of 16 byte entries in the receiver's * Senders cannot access the buffer directly, but send messages by * making a hypervisor call and passing in the 16 bytes. The hypervisor - * puts the message in the next 16 byte space in round-robbin fashion, + * puts the message in the next 16 byte space in round-robin fashion, * turns on the high order bit of the message (the valid bit), and * generates an interrupt to the receiver (if interrupts are turned on.) * The receiver just turns off the valid bit when they have copied out @@ -55,13 +55,7 @@ * and sends a CRQ message back to inform the client that the request has * completed. * - * Note that some of the underlying infrastructure is different between - * machines conforming to the "RS/6000 Platform Architecture" (RPA) and - * the older iSeries hypervisor models. To support both, some low level - * routines have been broken out into rpa_vscsi.c and iseries_vscsi.c. - * The Makefile should pick one, not two, not zero, of these. - * - * TODO: This is currently pretty tied to the IBM i/pSeries hypervisor + * TODO: This is currently pretty tied to the IBM pSeries hypervisor * interfaces. It would be really nice to abstract this above an RDMA * layer. */ @@ -70,9 +64,12 @@ #include <linux/moduleparam.h> #include <linux/dma-mapping.h> #include <linux/delay.h> +#include <linux/slab.h> +#include <linux/of.h> +#include <linux/pm.h> +#include <linux/kthread.h> #include <asm/firmware.h> #include <asm/vio.h> -#include <asm/firmware.h> #include <scsi/scsi.h> #include <scsi/scsi_cmnd.h> #include <scsi/scsi_host.h> @@ -87,15 +84,21 @@ */ static int max_id = 64; static int max_channel = 3; -static int init_timeout = 5; +static int init_timeout = 300; +static int login_timeout = 60; +static int info_timeout = 30; +static int abort_timeout = 60; +static int reset_timeout = 60; static int max_requests = IBMVSCSI_MAX_REQUESTS_DEFAULT; static int max_events = IBMVSCSI_MAX_REQUESTS_DEFAULT + 2; +static int fast_fail = 1; +static int client_reserve = 1; +static char partition_name[97] = "UNKNOWN"; +static unsigned int partition_number = -1; static struct scsi_transport_template *ibmvscsi_transport_template; -#define IBMVSCSI_VERSION "1.5.8" - -static struct ibmvscsi_ops *ibmvscsi_ops; +#define IBMVSCSI_VERSION "1.5.9" MODULE_DESCRIPTION("IBM Virtual SCSI"); MODULE_AUTHOR("Dave Boutcher"); @@ -110,6 +113,331 @@ module_param_named(init_timeout, init_timeout, int, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(init_timeout, "Initialization timeout in seconds"); module_param_named(max_requests, max_requests, int, S_IRUGO); MODULE_PARM_DESC(max_requests, "Maximum requests for this adapter"); +module_param_named(fast_fail, fast_fail, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(fast_fail, "Enable fast fail. [Default=1]"); +module_param_named(client_reserve, client_reserve, int, S_IRUGO ); +MODULE_PARM_DESC(client_reserve, "Attempt client managed reserve/release"); + +static void ibmvscsi_handle_crq(struct viosrp_crq *crq, + struct ibmvscsi_host_data *hostdata); + +/* ------------------------------------------------------------ + * Routines for managing the command/response queue + */ +/** + * ibmvscsi_handle_event: - Interrupt handler for crq events + * @irq: number of irq to handle, not used + * @dev_instance: ibmvscsi_host_data of host that received interrupt + * + * Disables interrupts and schedules srp_task + * Always returns IRQ_HANDLED + */ +static irqreturn_t ibmvscsi_handle_event(int irq, void *dev_instance) +{ + struct ibmvscsi_host_data *hostdata = + (struct ibmvscsi_host_data *)dev_instance; + vio_disable_interrupts(to_vio_dev(hostdata->dev)); + tasklet_schedule(&hostdata->srp_task); + return IRQ_HANDLED; +} + +/** + * release_crq_queue: - Deallocates data and unregisters CRQ + * @queue: crq_queue to initialize and register + * @host_data: ibmvscsi_host_data of host + * + * Frees irq, deallocates a page for messages, unmaps dma, and unregisters + * the crq with the hypervisor. + */ +static void ibmvscsi_release_crq_queue(struct crq_queue *queue, + struct ibmvscsi_host_data *hostdata, + int max_requests) +{ + long rc = 0; + struct vio_dev *vdev = to_vio_dev(hostdata->dev); + free_irq(vdev->irq, (void *)hostdata); + tasklet_kill(&hostdata->srp_task); + do { + if (rc) + msleep(100); + rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address); + } while ((rc == H_BUSY) || (H_IS_LONG_BUSY(rc))); + dma_unmap_single(hostdata->dev, + queue->msg_token, + queue->size * sizeof(*queue->msgs), DMA_BIDIRECTIONAL); + free_page((unsigned long)queue->msgs); +} + +/** + * crq_queue_next_crq: - Returns the next entry in message queue + * @queue: crq_queue to use + * + * Returns pointer to next entry in queue, or NULL if there are no new + * entried in the CRQ. + */ +static struct viosrp_crq *crq_queue_next_crq(struct crq_queue *queue) +{ + struct viosrp_crq *crq; + unsigned long flags; + + spin_lock_irqsave(&queue->lock, flags); + crq = &queue->msgs[queue->cur]; + if (crq->valid & 0x80) { + if (++queue->cur == queue->size) + queue->cur = 0; + + /* Ensure the read of the valid bit occurs before reading any + * other bits of the CRQ entry + */ + rmb(); + } else + crq = NULL; + spin_unlock_irqrestore(&queue->lock, flags); + + return crq; +} + +/** + * ibmvscsi_send_crq: - Send a CRQ + * @hostdata: the adapter + * @word1: the first 64 bits of the data + * @word2: the second 64 bits of the data + */ +static int ibmvscsi_send_crq(struct ibmvscsi_host_data *hostdata, + u64 word1, u64 word2) +{ + struct vio_dev *vdev = to_vio_dev(hostdata->dev); + + /* + * Ensure the command buffer is flushed to memory before handing it + * over to the VIOS to prevent it from fetching any stale data. + */ + mb(); + return plpar_hcall_norets(H_SEND_CRQ, vdev->unit_address, word1, word2); +} + +/** + * ibmvscsi_task: - Process srps asynchronously + * @data: ibmvscsi_host_data of host + */ +static void ibmvscsi_task(void *data) +{ + struct ibmvscsi_host_data *hostdata = (struct ibmvscsi_host_data *)data; + struct vio_dev *vdev = to_vio_dev(hostdata->dev); + struct viosrp_crq *crq; + int done = 0; + + while (!done) { + /* Pull all the valid messages off the CRQ */ + while ((crq = crq_queue_next_crq(&hostdata->queue)) != NULL) { + ibmvscsi_handle_crq(crq, hostdata); + crq->valid = 0x00; + } + + vio_enable_interrupts(vdev); + crq = crq_queue_next_crq(&hostdata->queue); + if (crq != NULL) { + vio_disable_interrupts(vdev); + ibmvscsi_handle_crq(crq, hostdata); + crq->valid = 0x00; + } else { + done = 1; + } + } +} + +static void gather_partition_info(void) +{ + struct device_node *rootdn; + + const char *ppartition_name; + const __be32 *p_number_ptr; + + /* Retrieve information about this partition */ + rootdn = of_find_node_by_path("/"); + if (!rootdn) { + return; + } + + ppartition_name = of_get_property(rootdn, "ibm,partition-name", NULL); + if (ppartition_name) + strncpy(partition_name, ppartition_name, + sizeof(partition_name)); + p_number_ptr = of_get_property(rootdn, "ibm,partition-no", NULL); + if (p_number_ptr) + partition_number = of_read_number(p_number_ptr, 1); + of_node_put(rootdn); +} + +static void set_adapter_info(struct ibmvscsi_host_data *hostdata) +{ + memset(&hostdata->madapter_info, 0x00, + sizeof(hostdata->madapter_info)); + + dev_info(hostdata->dev, "SRP_VERSION: %s\n", SRP_VERSION); + strcpy(hostdata->madapter_info.srp_version, SRP_VERSION); + + strncpy(hostdata->madapter_info.partition_name, partition_name, + sizeof(hostdata->madapter_info.partition_name)); + + hostdata->madapter_info.partition_number = + cpu_to_be32(partition_number); + + hostdata->madapter_info.mad_version = cpu_to_be32(1); + hostdata->madapter_info.os_type = cpu_to_be32(2); +} + +/** + * reset_crq_queue: - resets a crq after a failure + * @queue: crq_queue to initialize and register + * @hostdata: ibmvscsi_host_data of host + * + */ +static int ibmvscsi_reset_crq_queue(struct crq_queue *queue, + struct ibmvscsi_host_data *hostdata) +{ + int rc = 0; + struct vio_dev *vdev = to_vio_dev(hostdata->dev); + + /* Close the CRQ */ + do { + if (rc) + msleep(100); + rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address); + } while ((rc == H_BUSY) || (H_IS_LONG_BUSY(rc))); + + /* Clean out the queue */ + memset(queue->msgs, 0x00, PAGE_SIZE); + queue->cur = 0; + + set_adapter_info(hostdata); + + /* And re-open it again */ + rc = plpar_hcall_norets(H_REG_CRQ, + vdev->unit_address, + queue->msg_token, PAGE_SIZE); + if (rc == 2) { + /* Adapter is good, but other end is not ready */ + dev_warn(hostdata->dev, "Partner adapter not ready\n"); + } else if (rc != 0) { + dev_warn(hostdata->dev, "couldn't register crq--rc 0x%x\n", rc); + } + return rc; +} + +/** + * initialize_crq_queue: - Initializes and registers CRQ with hypervisor + * @queue: crq_queue to initialize and register + * @hostdata: ibmvscsi_host_data of host + * + * Allocates a page for messages, maps it for dma, and registers + * the crq with the hypervisor. + * Returns zero on success. + */ +static int ibmvscsi_init_crq_queue(struct crq_queue *queue, + struct ibmvscsi_host_data *hostdata, + int max_requests) +{ + int rc; + int retrc; + struct vio_dev *vdev = to_vio_dev(hostdata->dev); + + queue->msgs = (struct viosrp_crq *)get_zeroed_page(GFP_KERNEL); + + if (!queue->msgs) + goto malloc_failed; + queue->size = PAGE_SIZE / sizeof(*queue->msgs); + + queue->msg_token = dma_map_single(hostdata->dev, queue->msgs, + queue->size * sizeof(*queue->msgs), + DMA_BIDIRECTIONAL); + + if (dma_mapping_error(hostdata->dev, queue->msg_token)) + goto map_failed; + + gather_partition_info(); + set_adapter_info(hostdata); + + retrc = rc = plpar_hcall_norets(H_REG_CRQ, + vdev->unit_address, + queue->msg_token, PAGE_SIZE); + if (rc == H_RESOURCE) + /* maybe kexecing and resource is busy. try a reset */ + rc = ibmvscsi_reset_crq_queue(queue, + hostdata); + + if (rc == 2) { + /* Adapter is good, but other end is not ready */ + dev_warn(hostdata->dev, "Partner adapter not ready\n"); + retrc = 0; + } else if (rc != 0) { + dev_warn(hostdata->dev, "Error %d opening adapter\n", rc); + goto reg_crq_failed; + } + + queue->cur = 0; + spin_lock_init(&queue->lock); + + tasklet_init(&hostdata->srp_task, (void *)ibmvscsi_task, + (unsigned long)hostdata); + + if (request_irq(vdev->irq, + ibmvscsi_handle_event, + 0, "ibmvscsi", (void *)hostdata) != 0) { + dev_err(hostdata->dev, "couldn't register irq 0x%x\n", + vdev->irq); + goto req_irq_failed; + } + + rc = vio_enable_interrupts(vdev); + if (rc != 0) { + dev_err(hostdata->dev, "Error %d enabling interrupts!!!\n", rc); + goto req_irq_failed; + } + + return retrc; + + req_irq_failed: + tasklet_kill(&hostdata->srp_task); + rc = 0; + do { + if (rc) + msleep(100); + rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address); + } while ((rc == H_BUSY) || (H_IS_LONG_BUSY(rc))); + reg_crq_failed: + dma_unmap_single(hostdata->dev, + queue->msg_token, + queue->size * sizeof(*queue->msgs), DMA_BIDIRECTIONAL); + map_failed: + free_page((unsigned long)queue->msgs); + malloc_failed: + return -1; +} + +/** + * reenable_crq_queue: - reenables a crq after + * @queue: crq_queue to initialize and register + * @hostdata: ibmvscsi_host_data of host + * + */ +static int ibmvscsi_reenable_crq_queue(struct crq_queue *queue, + struct ibmvscsi_host_data *hostdata) +{ + int rc = 0; + struct vio_dev *vdev = to_vio_dev(hostdata->dev); + + /* Re-enable the CRQ */ + do { + if (rc) + msleep(100); + rc = plpar_hcall_norets(H_ENABLE_CRQ, vdev->unit_address); + } while ((rc == H_IN_PROGRESS) || (rc == H_BUSY) || (H_IS_LONG_BUSY(rc))); + + if (rc) + dev_err(hostdata->dev, "Error %d enabling adapter\n", rc); + return rc; +} /* ------------------------------------------------------------ * Routines for the event pool and event structs @@ -147,9 +475,9 @@ static int initialize_event_pool(struct event_pool *pool, memset(&evt->crq, 0x00, sizeof(evt->crq)); atomic_set(&evt->free, 1); evt->crq.valid = 0x80; - evt->crq.IU_length = sizeof(*evt->xfer_iu); - evt->crq.IU_data_ptr = pool->iu_token + - sizeof(*evt->xfer_iu) * i; + evt->crq.IU_length = cpu_to_be16(sizeof(*evt->xfer_iu)); + evt->crq.IU_data_ptr = cpu_to_be64(pool->iu_token + + sizeof(*evt->xfer_iu) * i); evt->xfer_iu = pool->iu_storage + i; evt->hostdata = hostdata; evt->ext_list = NULL; @@ -271,7 +599,7 @@ static void init_event_struct(struct srp_event_struct *evt_struct, evt_struct->cmnd_done = NULL; evt_struct->sync_srp = NULL; evt_struct->crq.format = format; - evt_struct->crq.timeout = timeout; + evt_struct->crq.timeout = cpu_to_be16(timeout); evt_struct->done = done; } @@ -311,16 +639,6 @@ static void set_srp_direction(struct scsi_cmnd *cmd, srp_cmd->buf_fmt = fmt; } -static void unmap_sg_list(int num_entries, - struct device *dev, - struct srp_direct_buf *md) -{ - int i; - - for (i = 0; i < num_entries; ++i) - dma_unmap_single(dev, md[i].va, md[i].len, DMA_BIDIRECTIONAL); -} - /** * unmap_cmd_data: - Unmap data pointed in srp_cmd based on the format * @cmd: srp_cmd whose additional_data member will be unmapped @@ -338,24 +656,9 @@ static void unmap_cmd_data(struct srp_cmd *cmd, if (out_fmt == SRP_NO_DATA_DESC && in_fmt == SRP_NO_DATA_DESC) return; - else if (out_fmt == SRP_DATA_DESC_DIRECT || - in_fmt == SRP_DATA_DESC_DIRECT) { - struct srp_direct_buf *data = - (struct srp_direct_buf *) cmd->add_data; - dma_unmap_single(dev, data->va, data->len, DMA_BIDIRECTIONAL); - } else { - struct srp_indirect_buf *indirect = - (struct srp_indirect_buf *) cmd->add_data; - int num_mapped = indirect->table_desc.len / - sizeof(struct srp_direct_buf); - - if (num_mapped <= MAX_INDIRECT_BUFS) { - unmap_sg_list(num_mapped, dev, &indirect->desc_list[0]); - return; - } - unmap_sg_list(num_mapped, dev, evt_struct->ext_list); - } + if (evt_struct->cmnd) + scsi_dma_unmap(evt_struct->cmnd); } static int map_sg_list(struct scsi_cmnd *cmd, int nseg, @@ -367,8 +670,8 @@ static int map_sg_list(struct scsi_cmnd *cmd, int nseg, scsi_for_each_sg(cmd, sg, nseg, i) { struct srp_direct_buf *descr = md + i; - descr->va = sg_dma_address(sg); - descr->len = sg_dma_len(sg); + descr->va = cpu_to_be64(sg_dma_address(sg)); + descr->len = cpu_to_be32(sg_dma_len(sg)); descr->key = 0; total_length += sg_dma_len(sg); } @@ -411,13 +714,14 @@ static int map_sg_data(struct scsi_cmnd *cmd, } indirect->table_desc.va = 0; - indirect->table_desc.len = sg_mapped * sizeof(struct srp_direct_buf); + indirect->table_desc.len = cpu_to_be32(sg_mapped * + sizeof(struct srp_direct_buf)); indirect->table_desc.key = 0; if (sg_mapped <= MAX_INDIRECT_BUFS) { total_length = map_sg_list(cmd, sg_mapped, &indirect->desc_list[0]); - indirect->len = total_length; + indirect->len = cpu_to_be32(total_length); return 1; } @@ -439,9 +743,10 @@ static int map_sg_data(struct scsi_cmnd *cmd, total_length = map_sg_list(cmd, sg_mapped, evt_struct->ext_list); - indirect->len = total_length; - indirect->table_desc.va = evt_struct->ext_list_token; - indirect->table_desc.len = sg_mapped * sizeof(indirect->desc_list[0]); + indirect->len = cpu_to_be32(total_length); + indirect->table_desc.va = cpu_to_be64(evt_struct->ext_list_token); + indirect->table_desc.len = cpu_to_be32(sg_mapped * + sizeof(indirect->desc_list[0])); memcpy(indirect->desc_list, evt_struct->ext_list, MAX_INDIRECT_BUFS * sizeof(struct srp_direct_buf)); return 1; @@ -486,23 +791,27 @@ static int map_data_for_srp_cmd(struct scsi_cmnd *cmd, */ static void purge_requests(struct ibmvscsi_host_data *hostdata, int error_code) { - struct srp_event_struct *tmp_evt, *pos; + struct srp_event_struct *evt; unsigned long flags; spin_lock_irqsave(hostdata->host->host_lock, flags); - list_for_each_entry_safe(tmp_evt, pos, &hostdata->sent, list) { - list_del(&tmp_evt->list); - del_timer(&tmp_evt->timer); - if (tmp_evt->cmnd) { - tmp_evt->cmnd->result = (error_code << 16); - unmap_cmd_data(&tmp_evt->iu.srp.cmd, - tmp_evt, - tmp_evt->hostdata->dev); - if (tmp_evt->cmnd_done) - tmp_evt->cmnd_done(tmp_evt->cmnd); - } else if (tmp_evt->done) - tmp_evt->done(tmp_evt); - free_event_struct(&tmp_evt->hostdata->pool, tmp_evt); + while (!list_empty(&hostdata->sent)) { + evt = list_first_entry(&hostdata->sent, struct srp_event_struct, list); + list_del(&evt->list); + del_timer(&evt->timer); + + spin_unlock_irqrestore(hostdata->host->host_lock, flags); + if (evt->cmnd) { + evt->cmnd->result = (error_code << 16); + unmap_cmd_data(&evt->iu.srp.cmd, evt, + evt->hostdata->dev); + if (evt->cmnd_done) + evt->cmnd_done(evt->cmnd); + } else if (evt->done && evt->crq.format != VIOSRP_MAD_FORMAT && + evt->iu.srp.login_req.opcode != SRP_LOGIN_REQ) + evt->done(evt); + free_event_struct(&evt->hostdata->pool, evt); + spin_lock_irqsave(hostdata->host->host_lock, flags); } spin_unlock_irqrestore(hostdata->host->host_lock, flags); } @@ -517,14 +826,8 @@ static void ibmvscsi_reset_host(struct ibmvscsi_host_data *hostdata) atomic_set(&hostdata->request_limit, 0); purge_requests(hostdata, DID_ERROR); - if ((ibmvscsi_ops->reset_crq_queue(&hostdata->queue, hostdata)) || - (ibmvscsi_ops->send_crq(hostdata, 0xC001000000000000LL, 0)) || - (vio_enable_interrupts(to_vio_dev(hostdata->dev)))) { - atomic_set(&hostdata->request_limit, -1); - dev_err(hostdata->dev, "error after reset\n"); - } - - scsi_unblock_requests(hostdata->host); + hostdata->reset_crq = 1; + wake_up(&hostdata->work_wait_q); } /** @@ -560,9 +863,10 @@ static int ibmvscsi_send_srp_event(struct srp_event_struct *evt_struct, struct ibmvscsi_host_data *hostdata, unsigned long timeout) { - u64 *crq_as_u64 = (u64 *) &evt_struct->crq; + __be64 *crq_as_u64 = (__be64 *)&evt_struct->crq; int request_status = 0; int rc; + int srp_req = 0; /* If we have exhausted our request limit, just fail this request, * unless it is for a reset or abort. @@ -571,6 +875,7 @@ static int ibmvscsi_send_srp_event(struct srp_event_struct *evt_struct, * can handle more requests (can_queue) when we actually can't */ if (evt_struct->crq.format == VIOSRP_SRP_FORMAT) { + srp_req = 1; request_status = atomic_dec_if_positive(&hostdata->request_limit); /* If request limit was -1 when we started, it is now even @@ -629,8 +934,9 @@ static int ibmvscsi_send_srp_event(struct srp_event_struct *evt_struct, add_timer(&evt_struct->timer); } - if ((rc = - ibmvscsi_ops->send_crq(hostdata, crq_as_u64[0], crq_as_u64[1])) != 0) { + rc = ibmvscsi_send_crq(hostdata, be64_to_cpu(crq_as_u64[0]), + be64_to_cpu(crq_as_u64[1])); + if (rc != 0) { list_del(&evt_struct->list); del_timer(&evt_struct->timer); @@ -645,7 +951,8 @@ static int ibmvscsi_send_srp_event(struct srp_event_struct *evt_struct, goto send_busy; } dev_err(hostdata->dev, "send error %d\n", rc); - atomic_inc(&hostdata->request_limit); + if (srp_req) + atomic_inc(&hostdata->request_limit); goto send_error; } @@ -655,7 +962,7 @@ static int ibmvscsi_send_srp_event(struct srp_event_struct *evt_struct, unmap_cmd_data(&evt_struct->iu.srp.cmd, evt_struct, hostdata->dev); free_event_struct(&hostdata->pool, evt_struct); - if (request_status != -1) + if (srp_req && request_status != -1) atomic_inc(&hostdata->request_limit); return SCSI_MLQUEUE_HOST_BUSY; @@ -695,15 +1002,16 @@ static void handle_cmd_rsp(struct srp_event_struct *evt_struct) if (((cmnd->result >> 1) & 0x1f) == CHECK_CONDITION) memcpy(cmnd->sense_buffer, rsp->data, - rsp->sense_data_len); + be32_to_cpu(rsp->sense_data_len)); unmap_cmd_data(&evt_struct->iu.srp.cmd, evt_struct, evt_struct->hostdata->dev); if (rsp->flags & SRP_RSP_FLAG_DOOVER) - scsi_set_resid(cmnd, rsp->data_out_res_cnt); + scsi_set_resid(cmnd, + be32_to_cpu(rsp->data_out_res_cnt)); else if (rsp->flags & SRP_RSP_FLAG_DIOVER) - scsi_set_resid(cmnd, rsp->data_in_res_cnt); + scsi_set_resid(cmnd, be32_to_cpu(rsp->data_in_res_cnt)); } if (evt_struct->cmnd_done) @@ -725,7 +1033,7 @@ static inline u16 lun_from_dev(struct scsi_device *dev) * @cmd: struct scsi_cmnd to be executed * @done: Callback function to be called when cmd is completed */ -static int ibmvscsi_queuecommand(struct scsi_cmnd *cmnd, +static int ibmvscsi_queuecommand_lck(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *)) { struct srp_cmd *srp_cmd; @@ -745,7 +1053,7 @@ static int ibmvscsi_queuecommand(struct scsi_cmnd *cmnd, memset(srp_cmd, 0x00, SRP_MAX_IU_LEN); srp_cmd->opcode = SRP_CMD; memcpy(srp_cmd->cdb, cmnd->cmnd, sizeof(srp_cmd->cdb)); - srp_cmd->lun = ((u64) lun) << 48; + srp_cmd->lun = cpu_to_be64(((u64)lun) << 48); if (!map_data_for_srp_cmd(cmnd, evt_struct, srp_cmd, hostdata->dev)) { if (!firmware_has_feature(FW_FEATURE_CMO)) @@ -770,116 +1078,67 @@ static int ibmvscsi_queuecommand(struct scsi_cmnd *cmnd, if ((in_fmt == SRP_DATA_DESC_INDIRECT || out_fmt == SRP_DATA_DESC_INDIRECT) && indirect->table_desc.va == 0) { - indirect->table_desc.va = evt_struct->crq.IU_data_ptr + + indirect->table_desc.va = + cpu_to_be64(be64_to_cpu(evt_struct->crq.IU_data_ptr) + offsetof(struct srp_cmd, add_data) + - offsetof(struct srp_indirect_buf, desc_list); + offsetof(struct srp_indirect_buf, desc_list)); } return ibmvscsi_send_srp_event(evt_struct, hostdata, 0); } +static DEF_SCSI_QCMD(ibmvscsi_queuecommand) + /* ------------------------------------------------------------ * Routines for driver initialization */ + /** - * adapter_info_rsp: - Handle response to MAD adapter info request - * @evt_struct: srp_event_struct with the response + * map_persist_bufs: - Pre-map persistent data for adapter logins + * @hostdata: ibmvscsi_host_data of host * - * Used as a "done" callback by when sending adapter_info. Gets called - * by ibmvscsi_handle_crq() -*/ -static void adapter_info_rsp(struct srp_event_struct *evt_struct) + * Map the capabilities and adapter info DMA buffers to avoid runtime failures. + * Return 1 on error, 0 on success. + */ +static int map_persist_bufs(struct ibmvscsi_host_data *hostdata) { - struct ibmvscsi_host_data *hostdata = evt_struct->hostdata; - dma_unmap_single(hostdata->dev, - evt_struct->iu.mad.adapter_info.buffer, - evt_struct->iu.mad.adapter_info.common.length, - DMA_BIDIRECTIONAL); - if (evt_struct->xfer_iu->mad.adapter_info.common.status) { - dev_err(hostdata->dev, "error %d getting adapter info\n", - evt_struct->xfer_iu->mad.adapter_info.common.status); - } else { - dev_info(hostdata->dev, "host srp version: %s, " - "host partition %s (%d), OS %d, max io %u\n", - hostdata->madapter_info.srp_version, - hostdata->madapter_info.partition_name, - hostdata->madapter_info.partition_number, - hostdata->madapter_info.os_type, - hostdata->madapter_info.port_max_txu[0]); - - if (hostdata->madapter_info.port_max_txu[0]) - hostdata->host->max_sectors = - hostdata->madapter_info.port_max_txu[0] >> 9; - - if (hostdata->madapter_info.os_type == 3 && - strcmp(hostdata->madapter_info.srp_version, "1.6a") <= 0) { - dev_err(hostdata->dev, "host (Ver. %s) doesn't support large transfers\n", - hostdata->madapter_info.srp_version); - dev_err(hostdata->dev, "limiting scatterlists to %d\n", - MAX_INDIRECT_BUFS); - hostdata->host->sg_tablesize = MAX_INDIRECT_BUFS; - } + hostdata->caps_addr = dma_map_single(hostdata->dev, &hostdata->caps, + sizeof(hostdata->caps), DMA_BIDIRECTIONAL); + + if (dma_mapping_error(hostdata->dev, hostdata->caps_addr)) { + dev_err(hostdata->dev, "Unable to map capabilities buffer!\n"); + return 1; } + + hostdata->adapter_info_addr = dma_map_single(hostdata->dev, + &hostdata->madapter_info, + sizeof(hostdata->madapter_info), + DMA_BIDIRECTIONAL); + if (dma_mapping_error(hostdata->dev, hostdata->adapter_info_addr)) { + dev_err(hostdata->dev, "Unable to map adapter info buffer!\n"); + dma_unmap_single(hostdata->dev, hostdata->caps_addr, + sizeof(hostdata->caps), DMA_BIDIRECTIONAL); + return 1; + } + + return 0; } /** - * send_mad_adapter_info: - Sends the mad adapter info request - * and stores the result so it can be retrieved with - * sysfs. We COULD consider causing a failure if the - * returned SRP version doesn't match ours. - * @hostdata: ibmvscsi_host_data of host - * - * Returns zero if successful. -*/ -static void send_mad_adapter_info(struct ibmvscsi_host_data *hostdata) + * unmap_persist_bufs: - Unmap persistent data needed for adapter logins + * @hostdata: ibmvscsi_host_data of host + * + * Unmap the capabilities and adapter info DMA buffers + */ +static void unmap_persist_bufs(struct ibmvscsi_host_data *hostdata) { - struct viosrp_adapter_info *req; - struct srp_event_struct *evt_struct; - unsigned long flags; - dma_addr_t addr; - - evt_struct = get_event_struct(&hostdata->pool); - if (!evt_struct) { - dev_err(hostdata->dev, - "couldn't allocate an event for ADAPTER_INFO_REQ!\n"); - return; - } + dma_unmap_single(hostdata->dev, hostdata->caps_addr, + sizeof(hostdata->caps), DMA_BIDIRECTIONAL); - init_event_struct(evt_struct, - adapter_info_rsp, - VIOSRP_MAD_FORMAT, - init_timeout); - - req = &evt_struct->iu.mad.adapter_info; - memset(req, 0x00, sizeof(*req)); - - req->common.type = VIOSRP_ADAPTER_INFO_TYPE; - req->common.length = sizeof(hostdata->madapter_info); - req->buffer = addr = dma_map_single(hostdata->dev, - &hostdata->madapter_info, - sizeof(hostdata->madapter_info), - DMA_BIDIRECTIONAL); - - if (dma_mapping_error(hostdata->dev, req->buffer)) { - if (!firmware_has_feature(FW_FEATURE_CMO)) - dev_err(hostdata->dev, - "Unable to map request_buffer for " - "adapter_info!\n"); - free_event_struct(&hostdata->pool, evt_struct); - return; - } - - spin_lock_irqsave(hostdata->host->host_lock, flags); - if (ibmvscsi_send_srp_event(evt_struct, hostdata, init_timeout * 2)) { - dev_err(hostdata->dev, "couldn't send ADAPTER_INFO_REQ!\n"); - dma_unmap_single(hostdata->dev, - addr, - sizeof(hostdata->madapter_info), - DMA_BIDIRECTIONAL); - } - spin_unlock_irqrestore(hostdata->host->host_lock, flags); -}; + dma_unmap_single(hostdata->dev, hostdata->adapter_info_addr, + sizeof(hostdata->madapter_info), DMA_BIDIRECTIONAL); +} /** * login_rsp: - Handle response to SRP login request @@ -909,28 +1168,23 @@ static void login_rsp(struct srp_event_struct *evt_struct) } dev_info(hostdata->dev, "SRP_LOGIN succeeded\n"); - - if (evt_struct->xfer_iu->srp.login_rsp.req_lim_delta < 0) - dev_err(hostdata->dev, "Invalid request_limit.\n"); + hostdata->client_migrated = 0; /* Now we know what the real request-limit is. * This value is set rather than added to request_limit because * request_limit could have been set to -1 by this client. */ atomic_set(&hostdata->request_limit, - evt_struct->xfer_iu->srp.login_rsp.req_lim_delta); + be32_to_cpu(evt_struct->xfer_iu->srp.login_rsp.req_lim_delta)); /* If we had any pending I/Os, kick them */ scsi_unblock_requests(hostdata->host); - - send_mad_adapter_info(hostdata); - return; } /** * send_srp_login: - Sends the srp login * @hostdata: ibmvscsi_host_data of host - * + * * Returns zero if successful. */ static int send_srp_login(struct ibmvscsi_host_data *hostdata) @@ -939,22 +1193,18 @@ static int send_srp_login(struct ibmvscsi_host_data *hostdata) unsigned long flags; struct srp_login_req *login; struct srp_event_struct *evt_struct = get_event_struct(&hostdata->pool); - if (!evt_struct) { - dev_err(hostdata->dev, "couldn't allocate an event for login req!\n"); - return FAILED; - } - init_event_struct(evt_struct, - login_rsp, - VIOSRP_SRP_FORMAT, - init_timeout); + BUG_ON(!evt_struct); + init_event_struct(evt_struct, login_rsp, + VIOSRP_SRP_FORMAT, login_timeout); login = &evt_struct->iu.srp.login_req; - memset(login, 0x00, sizeof(struct srp_login_req)); + memset(login, 0, sizeof(*login)); login->opcode = SRP_LOGIN_REQ; - login->req_it_iu_len = sizeof(union srp_iu); - login->req_buf_fmt = SRP_BUF_FORMAT_DIRECT | SRP_BUF_FORMAT_INDIRECT; - + login->req_it_iu_len = cpu_to_be32(sizeof(union srp_iu)); + login->req_buf_fmt = cpu_to_be16(SRP_BUF_FORMAT_DIRECT | + SRP_BUF_FORMAT_INDIRECT); + spin_lock_irqsave(hostdata->host->host_lock, flags); /* Start out with a request limit of 0, since this is negotiated in * the login request we are just sending and login requests always @@ -962,13 +1212,256 @@ static int send_srp_login(struct ibmvscsi_host_data *hostdata) */ atomic_set(&hostdata->request_limit, 0); - rc = ibmvscsi_send_srp_event(evt_struct, hostdata, init_timeout * 2); + rc = ibmvscsi_send_srp_event(evt_struct, hostdata, login_timeout * 2); spin_unlock_irqrestore(hostdata->host->host_lock, flags); dev_info(hostdata->dev, "sent SRP login\n"); return rc; }; /** + * capabilities_rsp: - Handle response to MAD adapter capabilities request + * @evt_struct: srp_event_struct with the response + * + * Used as a "done" callback by when sending adapter_info. + */ +static void capabilities_rsp(struct srp_event_struct *evt_struct) +{ + struct ibmvscsi_host_data *hostdata = evt_struct->hostdata; + + if (evt_struct->xfer_iu->mad.capabilities.common.status) { + dev_err(hostdata->dev, "error 0x%X getting capabilities info\n", + evt_struct->xfer_iu->mad.capabilities.common.status); + } else { + if (hostdata->caps.migration.common.server_support != + cpu_to_be16(SERVER_SUPPORTS_CAP)) + dev_info(hostdata->dev, "Partition migration not supported\n"); + + if (client_reserve) { + if (hostdata->caps.reserve.common.server_support == + cpu_to_be16(SERVER_SUPPORTS_CAP)) + dev_info(hostdata->dev, "Client reserve enabled\n"); + else + dev_info(hostdata->dev, "Client reserve not supported\n"); + } + } + + send_srp_login(hostdata); +} + +/** + * send_mad_capabilities: - Sends the mad capabilities request + * and stores the result so it can be retrieved with + * @hostdata: ibmvscsi_host_data of host + */ +static void send_mad_capabilities(struct ibmvscsi_host_data *hostdata) +{ + struct viosrp_capabilities *req; + struct srp_event_struct *evt_struct; + unsigned long flags; + struct device_node *of_node = hostdata->dev->of_node; + const char *location; + + evt_struct = get_event_struct(&hostdata->pool); + BUG_ON(!evt_struct); + + init_event_struct(evt_struct, capabilities_rsp, + VIOSRP_MAD_FORMAT, info_timeout); + + req = &evt_struct->iu.mad.capabilities; + memset(req, 0, sizeof(*req)); + + hostdata->caps.flags = cpu_to_be32(CAP_LIST_SUPPORTED); + if (hostdata->client_migrated) + hostdata->caps.flags |= cpu_to_be32(CLIENT_MIGRATED); + + strncpy(hostdata->caps.name, dev_name(&hostdata->host->shost_gendev), + sizeof(hostdata->caps.name)); + hostdata->caps.name[sizeof(hostdata->caps.name) - 1] = '\0'; + + location = of_get_property(of_node, "ibm,loc-code", NULL); + location = location ? location : dev_name(hostdata->dev); + strncpy(hostdata->caps.loc, location, sizeof(hostdata->caps.loc)); + hostdata->caps.loc[sizeof(hostdata->caps.loc) - 1] = '\0'; + + req->common.type = cpu_to_be32(VIOSRP_CAPABILITIES_TYPE); + req->buffer = cpu_to_be64(hostdata->caps_addr); + + hostdata->caps.migration.common.cap_type = + cpu_to_be32(MIGRATION_CAPABILITIES); + hostdata->caps.migration.common.length = + cpu_to_be16(sizeof(hostdata->caps.migration)); + hostdata->caps.migration.common.server_support = + cpu_to_be16(SERVER_SUPPORTS_CAP); + hostdata->caps.migration.ecl = cpu_to_be32(1); + + if (client_reserve) { + hostdata->caps.reserve.common.cap_type = + cpu_to_be32(RESERVATION_CAPABILITIES); + hostdata->caps.reserve.common.length = + cpu_to_be16(sizeof(hostdata->caps.reserve)); + hostdata->caps.reserve.common.server_support = + cpu_to_be16(SERVER_SUPPORTS_CAP); + hostdata->caps.reserve.type = + cpu_to_be32(CLIENT_RESERVE_SCSI_2); + req->common.length = + cpu_to_be16(sizeof(hostdata->caps)); + } else + req->common.length = cpu_to_be16(sizeof(hostdata->caps) - + sizeof(hostdata->caps.reserve)); + + spin_lock_irqsave(hostdata->host->host_lock, flags); + if (ibmvscsi_send_srp_event(evt_struct, hostdata, info_timeout * 2)) + dev_err(hostdata->dev, "couldn't send CAPABILITIES_REQ!\n"); + spin_unlock_irqrestore(hostdata->host->host_lock, flags); +}; + +/** + * fast_fail_rsp: - Handle response to MAD enable fast fail + * @evt_struct: srp_event_struct with the response + * + * Used as a "done" callback by when sending enable fast fail. Gets called + * by ibmvscsi_handle_crq() + */ +static void fast_fail_rsp(struct srp_event_struct *evt_struct) +{ + struct ibmvscsi_host_data *hostdata = evt_struct->hostdata; + u16 status = be16_to_cpu(evt_struct->xfer_iu->mad.fast_fail.common.status); + + if (status == VIOSRP_MAD_NOT_SUPPORTED) + dev_err(hostdata->dev, "fast_fail not supported in server\n"); + else if (status == VIOSRP_MAD_FAILED) + dev_err(hostdata->dev, "fast_fail request failed\n"); + else if (status != VIOSRP_MAD_SUCCESS) + dev_err(hostdata->dev, "error 0x%X enabling fast_fail\n", status); + + send_mad_capabilities(hostdata); +} + +/** + * init_host - Start host initialization + * @hostdata: ibmvscsi_host_data of host + * + * Returns zero if successful. + */ +static int enable_fast_fail(struct ibmvscsi_host_data *hostdata) +{ + int rc; + unsigned long flags; + struct viosrp_fast_fail *fast_fail_mad; + struct srp_event_struct *evt_struct; + + if (!fast_fail) { + send_mad_capabilities(hostdata); + return 0; + } + + evt_struct = get_event_struct(&hostdata->pool); + BUG_ON(!evt_struct); + + init_event_struct(evt_struct, fast_fail_rsp, VIOSRP_MAD_FORMAT, info_timeout); + + fast_fail_mad = &evt_struct->iu.mad.fast_fail; + memset(fast_fail_mad, 0, sizeof(*fast_fail_mad)); + fast_fail_mad->common.type = cpu_to_be32(VIOSRP_ENABLE_FAST_FAIL); + fast_fail_mad->common.length = cpu_to_be16(sizeof(*fast_fail_mad)); + + spin_lock_irqsave(hostdata->host->host_lock, flags); + rc = ibmvscsi_send_srp_event(evt_struct, hostdata, info_timeout * 2); + spin_unlock_irqrestore(hostdata->host->host_lock, flags); + return rc; +} + +/** + * adapter_info_rsp: - Handle response to MAD adapter info request + * @evt_struct: srp_event_struct with the response + * + * Used as a "done" callback by when sending adapter_info. Gets called + * by ibmvscsi_handle_crq() +*/ +static void adapter_info_rsp(struct srp_event_struct *evt_struct) +{ + struct ibmvscsi_host_data *hostdata = evt_struct->hostdata; + + if (evt_struct->xfer_iu->mad.adapter_info.common.status) { + dev_err(hostdata->dev, "error %d getting adapter info\n", + evt_struct->xfer_iu->mad.adapter_info.common.status); + } else { + dev_info(hostdata->dev, "host srp version: %s, " + "host partition %s (%d), OS %d, max io %u\n", + hostdata->madapter_info.srp_version, + hostdata->madapter_info.partition_name, + be32_to_cpu(hostdata->madapter_info.partition_number), + be32_to_cpu(hostdata->madapter_info.os_type), + be32_to_cpu(hostdata->madapter_info.port_max_txu[0])); + + if (hostdata->madapter_info.port_max_txu[0]) + hostdata->host->max_sectors = + be32_to_cpu(hostdata->madapter_info.port_max_txu[0]) >> 9; + + if (be32_to_cpu(hostdata->madapter_info.os_type) == 3 && + strcmp(hostdata->madapter_info.srp_version, "1.6a") <= 0) { + dev_err(hostdata->dev, "host (Ver. %s) doesn't support large transfers\n", + hostdata->madapter_info.srp_version); + dev_err(hostdata->dev, "limiting scatterlists to %d\n", + MAX_INDIRECT_BUFS); + hostdata->host->sg_tablesize = MAX_INDIRECT_BUFS; + } + + if (be32_to_cpu(hostdata->madapter_info.os_type) == 3) { + enable_fast_fail(hostdata); + return; + } + } + + send_srp_login(hostdata); +} + +/** + * send_mad_adapter_info: - Sends the mad adapter info request + * and stores the result so it can be retrieved with + * sysfs. We COULD consider causing a failure if the + * returned SRP version doesn't match ours. + * @hostdata: ibmvscsi_host_data of host + * + * Returns zero if successful. +*/ +static void send_mad_adapter_info(struct ibmvscsi_host_data *hostdata) +{ + struct viosrp_adapter_info *req; + struct srp_event_struct *evt_struct; + unsigned long flags; + + evt_struct = get_event_struct(&hostdata->pool); + BUG_ON(!evt_struct); + + init_event_struct(evt_struct, + adapter_info_rsp, + VIOSRP_MAD_FORMAT, + info_timeout); + + req = &evt_struct->iu.mad.adapter_info; + memset(req, 0x00, sizeof(*req)); + + req->common.type = cpu_to_be32(VIOSRP_ADAPTER_INFO_TYPE); + req->common.length = cpu_to_be16(sizeof(hostdata->madapter_info)); + req->buffer = cpu_to_be64(hostdata->adapter_info_addr); + + spin_lock_irqsave(hostdata->host->host_lock, flags); + if (ibmvscsi_send_srp_event(evt_struct, hostdata, info_timeout * 2)) + dev_err(hostdata->dev, "couldn't send ADAPTER_INFO_REQ!\n"); + spin_unlock_irqrestore(hostdata->host->host_lock, flags); +}; + +/** + * init_adapter: Start virtual adapter initialization sequence + * + */ +static void init_adapter(struct ibmvscsi_host_data *hostdata) +{ + send_mad_adapter_info(hostdata); +} + +/** * sync_completion: Signal that a synchronous command has completed * Note that after returning from this call, the evt_struct is freed. * the caller waiting on this completion shouldn't touch the evt_struct @@ -1029,21 +1522,21 @@ static int ibmvscsi_eh_abort_handler(struct scsi_cmnd *cmd) init_event_struct(evt, sync_completion, VIOSRP_SRP_FORMAT, - init_timeout); + abort_timeout); tsk_mgmt = &evt->iu.srp.tsk_mgmt; /* Set up an abort SRP command */ memset(tsk_mgmt, 0x00, sizeof(*tsk_mgmt)); tsk_mgmt->opcode = SRP_TSK_MGMT; - tsk_mgmt->lun = ((u64) lun) << 48; + tsk_mgmt->lun = cpu_to_be64(((u64) lun) << 48); tsk_mgmt->tsk_mgmt_func = SRP_TSK_ABORT_TASK; tsk_mgmt->task_tag = (u64) found_evt; evt->sync_srp = &srp_rsp; init_completion(&evt->comp); - rsp_rc = ibmvscsi_send_srp_event(evt, hostdata, init_timeout * 2); + rsp_rc = ibmvscsi_send_srp_event(evt, hostdata, abort_timeout * 2); if (rsp_rc != SCSI_MLQUEUE_HOST_BUSY) break; @@ -1152,20 +1645,20 @@ static int ibmvscsi_eh_device_reset_handler(struct scsi_cmnd *cmd) init_event_struct(evt, sync_completion, VIOSRP_SRP_FORMAT, - init_timeout); + reset_timeout); tsk_mgmt = &evt->iu.srp.tsk_mgmt; /* Set up a lun reset SRP command */ memset(tsk_mgmt, 0x00, sizeof(*tsk_mgmt)); tsk_mgmt->opcode = SRP_TSK_MGMT; - tsk_mgmt->lun = ((u64) lun) << 48; + tsk_mgmt->lun = cpu_to_be64(((u64) lun) << 48); tsk_mgmt->tsk_mgmt_func = SRP_TSK_LUN_RESET; evt->sync_srp = &srp_rsp; init_completion(&evt->comp); - rsp_rc = ibmvscsi_send_srp_event(evt, hostdata, init_timeout * 2); + rsp_rc = ibmvscsi_send_srp_event(evt, hostdata, reset_timeout * 2); if (rsp_rc != SCSI_MLQUEUE_HOST_BUSY) break; @@ -1265,23 +1758,24 @@ static int ibmvscsi_eh_host_reset_handler(struct scsi_cmnd *cmd) * @hostdata: ibmvscsi_host_data of host * */ -void ibmvscsi_handle_crq(struct viosrp_crq *crq, - struct ibmvscsi_host_data *hostdata) +static void ibmvscsi_handle_crq(struct viosrp_crq *crq, + struct ibmvscsi_host_data *hostdata) { long rc; unsigned long flags; + /* The hypervisor copies our tag value here so no byteswapping */ struct srp_event_struct *evt_struct = - (struct srp_event_struct *)crq->IU_data_ptr; + (__force struct srp_event_struct *)crq->IU_data_ptr; switch (crq->valid) { case 0xC0: /* initialization */ switch (crq->format) { case 0x01: /* Initialization message */ dev_info(hostdata->dev, "partner initialized\n"); /* Send back a response */ - if ((rc = ibmvscsi_ops->send_crq(hostdata, - 0xC002000000000000LL, 0)) == 0) { + rc = ibmvscsi_send_crq(hostdata, 0xC002000000000000LL, 0); + if (rc == 0) { /* Now login */ - send_srp_login(hostdata); + init_adapter(hostdata); } else { dev_err(hostdata->dev, "Unable to send init rsp. rc=%ld\n", rc); } @@ -1291,7 +1785,7 @@ void ibmvscsi_handle_crq(struct viosrp_crq *crq, dev_info(hostdata->dev, "partner initialization complete\n"); /* Now login */ - send_srp_login(hostdata); + init_adapter(hostdata); break; default: dev_err(hostdata->dev, "unknown crq message type: %d\n", crq->format); @@ -1303,30 +1797,15 @@ void ibmvscsi_handle_crq(struct viosrp_crq *crq, if (crq->format == 0x06) { /* We need to re-setup the interpartition connection */ dev_info(hostdata->dev, "Re-enabling adapter!\n"); + hostdata->client_migrated = 1; + hostdata->reenable_crq = 1; purge_requests(hostdata, DID_REQUEUE); - if ((ibmvscsi_ops->reenable_crq_queue(&hostdata->queue, - hostdata)) || - (ibmvscsi_ops->send_crq(hostdata, - 0xC001000000000000LL, 0))) { - atomic_set(&hostdata->request_limit, - -1); - dev_err(hostdata->dev, "error after enable\n"); - } + wake_up(&hostdata->work_wait_q); } else { dev_err(hostdata->dev, "Virtual adapter failed rc %d!\n", crq->format); - - purge_requests(hostdata, DID_ERROR); - if ((ibmvscsi_ops->reset_crq_queue(&hostdata->queue, - hostdata)) || - (ibmvscsi_ops->send_crq(hostdata, - 0xC001000000000000LL, 0))) { - atomic_set(&hostdata->request_limit, - -1); - dev_err(hostdata->dev, "error after reset\n"); - } + ibmvscsi_reset_host(hostdata); } - scsi_unblock_requests(hostdata->host); return; case 0x80: /* real payload */ break; @@ -1342,18 +1821,18 @@ void ibmvscsi_handle_crq(struct viosrp_crq *crq, */ if (!valid_event_struct(&hostdata->pool, evt_struct)) { dev_err(hostdata->dev, "returned correlation_token 0x%p is invalid!\n", - (void *)crq->IU_data_ptr); + evt_struct); return; } if (atomic_read(&evt_struct->free)) { dev_err(hostdata->dev, "received duplicate correlation_token 0x%p!\n", - (void *)crq->IU_data_ptr); + evt_struct); return; } if (crq->format == VIOSRP_SRP_FORMAT) - atomic_add(evt_struct->xfer_iu->srp.rsp.req_lim_delta, + atomic_add(be32_to_cpu(evt_struct->xfer_iu->srp.rsp.req_lim_delta), &hostdata->request_limit); del_timer(&evt_struct->timer); @@ -1397,19 +1876,20 @@ static int ibmvscsi_do_host_config(struct ibmvscsi_host_data *hostdata, init_event_struct(evt_struct, sync_completion, VIOSRP_MAD_FORMAT, - init_timeout); + info_timeout); host_config = &evt_struct->iu.mad.host_config; + /* The transport length field is only 16-bit */ + length = min(0xffff, length); + /* Set up a lun reset SRP command */ memset(host_config, 0x00, sizeof(*host_config)); - host_config->common.type = VIOSRP_HOST_CONFIG_TYPE; - host_config->common.length = length; - host_config->buffer = addr = dma_map_single(hostdata->dev, buffer, - length, - DMA_BIDIRECTIONAL); + host_config->common.type = cpu_to_be32(VIOSRP_HOST_CONFIG_TYPE); + host_config->common.length = cpu_to_be16(length); + addr = dma_map_single(hostdata->dev, buffer, length, DMA_BIDIRECTIONAL); - if (dma_mapping_error(hostdata->dev, host_config->buffer)) { + if (dma_mapping_error(hostdata->dev, addr)) { if (!firmware_has_feature(FW_FEATURE_CMO)) dev_err(hostdata->dev, "dma_mapping error getting host config\n"); @@ -1417,9 +1897,11 @@ static int ibmvscsi_do_host_config(struct ibmvscsi_host_data *hostdata, return -1; } + host_config->buffer = cpu_to_be64(addr); + init_completion(&evt_struct->comp); spin_lock_irqsave(hostdata->host->host_lock, flags); - rc = ibmvscsi_send_srp_event(evt_struct, hostdata, init_timeout * 2); + rc = ibmvscsi_send_srp_event(evt_struct, hostdata, info_timeout * 2); spin_unlock_irqrestore(hostdata->host->host_lock, flags); if (rc == 0) wait_for_completion(&evt_struct->comp); @@ -1444,10 +1926,10 @@ static int ibmvscsi_slave_configure(struct scsi_device *sdev) spin_lock_irqsave(shost->host_lock, lock_flags); if (sdev->type == TYPE_DISK) { sdev->allow_restart = 1; - blk_queue_rq_timeout(sdev->request_queue, 60 * HZ); + blk_queue_rq_timeout(sdev->request_queue, 120 * HZ); } - scsi_adjust_queue_depth(sdev, 0, shost->cmd_per_lun); spin_unlock_irqrestore(shost->host_lock, lock_flags); + scsi_adjust_queue_depth(sdev, 0, shost->cmd_per_lun); return 0; } @@ -1455,12 +1937,17 @@ static int ibmvscsi_slave_configure(struct scsi_device *sdev) * ibmvscsi_change_queue_depth - Change the device's queue depth * @sdev: scsi device struct * @qdepth: depth to set + * @reason: calling context * * Return value: * actual depth set **/ -static int ibmvscsi_change_queue_depth(struct scsi_device *sdev, int qdepth) +static int ibmvscsi_change_queue_depth(struct scsi_device *sdev, int qdepth, + int reason) { + if (reason != SCSI_QDEPTH_DEFAULT) + return -EOPNOTSUPP; + if (qdepth > IBMVSCSI_MAX_CMDS_PER_LUN) qdepth = IBMVSCSI_MAX_CMDS_PER_LUN; @@ -1471,6 +1958,46 @@ static int ibmvscsi_change_queue_depth(struct scsi_device *sdev, int qdepth) /* ------------------------------------------------------------ * sysfs attributes */ +static ssize_t show_host_vhost_loc(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct Scsi_Host *shost = class_to_shost(dev); + struct ibmvscsi_host_data *hostdata = shost_priv(shost); + int len; + + len = snprintf(buf, sizeof(hostdata->caps.loc), "%s\n", + hostdata->caps.loc); + return len; +} + +static struct device_attribute ibmvscsi_host_vhost_loc = { + .attr = { + .name = "vhost_loc", + .mode = S_IRUGO, + }, + .show = show_host_vhost_loc, +}; + +static ssize_t show_host_vhost_name(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct Scsi_Host *shost = class_to_shost(dev); + struct ibmvscsi_host_data *hostdata = shost_priv(shost); + int len; + + len = snprintf(buf, sizeof(hostdata->caps.name), "%s\n", + hostdata->caps.name); + return len; +} + +static struct device_attribute ibmvscsi_host_vhost_name = { + .attr = { + .name = "vhost_name", + .mode = S_IRUGO, + }, + .show = show_host_vhost_name, +}; + static ssize_t show_host_srp_version(struct device *dev, struct device_attribute *attr, char *buf) { @@ -1594,6 +2121,8 @@ static struct device_attribute ibmvscsi_host_config = { }; static struct device_attribute *ibmvscsi_attrs[] = { + &ibmvscsi_host_vhost_loc, + &ibmvscsi_host_vhost_name, &ibmvscsi_host_srp_version, &ibmvscsi_host_partition_name, &ibmvscsi_host_partition_number, @@ -1644,6 +2173,74 @@ static unsigned long ibmvscsi_get_desired_dma(struct vio_dev *vdev) return desired_io; } +static void ibmvscsi_do_work(struct ibmvscsi_host_data *hostdata) +{ + int rc; + char *action = "reset"; + + if (hostdata->reset_crq) { + smp_rmb(); + hostdata->reset_crq = 0; + + rc = ibmvscsi_reset_crq_queue(&hostdata->queue, hostdata); + if (!rc) + rc = ibmvscsi_send_crq(hostdata, 0xC001000000000000LL, 0); + vio_enable_interrupts(to_vio_dev(hostdata->dev)); + } else if (hostdata->reenable_crq) { + smp_rmb(); + action = "enable"; + rc = ibmvscsi_reenable_crq_queue(&hostdata->queue, hostdata); + hostdata->reenable_crq = 0; + if (!rc) + rc = ibmvscsi_send_crq(hostdata, 0xC001000000000000LL, 0); + } else + return; + + if (rc) { + atomic_set(&hostdata->request_limit, -1); + dev_err(hostdata->dev, "error after %s\n", action); + } + + scsi_unblock_requests(hostdata->host); +} + +static int ibmvscsi_work_to_do(struct ibmvscsi_host_data *hostdata) +{ + if (kthread_should_stop()) + return 1; + else if (hostdata->reset_crq) { + smp_rmb(); + return 1; + } else if (hostdata->reenable_crq) { + smp_rmb(); + return 1; + } + + return 0; +} + +static int ibmvscsi_work(void *data) +{ + struct ibmvscsi_host_data *hostdata = data; + int rc; + + set_user_nice(current, MIN_NICE); + + while (1) { + rc = wait_event_interruptible(hostdata->work_wait_q, + ibmvscsi_work_to_do(hostdata)); + + BUG_ON(rc); + + if (kthread_should_stop()) + break; + + ibmvscsi_do_work(hostdata); + } + + return 0; +} + /** * Called by bus code for each adapter */ @@ -1657,7 +2254,7 @@ static int ibmvscsi_probe(struct vio_dev *vdev, const struct vio_device_id *id) unsigned long wait_switch = 0; int rc; - vdev->dev.driver_data = NULL; + dev_set_drvdata(&vdev->dev, NULL); host = scsi_host_alloc(&driver_template, sizeof(*hostdata)); if (!host) { @@ -1669,15 +2266,30 @@ static int ibmvscsi_probe(struct vio_dev *vdev, const struct vio_device_id *id) hostdata = shost_priv(host); memset(hostdata, 0x00, sizeof(*hostdata)); INIT_LIST_HEAD(&hostdata->sent); + init_waitqueue_head(&hostdata->work_wait_q); hostdata->host = host; hostdata->dev = dev; atomic_set(&hostdata->request_limit, -1); hostdata->host->max_sectors = IBMVSCSI_MAX_SECTORS_DEFAULT; - rc = ibmvscsi_ops->init_crq_queue(&hostdata->queue, hostdata, max_events); + if (map_persist_bufs(hostdata)) { + dev_err(&vdev->dev, "couldn't map persistent buffers\n"); + goto persist_bufs_failed; + } + + hostdata->work_thread = kthread_run(ibmvscsi_work, hostdata, "%s_%d", + "ibmvscsi", host->host_no); + + if (IS_ERR(hostdata->work_thread)) { + dev_err(&vdev->dev, "couldn't initialize kthread. rc=%ld\n", + PTR_ERR(hostdata->work_thread)); + goto init_crq_failed; + } + + rc = ibmvscsi_init_crq_queue(&hostdata->queue, hostdata, max_events); if (rc != 0 && rc != H_RESOURCE) { dev_err(&vdev->dev, "couldn't initialize crq. rc=%d\n", rc); - goto init_crq_failed; + goto kill_kthread; } if (initialize_event_pool(&hostdata->pool, max_events, hostdata) != 0) { dev_err(&vdev->dev, "couldn't initialize event pool\n"); @@ -1687,6 +2299,7 @@ static int ibmvscsi_probe(struct vio_dev *vdev, const struct vio_device_id *id) host->max_lun = 8; host->max_id = max_id; host->max_channel = max_channel; + host->max_cmd_len = 16; if (scsi_add_host(hostdata->host, hostdata->dev)) goto add_host_failed; @@ -1703,7 +2316,7 @@ static int ibmvscsi_probe(struct vio_dev *vdev, const struct vio_device_id *id) * to fail if the other end is not acive. In that case we don't * want to scan */ - if (ibmvscsi_ops->send_crq(hostdata, 0xC001000000000000LL, 0) == 0 + if (ibmvscsi_send_crq(hostdata, 0xC001000000000000LL, 0) == 0 || rc == H_RESOURCE) { /* * Wait around max init_timeout secs for the adapter to finish @@ -1723,7 +2336,7 @@ static int ibmvscsi_probe(struct vio_dev *vdev, const struct vio_device_id *id) scsi_scan_host(host); } - vdev->dev.driver_data = hostdata; + dev_set_drvdata(&vdev->dev, hostdata); return 0; add_srp_port_failed: @@ -1731,8 +2344,12 @@ static int ibmvscsi_probe(struct vio_dev *vdev, const struct vio_device_id *id) add_host_failed: release_event_pool(&hostdata->pool, hostdata); init_pool_failed: - ibmvscsi_ops->release_crq_queue(&hostdata->queue, hostdata, max_events); + ibmvscsi_release_crq_queue(&hostdata->queue, hostdata, max_events); + kill_kthread: + kthread_stop(hostdata->work_thread); init_crq_failed: + unmap_persist_bufs(hostdata); + persist_bufs_failed: scsi_host_put(host); scsi_host_alloc_failed: return -1; @@ -1740,11 +2357,13 @@ static int ibmvscsi_probe(struct vio_dev *vdev, const struct vio_device_id *id) static int ibmvscsi_remove(struct vio_dev *vdev) { - struct ibmvscsi_host_data *hostdata = vdev->dev.driver_data; + struct ibmvscsi_host_data *hostdata = dev_get_drvdata(&vdev->dev); + unmap_persist_bufs(hostdata); release_event_pool(&hostdata->pool, hostdata); - ibmvscsi_ops->release_crq_queue(&hostdata->queue, hostdata, + ibmvscsi_release_crq_queue(&hostdata->queue, hostdata, max_events); + kthread_stop(hostdata->work_thread); srp_remove_host(hostdata->host); scsi_remove_host(hostdata->host); scsi_host_put(hostdata->host); @@ -1753,24 +2372,42 @@ static int ibmvscsi_remove(struct vio_dev *vdev) } /** + * ibmvscsi_resume: Resume from suspend + * @dev: device struct + * + * We may have lost an interrupt across suspend/resume, so kick the + * interrupt handler + */ +static int ibmvscsi_resume(struct device *dev) +{ + struct ibmvscsi_host_data *hostdata = dev_get_drvdata(dev); + vio_disable_interrupts(to_vio_dev(hostdata->dev)); + tasklet_schedule(&hostdata->srp_task); + + return 0; +} + +/** * ibmvscsi_device_table: Used by vio.c to match devices in the device tree we * support. */ -static struct vio_device_id ibmvscsi_device_table[] __devinitdata = { +static struct vio_device_id ibmvscsi_device_table[] = { {"vscsi", "IBM,v-scsi"}, { "", "" } }; MODULE_DEVICE_TABLE(vio, ibmvscsi_device_table); +static struct dev_pm_ops ibmvscsi_pm_ops = { + .resume = ibmvscsi_resume +}; + static struct vio_driver ibmvscsi_driver = { .id_table = ibmvscsi_device_table, .probe = ibmvscsi_probe, .remove = ibmvscsi_remove, .get_desired_dma = ibmvscsi_get_desired_dma, - .driver = { - .name = "ibmvscsi", - .owner = THIS_MODULE, - } + .name = "ibmvscsi", + .pm = &ibmvscsi_pm_ops, }; static struct srp_function_template ibmvscsi_transport_functions = { @@ -1784,11 +2421,7 @@ int __init ibmvscsi_module_init(void) driver_template.can_queue = max_requests; max_events = max_requests + 2; - if (firmware_has_feature(FW_FEATURE_ISERIES)) - ibmvscsi_ops = &iseriesvscsi_ops; - else if (firmware_has_feature(FW_FEATURE_VIO)) - ibmvscsi_ops = &rpavscsi_ops; - else + if (!firmware_has_feature(FW_FEATURE_VIO)) return -ENODEV; ibmvscsi_transport_template = diff --git a/drivers/scsi/ibmvscsi/ibmvscsi.h b/drivers/scsi/ibmvscsi/ibmvscsi.h index 2d4339d5e16..7d64867c5dd 100644 --- a/drivers/scsi/ibmvscsi/ibmvscsi.h +++ b/drivers/scsi/ibmvscsi/ibmvscsi.h @@ -90,35 +90,21 @@ struct event_pool { /* all driver data associated with a host adapter */ struct ibmvscsi_host_data { atomic_t request_limit; + int client_migrated; + int reset_crq; + int reenable_crq; struct device *dev; struct event_pool pool; struct crq_queue queue; struct tasklet_struct srp_task; struct list_head sent; struct Scsi_Host *host; + struct task_struct *work_thread; + wait_queue_head_t work_wait_q; struct mad_adapter_info_data madapter_info; + struct capabilities caps; + dma_addr_t caps_addr; + dma_addr_t adapter_info_addr; }; -/* routines for managing a command/response queue */ -void ibmvscsi_handle_crq(struct viosrp_crq *crq, - struct ibmvscsi_host_data *hostdata); - -struct ibmvscsi_ops { - int (*init_crq_queue)(struct crq_queue *queue, - struct ibmvscsi_host_data *hostdata, - int max_requests); - void (*release_crq_queue)(struct crq_queue *queue, - struct ibmvscsi_host_data *hostdata, - int max_requests); - int (*reset_crq_queue)(struct crq_queue *queue, - struct ibmvscsi_host_data *hostdata); - int (*reenable_crq_queue)(struct crq_queue *queue, - struct ibmvscsi_host_data *hostdata); - int (*send_crq)(struct ibmvscsi_host_data *hostdata, - u64 word1, u64 word2); -}; - -extern struct ibmvscsi_ops iseriesvscsi_ops; -extern struct ibmvscsi_ops rpavscsi_ops; - #endif /* IBMVSCSI_H */ diff --git a/drivers/scsi/ibmvscsi/ibmvstgt.c b/drivers/scsi/ibmvscsi/ibmvstgt.c index e2dd6a45924..56f8a861ed7 100644 --- a/drivers/scsi/ibmvscsi/ibmvstgt.c +++ b/drivers/scsi/ibmvscsi/ibmvstgt.c @@ -23,6 +23,7 @@ */ #include <linux/interrupt.h> #include <linux/module.h> +#include <linux/slab.h> #include <scsi/scsi.h> #include <scsi/scsi_host.h> #include <scsi/scsi_transport_srp.h> @@ -588,7 +589,7 @@ static int crq_queue_create(struct crq_queue *queue, struct srp_target *target) } err = request_irq(vport->dma_dev->irq, &ibmvstgt_interrupt, - IRQF_DISABLED, "ibmvstgt", target); + 0, "ibmvstgt", target); if (err) goto req_irq_failed; @@ -892,7 +893,7 @@ free_vport: static int ibmvstgt_remove(struct vio_dev *dev) { - struct srp_target *target = (struct srp_target *) dev->dev.driver_data; + struct srp_target *target = dev_get_drvdata(&dev->dev); struct Scsi_Host *shost = target->shost; struct vio_port *vport = target->ldata; @@ -906,7 +907,7 @@ static int ibmvstgt_remove(struct vio_dev *dev) return 0; } -static struct vio_device_id ibmvstgt_device_table[] __devinitdata = { +static struct vio_device_id ibmvstgt_device_table[] = { {"v-scsi-host", "IBM,v-scsi-host"}, {"",""} }; @@ -917,10 +918,7 @@ static struct vio_driver ibmvstgt_driver = { .id_table = ibmvstgt_device_table, .probe = ibmvstgt_probe, .remove = ibmvstgt_remove, - .driver = { - .name = "ibmvscsis", - .owner = THIS_MODULE, - } + .name = "ibmvscsis", }; static int get_system_info(void) @@ -955,7 +953,7 @@ static struct srp_function_template ibmvstgt_transport_functions = { .it_nexus_response = ibmvstgt_it_nexus_response, }; -static int ibmvstgt_init(void) +static int __init ibmvstgt_init(void) { int err = -ENOMEM; @@ -986,7 +984,7 @@ release_transport: return err; } -static void ibmvstgt_exit(void) +static void __exit ibmvstgt_exit(void) { printk("Unregister IBM virtual SCSI driver\n"); diff --git a/drivers/scsi/ibmvscsi/iseries_vscsi.c b/drivers/scsi/ibmvscsi/iseries_vscsi.c deleted file mode 100644 index 0775fdee5fa..00000000000 --- a/drivers/scsi/ibmvscsi/iseries_vscsi.c +++ /dev/null @@ -1,167 +0,0 @@ -/* ------------------------------------------------------------ - * iSeries_vscsi.c - * (C) Copyright IBM Corporation 1994, 2003 - * Authors: Colin DeVilbiss (devilbis@us.ibm.com) - * Santiago Leon (santil@us.ibm.com) - * Dave Boutcher (sleddog@us.ibm.com) - * - * 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 - * - * ------------------------------------------------------------ - * iSeries-specific functions of the SCSI host adapter for Virtual I/O devices - * - * This driver allows the Linux SCSI peripheral drivers to directly - * access devices in the hosting partition, either on an iSeries - * hypervisor system or a converged hypervisor system. - */ - -#include <asm/iseries/vio.h> -#include <asm/iseries/hv_lp_event.h> -#include <asm/iseries/hv_types.h> -#include <asm/iseries/hv_lp_config.h> -#include <asm/vio.h> -#include <linux/device.h> -#include "ibmvscsi.h" - -/* global variables */ -static struct ibmvscsi_host_data *single_host_data; - -/* ------------------------------------------------------------ - * Routines for direct interpartition interaction - */ -struct srp_lp_event { - struct HvLpEvent lpevt; /* 0x00-0x17 */ - u32 reserved1; /* 0x18-0x1B; unused */ - u16 version; /* 0x1C-0x1D; unused */ - u16 subtype_rc; /* 0x1E-0x1F; unused */ - struct viosrp_crq crq; /* 0x20-0x3F */ -}; - -/** - * standard interface for handling logical partition events. - */ -static void iseriesvscsi_handle_event(struct HvLpEvent *lpevt) -{ - struct srp_lp_event *evt = (struct srp_lp_event *)lpevt; - - if (!evt) { - printk(KERN_ERR "ibmvscsi: received null event\n"); - return; - } - - if (single_host_data == NULL) { - printk(KERN_ERR - "ibmvscsi: received event, no adapter present\n"); - return; - } - - ibmvscsi_handle_crq(&evt->crq, single_host_data); -} - -/* ------------------------------------------------------------ - * Routines for driver initialization - */ -static int iseriesvscsi_init_crq_queue(struct crq_queue *queue, - struct ibmvscsi_host_data *hostdata, - int max_requests) -{ - int rc; - - single_host_data = hostdata; - rc = viopath_open(viopath_hostLp, viomajorsubtype_scsi, max_requests); - if (rc < 0) { - printk("viopath_open failed with rc %d in open_event_path\n", - rc); - goto viopath_open_failed; - } - - rc = vio_setHandler(viomajorsubtype_scsi, iseriesvscsi_handle_event); - if (rc < 0) { - printk("vio_setHandler failed with rc %d in open_event_path\n", - rc); - goto vio_setHandler_failed; - } - return 0; - - vio_setHandler_failed: - viopath_close(viopath_hostLp, viomajorsubtype_scsi, max_requests); - viopath_open_failed: - return -1; -} - -static void iseriesvscsi_release_crq_queue(struct crq_queue *queue, - struct ibmvscsi_host_data *hostdata, - int max_requests) -{ - vio_clearHandler(viomajorsubtype_scsi); - viopath_close(viopath_hostLp, viomajorsubtype_scsi, max_requests); -} - -/** - * reset_crq_queue: - resets a crq after a failure - * @queue: crq_queue to initialize and register - * @hostdata: ibmvscsi_host_data of host - * - * no-op for iSeries - */ -static int iseriesvscsi_reset_crq_queue(struct crq_queue *queue, - struct ibmvscsi_host_data *hostdata) -{ - return 0; -} - -/** - * reenable_crq_queue: - reenables a crq after a failure - * @queue: crq_queue to initialize and register - * @hostdata: ibmvscsi_host_data of host - * - * no-op for iSeries - */ -static int iseriesvscsi_reenable_crq_queue(struct crq_queue *queue, - struct ibmvscsi_host_data *hostdata) -{ - return 0; -} - -/** - * iseriesvscsi_send_crq: - Send a CRQ - * @hostdata: the adapter - * @word1: the first 64 bits of the data - * @word2: the second 64 bits of the data - */ -static int iseriesvscsi_send_crq(struct ibmvscsi_host_data *hostdata, - u64 word1, u64 word2) -{ - single_host_data = hostdata; - return HvCallEvent_signalLpEventFast(viopath_hostLp, - HvLpEvent_Type_VirtualIo, - viomajorsubtype_scsi, - HvLpEvent_AckInd_NoAck, - HvLpEvent_AckType_ImmediateAck, - viopath_sourceinst(viopath_hostLp), - viopath_targetinst(viopath_hostLp), - 0, - VIOVERSION << 16, word1, word2, 0, - 0); -} - -struct ibmvscsi_ops iseriesvscsi_ops = { - .init_crq_queue = iseriesvscsi_init_crq_queue, - .release_crq_queue = iseriesvscsi_release_crq_queue, - .reset_crq_queue = iseriesvscsi_reset_crq_queue, - .reenable_crq_queue = iseriesvscsi_reenable_crq_queue, - .send_crq = iseriesvscsi_send_crq, -}; diff --git a/drivers/scsi/ibmvscsi/rpa_vscsi.c b/drivers/scsi/ibmvscsi/rpa_vscsi.c deleted file mode 100644 index 462a8574dad..00000000000 --- a/drivers/scsi/ibmvscsi/rpa_vscsi.c +++ /dev/null @@ -1,343 +0,0 @@ -/* ------------------------------------------------------------ - * rpa_vscsi.c - * (C) Copyright IBM Corporation 1994, 2003 - * Authors: Colin DeVilbiss (devilbis@us.ibm.com) - * Santiago Leon (santil@us.ibm.com) - * - * 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 - * - * ------------------------------------------------------------ - * RPA-specific functions of the SCSI host adapter for Virtual I/O devices - * - * This driver allows the Linux SCSI peripheral drivers to directly - * access devices in the hosting partition, either on an iSeries - * hypervisor system or a converged hypervisor system. - */ - -#include <asm/vio.h> -#include <asm/prom.h> -#include <asm/iommu.h> -#include <asm/hvcall.h> -#include <linux/dma-mapping.h> -#include <linux/interrupt.h> -#include "ibmvscsi.h" - -static char partition_name[97] = "UNKNOWN"; -static unsigned int partition_number = -1; - -/* ------------------------------------------------------------ - * Routines for managing the command/response queue - */ -/** - * rpavscsi_handle_event: - Interrupt handler for crq events - * @irq: number of irq to handle, not used - * @dev_instance: ibmvscsi_host_data of host that received interrupt - * - * Disables interrupts and schedules srp_task - * Always returns IRQ_HANDLED - */ -static irqreturn_t rpavscsi_handle_event(int irq, void *dev_instance) -{ - struct ibmvscsi_host_data *hostdata = - (struct ibmvscsi_host_data *)dev_instance; - vio_disable_interrupts(to_vio_dev(hostdata->dev)); - tasklet_schedule(&hostdata->srp_task); - return IRQ_HANDLED; -} - -/** - * release_crq_queue: - Deallocates data and unregisters CRQ - * @queue: crq_queue to initialize and register - * @host_data: ibmvscsi_host_data of host - * - * Frees irq, deallocates a page for messages, unmaps dma, and unregisters - * the crq with the hypervisor. - */ -static void rpavscsi_release_crq_queue(struct crq_queue *queue, - struct ibmvscsi_host_data *hostdata, - int max_requests) -{ - long rc; - struct vio_dev *vdev = to_vio_dev(hostdata->dev); - free_irq(vdev->irq, (void *)hostdata); - tasklet_kill(&hostdata->srp_task); - do { - rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address); - } while ((rc == H_BUSY) || (H_IS_LONG_BUSY(rc))); - dma_unmap_single(hostdata->dev, - queue->msg_token, - queue->size * sizeof(*queue->msgs), DMA_BIDIRECTIONAL); - free_page((unsigned long)queue->msgs); -} - -/** - * crq_queue_next_crq: - Returns the next entry in message queue - * @queue: crq_queue to use - * - * Returns pointer to next entry in queue, or NULL if there are no new - * entried in the CRQ. - */ -static struct viosrp_crq *crq_queue_next_crq(struct crq_queue *queue) -{ - struct viosrp_crq *crq; - unsigned long flags; - - spin_lock_irqsave(&queue->lock, flags); - crq = &queue->msgs[queue->cur]; - if (crq->valid & 0x80) { - if (++queue->cur == queue->size) - queue->cur = 0; - } else - crq = NULL; - spin_unlock_irqrestore(&queue->lock, flags); - - return crq; -} - -/** - * rpavscsi_send_crq: - Send a CRQ - * @hostdata: the adapter - * @word1: the first 64 bits of the data - * @word2: the second 64 bits of the data - */ -static int rpavscsi_send_crq(struct ibmvscsi_host_data *hostdata, - u64 word1, u64 word2) -{ - struct vio_dev *vdev = to_vio_dev(hostdata->dev); - - return plpar_hcall_norets(H_SEND_CRQ, vdev->unit_address, word1, word2); -} - -/** - * rpavscsi_task: - Process srps asynchronously - * @data: ibmvscsi_host_data of host - */ -static void rpavscsi_task(void *data) -{ - struct ibmvscsi_host_data *hostdata = (struct ibmvscsi_host_data *)data; - struct vio_dev *vdev = to_vio_dev(hostdata->dev); - struct viosrp_crq *crq; - int done = 0; - - while (!done) { - /* Pull all the valid messages off the CRQ */ - while ((crq = crq_queue_next_crq(&hostdata->queue)) != NULL) { - ibmvscsi_handle_crq(crq, hostdata); - crq->valid = 0x00; - } - - vio_enable_interrupts(vdev); - if ((crq = crq_queue_next_crq(&hostdata->queue)) != NULL) { - vio_disable_interrupts(vdev); - ibmvscsi_handle_crq(crq, hostdata); - crq->valid = 0x00; - } else { - done = 1; - } - } -} - -static void gather_partition_info(void) -{ - struct device_node *rootdn; - - const char *ppartition_name; - const unsigned int *p_number_ptr; - - /* Retrieve information about this partition */ - rootdn = of_find_node_by_path("/"); - if (!rootdn) { - return; - } - - ppartition_name = of_get_property(rootdn, "ibm,partition-name", NULL); - if (ppartition_name) - strncpy(partition_name, ppartition_name, - sizeof(partition_name)); - p_number_ptr = of_get_property(rootdn, "ibm,partition-no", NULL); - if (p_number_ptr) - partition_number = *p_number_ptr; - of_node_put(rootdn); -} - -static void set_adapter_info(struct ibmvscsi_host_data *hostdata) -{ - memset(&hostdata->madapter_info, 0x00, - sizeof(hostdata->madapter_info)); - - dev_info(hostdata->dev, "SRP_VERSION: %s\n", SRP_VERSION); - strcpy(hostdata->madapter_info.srp_version, SRP_VERSION); - - strncpy(hostdata->madapter_info.partition_name, partition_name, - sizeof(hostdata->madapter_info.partition_name)); - - hostdata->madapter_info.partition_number = partition_number; - - hostdata->madapter_info.mad_version = 1; - hostdata->madapter_info.os_type = 2; -} - -/** - * reset_crq_queue: - resets a crq after a failure - * @queue: crq_queue to initialize and register - * @hostdata: ibmvscsi_host_data of host - * - */ -static int rpavscsi_reset_crq_queue(struct crq_queue *queue, - struct ibmvscsi_host_data *hostdata) -{ - int rc; - struct vio_dev *vdev = to_vio_dev(hostdata->dev); - - /* Close the CRQ */ - do { - rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address); - } while ((rc == H_BUSY) || (H_IS_LONG_BUSY(rc))); - - /* Clean out the queue */ - memset(queue->msgs, 0x00, PAGE_SIZE); - queue->cur = 0; - - set_adapter_info(hostdata); - - /* And re-open it again */ - rc = plpar_hcall_norets(H_REG_CRQ, - vdev->unit_address, - queue->msg_token, PAGE_SIZE); - if (rc == 2) { - /* Adapter is good, but other end is not ready */ - dev_warn(hostdata->dev, "Partner adapter not ready\n"); - } else if (rc != 0) { - dev_warn(hostdata->dev, "couldn't register crq--rc 0x%x\n", rc); - } - return rc; -} - -/** - * initialize_crq_queue: - Initializes and registers CRQ with hypervisor - * @queue: crq_queue to initialize and register - * @hostdata: ibmvscsi_host_data of host - * - * Allocates a page for messages, maps it for dma, and registers - * the crq with the hypervisor. - * Returns zero on success. - */ -static int rpavscsi_init_crq_queue(struct crq_queue *queue, - struct ibmvscsi_host_data *hostdata, - int max_requests) -{ - int rc; - int retrc; - struct vio_dev *vdev = to_vio_dev(hostdata->dev); - - queue->msgs = (struct viosrp_crq *)get_zeroed_page(GFP_KERNEL); - - if (!queue->msgs) - goto malloc_failed; - queue->size = PAGE_SIZE / sizeof(*queue->msgs); - - queue->msg_token = dma_map_single(hostdata->dev, queue->msgs, - queue->size * sizeof(*queue->msgs), - DMA_BIDIRECTIONAL); - - if (dma_mapping_error(hostdata->dev, queue->msg_token)) - goto map_failed; - - gather_partition_info(); - set_adapter_info(hostdata); - - retrc = rc = plpar_hcall_norets(H_REG_CRQ, - vdev->unit_address, - queue->msg_token, PAGE_SIZE); - if (rc == H_RESOURCE) - /* maybe kexecing and resource is busy. try a reset */ - rc = rpavscsi_reset_crq_queue(queue, - hostdata); - - if (rc == 2) { - /* Adapter is good, but other end is not ready */ - dev_warn(hostdata->dev, "Partner adapter not ready\n"); - retrc = 0; - } else if (rc != 0) { - dev_warn(hostdata->dev, "Error %d opening adapter\n", rc); - goto reg_crq_failed; - } - - if (request_irq(vdev->irq, - rpavscsi_handle_event, - 0, "ibmvscsi", (void *)hostdata) != 0) { - dev_err(hostdata->dev, "couldn't register irq 0x%x\n", - vdev->irq); - goto req_irq_failed; - } - - rc = vio_enable_interrupts(vdev); - if (rc != 0) { - dev_err(hostdata->dev, "Error %d enabling interrupts!!!\n", rc); - goto req_irq_failed; - } - - queue->cur = 0; - spin_lock_init(&queue->lock); - - tasklet_init(&hostdata->srp_task, (void *)rpavscsi_task, - (unsigned long)hostdata); - - return retrc; - - req_irq_failed: - do { - rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address); - } while ((rc == H_BUSY) || (H_IS_LONG_BUSY(rc))); - reg_crq_failed: - dma_unmap_single(hostdata->dev, - queue->msg_token, - queue->size * sizeof(*queue->msgs), DMA_BIDIRECTIONAL); - map_failed: - free_page((unsigned long)queue->msgs); - malloc_failed: - return -1; -} - -/** - * reenable_crq_queue: - reenables a crq after - * @queue: crq_queue to initialize and register - * @hostdata: ibmvscsi_host_data of host - * - */ -static int rpavscsi_reenable_crq_queue(struct crq_queue *queue, - struct ibmvscsi_host_data *hostdata) -{ - int rc; - struct vio_dev *vdev = to_vio_dev(hostdata->dev); - - /* Re-enable the CRQ */ - do { - rc = plpar_hcall_norets(H_ENABLE_CRQ, vdev->unit_address); - } while ((rc == H_IN_PROGRESS) || (rc == H_BUSY) || (H_IS_LONG_BUSY(rc))); - - if (rc) - dev_err(hostdata->dev, "Error %d enabling adapter\n", rc); - return rc; -} - -struct ibmvscsi_ops rpavscsi_ops = { - .init_crq_queue = rpavscsi_init_crq_queue, - .release_crq_queue = rpavscsi_release_crq_queue, - .reset_crq_queue = rpavscsi_reset_crq_queue, - .reenable_crq_queue = rpavscsi_reenable_crq_queue, - .send_crq = rpavscsi_send_crq, -}; diff --git a/drivers/scsi/ibmvscsi/viosrp.h b/drivers/scsi/ibmvscsi/viosrp.h index 204604501ad..11624308762 100644 --- a/drivers/scsi/ibmvscsi/viosrp.h +++ b/drivers/scsi/ibmvscsi/viosrp.h @@ -37,6 +37,7 @@ #define SRP_VERSION "16.a" #define SRP_MAX_IU_LEN 256 +#define SRP_MAX_LOC_LEN 32 union srp_iu { struct srp_login_req login_req; @@ -74,9 +75,9 @@ struct viosrp_crq { u8 format; /* SCSI vs out-of-band */ u8 reserved; u8 status; /* non-scsi failure? (e.g. DMA failure) */ - u16 timeout; /* in seconds */ - u16 IU_length; /* in bytes */ - u64 IU_data_ptr; /* the TCE for transferring data */ + __be16 timeout; /* in seconds */ + __be16 IU_length; /* in bytes */ + __be64 IU_data_ptr; /* the TCE for transferring data */ }; /* MADs are Management requests above and beyond the IUs defined in the SRP @@ -86,17 +87,47 @@ enum viosrp_mad_types { VIOSRP_EMPTY_IU_TYPE = 0x01, VIOSRP_ERROR_LOG_TYPE = 0x02, VIOSRP_ADAPTER_INFO_TYPE = 0x03, - VIOSRP_HOST_CONFIG_TYPE = 0x04 + VIOSRP_HOST_CONFIG_TYPE = 0x04, + VIOSRP_CAPABILITIES_TYPE = 0x05, + VIOSRP_ENABLE_FAST_FAIL = 0x08, +}; + +enum viosrp_mad_status { + VIOSRP_MAD_SUCCESS = 0x00, + VIOSRP_MAD_NOT_SUPPORTED = 0xF1, + VIOSRP_MAD_FAILED = 0xF7, +}; + +enum viosrp_capability_type { + MIGRATION_CAPABILITIES = 0x01, + RESERVATION_CAPABILITIES = 0x02, +}; + +enum viosrp_capability_support { + SERVER_DOES_NOT_SUPPORTS_CAP = 0x0, + SERVER_SUPPORTS_CAP = 0x01, + SERVER_CAP_DATA = 0x02, +}; + +enum viosrp_reserve_type { + CLIENT_RESERVE_SCSI_2 = 0x01, +}; + +enum viosrp_capability_flag { + CLIENT_MIGRATED = 0x01, + CLIENT_RECONNECT = 0x02, + CAP_LIST_SUPPORTED = 0x04, + CAP_LIST_DATA = 0x08, }; /* * Common MAD header */ struct mad_common { - u32 type; - u16 status; - u16 length; - u64 tag; + __be32 type; + __be16 status; + __be16 length; + __be64 tag; }; /* @@ -108,23 +139,56 @@ struct mad_common { */ struct viosrp_empty_iu { struct mad_common common; - u64 buffer; - u32 port; + __be64 buffer; + __be32 port; }; struct viosrp_error_log { struct mad_common common; - u64 buffer; + __be64 buffer; }; struct viosrp_adapter_info { struct mad_common common; - u64 buffer; + __be64 buffer; }; struct viosrp_host_config { struct mad_common common; - u64 buffer; + __be64 buffer; +}; + +struct viosrp_fast_fail { + struct mad_common common; +}; + +struct viosrp_capabilities { + struct mad_common common; + __be64 buffer; +}; + +struct mad_capability_common { + __be32 cap_type; + __be16 length; + __be16 server_support; +}; + +struct mad_reserve_cap { + struct mad_capability_common common; + __be32 type; +}; + +struct mad_migration_cap { + struct mad_capability_common common; + __be32 ecl; +}; + +struct capabilities{ + __be32 flags; + char name[SRP_MAX_LOC_LEN]; + char loc[SRP_MAX_LOC_LEN]; + struct mad_migration_cap migration; + struct mad_reserve_cap reserve; }; union mad_iu { @@ -132,6 +196,8 @@ union mad_iu { struct viosrp_error_log error_log; struct viosrp_adapter_info adapter_info; struct viosrp_host_config host_config; + struct viosrp_fast_fail fast_fail; + struct viosrp_capabilities capabilities; }; union viosrp_iu { @@ -142,10 +208,10 @@ union viosrp_iu { struct mad_adapter_info_data { char srp_version[8]; char partition_name[96]; - u32 partition_number; - u32 mad_version; - u32 os_type; - u32 port_max_txu[8]; /* per-port maximum transfer */ + __be32 partition_number; + __be32 mad_version; + __be32 os_type; + __be32 port_max_txu[8]; /* per-port maximum transfer */ }; #endif |
