#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/dma-mapping.h>
+#include <linux/delay.h>
#include <asm/vio.h>
#include <scsi/scsi.h>
#include <scsi/scsi_cmnd.h>
static int init_timeout = 5;
static int max_requests = 50;
-#define IBMVSCSI_VERSION "1.5.1"
+#define IBMVSCSI_VERSION "1.5.8"
MODULE_DESCRIPTION("IBM Virtual SCSI");
MODULE_AUTHOR("Dave Boutcher");
pool->size = size;
pool->next = 0;
- pool->events = kmalloc(pool->size * sizeof(*pool->events), GFP_KERNEL);
+ pool->events = kcalloc(pool->size, sizeof(*pool->events), GFP_KERNEL);
if (!pool->events)
return -ENOMEM;
- memset(pool->events, 0x00, pool->size * sizeof(*pool->events));
pool->iu_storage =
dma_alloc_coherent(hostdata->dev,
sizeof(*evt->xfer_iu) * i;
evt->xfer_iu = pool->iu_storage + i;
evt->hostdata = hostdata;
+ evt->ext_list = NULL;
+ evt->ext_list_token = 0;
}
return 0;
struct ibmvscsi_host_data *hostdata)
{
int i, in_use = 0;
- for (i = 0; i < pool->size; ++i)
+ for (i = 0; i < pool->size; ++i) {
if (atomic_read(&pool->events[i].free) != 1)
++in_use;
+ if (pool->events[i].ext_list) {
+ dma_free_coherent(hostdata->dev,
+ SG_ALL * sizeof(struct srp_direct_buf),
+ pool->events[i].ext_list,
+ pool->events[i].ext_list_token);
+ }
+ }
if (in_use)
printk(KERN_WARNING
"ibmvscsi: releasing event pool with %d "
{
evt_struct->cmnd = NULL;
evt_struct->cmnd_done = NULL;
+ evt_struct->sync_srp = NULL;
evt_struct->crq.format = format;
evt_struct->crq.timeout = timeout;
evt_struct->done = done;
struct srp_cmd *srp_cmd,
int numbuf)
{
+ u8 fmt;
+
if (numbuf == 0)
return;
- if (numbuf == 1) {
+ if (numbuf == 1)
+ fmt = SRP_DATA_DESC_DIRECT;
+ else {
+ fmt = SRP_DATA_DESC_INDIRECT;
+ numbuf = min(numbuf, MAX_INDIRECT_BUFS);
+
if (cmd->sc_data_direction == DMA_TO_DEVICE)
- srp_cmd->data_out_format = SRP_DIRECT_BUFFER;
- else
- srp_cmd->data_in_format = SRP_DIRECT_BUFFER;
- } else {
- if (cmd->sc_data_direction == DMA_TO_DEVICE) {
- srp_cmd->data_out_format = SRP_INDIRECT_BUFFER;
- srp_cmd->data_out_count = numbuf;
- } else {
- srp_cmd->data_in_format = SRP_INDIRECT_BUFFER;
- srp_cmd->data_in_count = numbuf;
- }
+ srp_cmd->data_out_desc_cnt = numbuf;
+ else
+ srp_cmd->data_in_desc_cnt = numbuf;
}
+
+ if (cmd->sc_data_direction == DMA_TO_DEVICE)
+ srp_cmd->buf_fmt = fmt << 4;
+ else
+ 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);
}
/**
* @dev: device for which the memory is mapped
*
*/
-static void unmap_cmd_data(struct srp_cmd *cmd, struct device *dev)
+static void unmap_cmd_data(struct srp_cmd *cmd,
+ struct srp_event_struct *evt_struct,
+ struct device *dev)
{
- int i;
+ u8 out_fmt, in_fmt;
+
+ out_fmt = cmd->buf_fmt >> 4;
+ in_fmt = cmd->buf_fmt & ((1U << 4) - 1);
- if ((cmd->data_out_format == SRP_NO_BUFFER) &&
- (cmd->data_in_format == SRP_NO_BUFFER))
+ if (out_fmt == SRP_NO_DATA_DESC && in_fmt == SRP_NO_DATA_DESC)
return;
- else if ((cmd->data_out_format == SRP_DIRECT_BUFFER) ||
- (cmd->data_in_format == SRP_DIRECT_BUFFER)) {
- struct memory_descriptor *data =
- (struct memory_descriptor *)cmd->additional_data;
- dma_unmap_single(dev, data->virtual_address, data->length,
- DMA_BIDIRECTIONAL);
+ 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 indirect_descriptor *indirect =
- (struct indirect_descriptor *)cmd->additional_data;
- int num_mapped = indirect->head.length /
- sizeof(indirect->list[0]);
- for (i = 0; i < num_mapped; ++i) {
- struct memory_descriptor *data = &indirect->list[i];
- dma_unmap_single(dev,
- data->virtual_address,
- data->length, DMA_BIDIRECTIONAL);
+ 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);
}
}
+static int map_sg_list(int num_entries,
+ struct scatterlist *sg,
+ struct srp_direct_buf *md)
+{
+ int i;
+ u64 total_length = 0;
+
+ for (i = 0; i < num_entries; ++i) {
+ struct srp_direct_buf *descr = md + i;
+ struct scatterlist *sg_entry = &sg[i];
+ descr->va = sg_dma_address(sg_entry);
+ descr->len = sg_dma_len(sg_entry);
+ descr->key = 0;
+ total_length += sg_dma_len(sg_entry);
+ }
+ return total_length;
+}
+
/**
* map_sg_data: - Maps dma for a scatterlist and initializes decriptor fields
* @cmd: Scsi_Cmnd with the scatterlist
* Returns 1 on success.
*/
static int map_sg_data(struct scsi_cmnd *cmd,
+ struct srp_event_struct *evt_struct,
struct srp_cmd *srp_cmd, struct device *dev)
{
- int i, sg_mapped;
+ int sg_mapped;
u64 total_length = 0;
struct scatterlist *sg = cmd->request_buffer;
- struct memory_descriptor *data =
- (struct memory_descriptor *)srp_cmd->additional_data;
- struct indirect_descriptor *indirect =
- (struct indirect_descriptor *)data;
+ struct srp_direct_buf *data =
+ (struct srp_direct_buf *) srp_cmd->add_data;
+ struct srp_indirect_buf *indirect =
+ (struct srp_indirect_buf *) data;
sg_mapped = dma_map_sg(dev, sg, cmd->use_sg, DMA_BIDIRECTIONAL);
/* special case; we can use a single direct descriptor */
if (sg_mapped == 1) {
- data->virtual_address = sg_dma_address(&sg[0]);
- data->length = sg_dma_len(&sg[0]);
- data->memory_handle = 0;
+ data->va = sg_dma_address(&sg[0]);
+ data->len = sg_dma_len(&sg[0]);
+ data->key = 0;
return 1;
}
- if (sg_mapped > MAX_INDIRECT_BUFS) {
+ if (sg_mapped > SG_ALL) {
printk(KERN_ERR
"ibmvscsi: More than %d mapped sg entries, got %d\n",
- MAX_INDIRECT_BUFS, sg_mapped);
+ SG_ALL, sg_mapped);
return 0;
}
- indirect->head.virtual_address = 0;
- indirect->head.length = sg_mapped * sizeof(indirect->list[0]);
- indirect->head.memory_handle = 0;
- for (i = 0; i < sg_mapped; ++i) {
- struct memory_descriptor *descr = &indirect->list[i];
- struct scatterlist *sg_entry = &sg[i];
- descr->virtual_address = sg_dma_address(sg_entry);
- descr->length = sg_dma_len(sg_entry);
- descr->memory_handle = 0;
- total_length += sg_dma_len(sg_entry);
+ indirect->table_desc.va = 0;
+ indirect->table_desc.len = sg_mapped * sizeof(struct srp_direct_buf);
+ indirect->table_desc.key = 0;
+
+ if (sg_mapped <= MAX_INDIRECT_BUFS) {
+ total_length = map_sg_list(sg_mapped, sg,
+ &indirect->desc_list[0]);
+ indirect->len = total_length;
+ return 1;
}
- indirect->total_length = total_length;
- return 1;
+ /* get indirect table */
+ if (!evt_struct->ext_list) {
+ evt_struct->ext_list = (struct srp_direct_buf *)
+ dma_alloc_coherent(dev,
+ SG_ALL * sizeof(struct srp_direct_buf),
+ &evt_struct->ext_list_token, 0);
+ if (!evt_struct->ext_list) {
+ printk(KERN_ERR
+ "ibmvscsi: Can't allocate memory for indirect table\n");
+ return 0;
+
+ }
+ }
+
+ total_length = map_sg_list(sg_mapped, sg, 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]);
+ memcpy(indirect->desc_list, evt_struct->ext_list,
+ MAX_INDIRECT_BUFS * sizeof(struct srp_direct_buf));
+
+ return 1;
}
/**
static int map_single_data(struct scsi_cmnd *cmd,
struct srp_cmd *srp_cmd, struct device *dev)
{
- struct memory_descriptor *data =
- (struct memory_descriptor *)srp_cmd->additional_data;
+ struct srp_direct_buf *data =
+ (struct srp_direct_buf *) srp_cmd->add_data;
- data->virtual_address =
+ data->va =
dma_map_single(dev, cmd->request_buffer,
cmd->request_bufflen,
DMA_BIDIRECTIONAL);
- if (dma_mapping_error(data->virtual_address)) {
+ if (dma_mapping_error(data->va)) {
printk(KERN_ERR
"ibmvscsi: Unable to map request_buffer for command!\n");
return 0;
}
- data->length = cmd->request_bufflen;
- data->memory_handle = 0;
+ data->len = cmd->request_bufflen;
+ data->key = 0;
set_srp_direction(cmd, srp_cmd, 1);
* Returns 1 on success.
*/
static int map_data_for_srp_cmd(struct scsi_cmnd *cmd,
+ struct srp_event_struct *evt_struct,
struct srp_cmd *srp_cmd, struct device *dev)
{
switch (cmd->sc_data_direction) {
if (!cmd->request_buffer)
return 1;
if (cmd->use_sg)
- return map_sg_data(cmd, srp_cmd, dev);
+ return map_sg_data(cmd, evt_struct, srp_cmd, dev);
return map_single_data(cmd, srp_cmd, dev);
}
static int ibmvscsi_send_srp_event(struct srp_event_struct *evt_struct,
struct ibmvscsi_host_data *hostdata)
{
- struct scsi_cmnd *cmnd = evt_struct->cmnd;
u64 *crq_as_u64 = (u64 *) &evt_struct->crq;
+ int request_status;
int rc;
/* If we have exhausted our request limit, just fail this request.
* (such as task management requests) that the mid layer may think we
* can handle more requests (can_queue) when we actually can't
*/
- if ((evt_struct->crq.format == VIOSRP_SRP_FORMAT) &&
- (atomic_dec_if_positive(&hostdata->request_limit) < 0)) {
- /* See if the adapter is disabled */
- if (atomic_read(&hostdata->request_limit) < 0) {
- if (cmnd)
- cmnd->result = DID_ERROR << 16;
- if (evt_struct->cmnd_done)
- evt_struct->cmnd_done(cmnd);
- unmap_cmd_data(&evt_struct->iu.srp.cmd,
- hostdata->dev);
- free_event_struct(&hostdata->pool, evt_struct);
- return 0;
- } else {
- printk("ibmvscsi: Warning, request_limit exceeded\n");
- unmap_cmd_data(&evt_struct->iu.srp.cmd,
- hostdata->dev);
- free_event_struct(&hostdata->pool, evt_struct);
- return SCSI_MLQUEUE_HOST_BUSY;
- }
+ if (evt_struct->crq.format == VIOSRP_SRP_FORMAT) {
+ request_status =
+ atomic_dec_if_positive(&hostdata->request_limit);
+ /* If request limit was -1 when we started, it is now even
+ * less than that
+ */
+ if (request_status < -1)
+ goto send_error;
+ /* Otherwise, if we have run out of requests */
+ else if (request_status < 0)
+ goto send_busy;
}
/* Copy the IU into the transfer area */
*evt_struct->xfer_iu = evt_struct->iu;
- evt_struct->xfer_iu->srp.generic.tag = (u64)evt_struct;
+ evt_struct->xfer_iu->srp.rsp.tag = (u64)evt_struct;
/* Add this to the sent list. We need to do this
* before we actually send
ibmvscsi_send_crq(hostdata, crq_as_u64[0], crq_as_u64[1])) != 0) {
list_del(&evt_struct->list);
- cmnd = evt_struct->cmnd;
- printk(KERN_ERR "ibmvscsi: failed to send event struct rc %d\n",
+ printk(KERN_ERR "ibmvscsi: send error %d\n",
rc);
- unmap_cmd_data(&evt_struct->iu.srp.cmd, hostdata->dev);
- free_event_struct(&hostdata->pool, evt_struct);
- if (cmnd)
- cmnd->result = DID_ERROR << 16;
- if (evt_struct->cmnd_done)
- evt_struct->cmnd_done(cmnd);
+ goto send_error;
}
return 0;
+
+ send_busy:
+ unmap_cmd_data(&evt_struct->iu.srp.cmd, evt_struct, hostdata->dev);
+
+ free_event_struct(&hostdata->pool, evt_struct);
+ return SCSI_MLQUEUE_HOST_BUSY;
+
+ send_error:
+ unmap_cmd_data(&evt_struct->iu.srp.cmd, evt_struct, hostdata->dev);
+
+ if (evt_struct->cmnd != NULL) {
+ evt_struct->cmnd->result = DID_ERROR << 16;
+ evt_struct->cmnd_done(evt_struct->cmnd);
+ } else if (evt_struct->done)
+ evt_struct->done(evt_struct);
+
+ free_event_struct(&hostdata->pool, evt_struct);
+ return 0;
}
/**
struct srp_rsp *rsp = &evt_struct->xfer_iu->srp.rsp;
struct scsi_cmnd *cmnd = evt_struct->cmnd;
+ if (unlikely(rsp->opcode != SRP_RSP)) {
+ if (printk_ratelimit())
+ printk(KERN_WARNING
+ "ibmvscsi: bad SRP RSP type %d\n",
+ rsp->opcode);
+ }
+
if (cmnd) {
cmnd->result = rsp->status;
if (((cmnd->result >> 1) & 0x1f) == CHECK_CONDITION)
memcpy(cmnd->sense_buffer,
- rsp->sense_and_response_data,
- rsp->sense_data_list_length);
+ rsp->data,
+ rsp->sense_data_len);
unmap_cmd_data(&evt_struct->iu.srp.cmd,
+ evt_struct,
evt_struct->hostdata->dev);
- if (rsp->doover)
- cmnd->resid = rsp->data_out_residual_count;
- else if (rsp->diover)
- cmnd->resid = rsp->data_in_residual_count;
+ if (rsp->flags & SRP_RSP_FLAG_DOOVER)
+ cmnd->resid = rsp->data_out_res_cnt;
+ else if (rsp->flags & SRP_RSP_FLAG_DIOVER)
+ cmnd->resid = rsp->data_in_res_cnt;
}
if (evt_struct->cmnd_done)
{
struct srp_cmd *srp_cmd;
struct srp_event_struct *evt_struct;
+ struct srp_indirect_buf *indirect;
struct ibmvscsi_host_data *hostdata =
(struct ibmvscsi_host_data *)&cmnd->device->host->hostdata;
u16 lun = lun_from_dev(cmnd->device);
+ u8 out_fmt, in_fmt;
evt_struct = get_event_struct(&hostdata->pool);
if (!evt_struct)
return SCSI_MLQUEUE_HOST_BUSY;
- init_event_struct(evt_struct,
- handle_cmd_rsp,
- VIOSRP_SRP_FORMAT,
- cmnd->timeout);
-
- evt_struct->cmnd = cmnd;
- evt_struct->cmnd_done = done;
-
/* Set up the actual SRP IU */
srp_cmd = &evt_struct->iu.srp.cmd;
- memset(srp_cmd, 0x00, sizeof(*srp_cmd));
- srp_cmd->type = SRP_CMD_TYPE;
+ memset(srp_cmd, 0x00, SRP_MAX_IU_LEN);
+ srp_cmd->opcode = SRP_CMD;
memcpy(srp_cmd->cdb, cmnd->cmnd, sizeof(cmnd->cmnd));
srp_cmd->lun = ((u64) lun) << 48;
- if (!map_data_for_srp_cmd(cmnd, srp_cmd, hostdata->dev)) {
+ if (!map_data_for_srp_cmd(cmnd, evt_struct, srp_cmd, hostdata->dev)) {
printk(KERN_ERR "ibmvscsi: couldn't convert cmd to srp_cmd\n");
free_event_struct(&hostdata->pool, evt_struct);
return SCSI_MLQUEUE_HOST_BUSY;
}
+ init_event_struct(evt_struct,
+ handle_cmd_rsp,
+ VIOSRP_SRP_FORMAT,
+ cmnd->timeout_per_command/HZ);
+
+ evt_struct->cmnd = cmnd;
+ evt_struct->cmnd_done = done;
+
/* Fix up dma address of the buffer itself */
- if ((srp_cmd->data_out_format == SRP_INDIRECT_BUFFER) ||
- (srp_cmd->data_in_format == SRP_INDIRECT_BUFFER)) {
- struct indirect_descriptor *indirect =
- (struct indirect_descriptor *)srp_cmd->additional_data;
- indirect->head.virtual_address = evt_struct->crq.IU_data_ptr +
- offsetof(struct srp_cmd, additional_data) +
- offsetof(struct indirect_descriptor, list);
+ indirect = (struct srp_indirect_buf *) srp_cmd->add_data;
+ out_fmt = srp_cmd->buf_fmt >> 4;
+ in_fmt = srp_cmd->buf_fmt & ((1U << 4) - 1);
+ 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 +
+ offsetof(struct srp_cmd, add_data) +
+ offsetof(struct srp_indirect_buf, desc_list);
}
return ibmvscsi_send_srp_event(evt_struct, hostdata);
evt_struct->xfer_iu->mad.adapter_info.common.status);
} else {
printk("ibmvscsi: host srp version: %s, "
- "host partition %s (%d), OS %d\n",
+ "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.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) {
+ printk("ibmvscsi: host (Ver. %s) doesn't support large"
+ "transfers\n",
+ hostdata->madapter_info.srp_version);
+ printk("ibmvscsi: limiting scatterlists to %d\n",
+ MAX_INDIRECT_BUFS);
+ hostdata->host->sg_tablesize = MAX_INDIRECT_BUFS;
+ }
}
}
{
struct viosrp_adapter_info *req;
struct srp_event_struct *evt_struct;
-
- memset(&hostdata->madapter_info, 0x00, sizeof(hostdata->madapter_info));
-
+ dma_addr_t addr;
+
evt_struct = get_event_struct(&hostdata->pool);
if (!evt_struct) {
printk(KERN_ERR "ibmvscsi: couldn't allocate an event "
req->common.type = VIOSRP_ADAPTER_INFO_TYPE;
req->common.length = sizeof(hostdata->madapter_info);
- req->buffer = dma_map_single(hostdata->dev,
- &hostdata->madapter_info,
- sizeof(hostdata->madapter_info),
- DMA_BIDIRECTIONAL);
+ req->buffer = addr = dma_map_single(hostdata->dev,
+ &hostdata->madapter_info,
+ sizeof(hostdata->madapter_info),
+ DMA_BIDIRECTIONAL);
if (dma_mapping_error(req->buffer)) {
printk(KERN_ERR
return;
}
- if (ibmvscsi_send_srp_event(evt_struct, hostdata))
+ if (ibmvscsi_send_srp_event(evt_struct, hostdata)) {
printk(KERN_ERR "ibmvscsi: couldn't send ADAPTER_INFO_REQ!\n");
+ dma_unmap_single(hostdata->dev,
+ addr,
+ sizeof(hostdata->madapter_info),
+ DMA_BIDIRECTIONAL);
+ }
};
/**
static void login_rsp(struct srp_event_struct *evt_struct)
{
struct ibmvscsi_host_data *hostdata = evt_struct->hostdata;
- switch (evt_struct->xfer_iu->srp.generic.type) {
- case SRP_LOGIN_RSP_TYPE: /* it worked! */
+ switch (evt_struct->xfer_iu->srp.login_rsp.opcode) {
+ case SRP_LOGIN_RSP: /* it worked! */
break;
- case SRP_LOGIN_REJ_TYPE: /* refused! */
- printk(KERN_INFO "ibmvscsi: SRP_LOGIN_REQ rejected\n");
+ case SRP_LOGIN_REJ: /* refused! */
+ printk(KERN_INFO "ibmvscsi: SRP_LOGIN_REJ reason %u\n",
+ evt_struct->xfer_iu->srp.login_rej.reason);
/* Login failed. */
atomic_set(&hostdata->request_limit, -1);
return;
default:
printk(KERN_ERR
"ibmvscsi: Invalid login response typecode 0x%02x!\n",
- evt_struct->xfer_iu->srp.generic.type);
+ evt_struct->xfer_iu->srp.login_rsp.opcode);
/* Login failed. */
atomic_set(&hostdata->request_limit, -1);
return;
printk(KERN_INFO "ibmvscsi: SRP_LOGIN succeeded\n");
- if (evt_struct->xfer_iu->srp.login_rsp.request_limit_delta >
+ if (evt_struct->xfer_iu->srp.login_rsp.req_lim_delta >
(max_requests - 2))
- evt_struct->xfer_iu->srp.login_rsp.request_limit_delta =
+ evt_struct->xfer_iu->srp.login_rsp.req_lim_delta =
max_requests - 2;
/* Now we know what the real request-limit is */
atomic_set(&hostdata->request_limit,
- evt_struct->xfer_iu->srp.login_rsp.request_limit_delta);
+ evt_struct->xfer_iu->srp.login_rsp.req_lim_delta);
hostdata->host->can_queue =
- evt_struct->xfer_iu->srp.login_rsp.request_limit_delta - 2;
+ evt_struct->xfer_iu->srp.login_rsp.req_lim_delta - 2;
if (hostdata->host->can_queue < 1) {
printk(KERN_ERR "ibmvscsi: Invalid request_limit_delta\n");
return;
}
+ /* If we had any pending I/Os, kick them */
+ scsi_unblock_requests(hostdata->host);
+
send_mad_adapter_info(hostdata);
return;
}
init_timeout * HZ);
login = &evt_struct->iu.srp.login_req;
- login->type = SRP_LOGIN_REQ_TYPE;
- login->max_requested_initiator_to_target_iulen = sizeof(union srp_iu);
- login->required_buffer_formats = 0x0006;
+ memset(login, 0x00, sizeof(struct srp_login_req));
+ 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;
+ spin_lock_irqsave(hostdata->host->host_lock, flags);
/* Start out with a request limit of 1, since this is negotiated in
* the login request we are just sending
*/
atomic_set(&hostdata->request_limit, 1);
- spin_lock_irqsave(hostdata->host->host_lock, flags);
rc = ibmvscsi_send_srp_event(evt_struct, hostdata);
spin_unlock_irqrestore(hostdata->host->host_lock, flags);
+ printk("ibmvscsic: sent SRP login\n");
return rc;
};
*/
static void sync_completion(struct srp_event_struct *evt_struct)
{
+ /* copy the response back */
+ if (evt_struct->sync_srp)
+ *evt_struct->sync_srp = *evt_struct->xfer_iu;
+
complete(&evt_struct->comp);
}
struct srp_tsk_mgmt *tsk_mgmt;
struct srp_event_struct *evt;
struct srp_event_struct *tmp_evt, *found_evt;
+ union viosrp_iu srp_rsp;
+ int rsp_rc;
+ unsigned long flags;
u16 lun = lun_from_dev(cmd->device);
/* First, find this command in our sent list so we can figure
* out the correct tag
*/
+ spin_lock_irqsave(hostdata->host->host_lock, flags);
found_evt = NULL;
list_for_each_entry(tmp_evt, &hostdata->sent, list) {
if (tmp_evt->cmnd == cmd) {
}
}
- if (!found_evt)
+ if (!found_evt) {
+ spin_unlock_irqrestore(hostdata->host->host_lock, flags);
return FAILED;
+ }
evt = get_event_struct(&hostdata->pool);
if (evt == NULL) {
+ spin_unlock_irqrestore(hostdata->host->host_lock, flags);
printk(KERN_ERR "ibmvscsi: failed to allocate abort event\n");
return FAILED;
}
/* Set up an abort SRP command */
memset(tsk_mgmt, 0x00, sizeof(*tsk_mgmt));
- tsk_mgmt->type = SRP_TSK_MGMT_TYPE;
+ tsk_mgmt->opcode = SRP_TSK_MGMT;
tsk_mgmt->lun = ((u64) lun) << 48;
- tsk_mgmt->task_mgmt_flags = 0x01; /* ABORT TASK */
- tsk_mgmt->managed_task_tag = (u64) found_evt;
+ tsk_mgmt->tsk_mgmt_func = SRP_TSK_ABORT_TASK;
+ tsk_mgmt->task_tag = (u64) found_evt;
printk(KERN_INFO "ibmvscsi: aborting command. lun 0x%lx, tag 0x%lx\n",
- tsk_mgmt->lun, tsk_mgmt->managed_task_tag);
+ tsk_mgmt->lun, tsk_mgmt->task_tag);
+ evt->sync_srp = &srp_rsp;
init_completion(&evt->comp);
- if (ibmvscsi_send_srp_event(evt, hostdata) != 0) {
+ rsp_rc = ibmvscsi_send_srp_event(evt, hostdata);
+ spin_unlock_irqrestore(hostdata->host->host_lock, flags);
+ if (rsp_rc != 0) {
printk(KERN_ERR "ibmvscsi: failed to send abort() event\n");
return FAILED;
}
- spin_unlock_irq(hostdata->host->host_lock);
wait_for_completion(&evt->comp);
- spin_lock_irq(hostdata->host->host_lock);
+
+ /* make sure we got a good response */
+ if (unlikely(srp_rsp.srp.rsp.opcode != SRP_RSP)) {
+ if (printk_ratelimit())
+ printk(KERN_WARNING
+ "ibmvscsi: abort bad SRP RSP type %d\n",
+ srp_rsp.srp.rsp.opcode);
+ return FAILED;
+ }
+
+ if (srp_rsp.srp.rsp.flags & SRP_RSP_FLAG_RSPVALID)
+ rsp_rc = *((int *)srp_rsp.srp.rsp.data);
+ else
+ rsp_rc = srp_rsp.srp.rsp.status;
+
+ if (rsp_rc) {
+ if (printk_ratelimit())
+ printk(KERN_WARNING
+ "ibmvscsi: abort code %d for task tag 0x%lx\n",
+ rsp_rc,
+ tsk_mgmt->task_tag);
+ return FAILED;
+ }
/* Because we dropped the spinlock above, it's possible
* The event is no longer in our list. Make sure it didn't
* complete while we were aborting
*/
+ spin_lock_irqsave(hostdata->host->host_lock, flags);
found_evt = NULL;
list_for_each_entry(tmp_evt, &hostdata->sent, list) {
if (tmp_evt->cmnd == cmd) {
}
}
+ if (found_evt == NULL) {
+ spin_unlock_irqrestore(hostdata->host->host_lock, flags);
+ printk(KERN_INFO
+ "ibmvscsi: aborted task tag 0x%lx completed\n",
+ tsk_mgmt->task_tag);
+ return SUCCESS;
+ }
+
printk(KERN_INFO
"ibmvscsi: successfully aborted task tag 0x%lx\n",
- tsk_mgmt->managed_task_tag);
-
- if (found_evt == NULL)
- return SUCCESS;
+ tsk_mgmt->task_tag);
cmd->result = (DID_ABORT << 16);
list_del(&found_evt->list);
- unmap_cmd_data(&found_evt->iu.srp.cmd, found_evt->hostdata->dev);
+ unmap_cmd_data(&found_evt->iu.srp.cmd, found_evt,
+ found_evt->hostdata->dev);
free_event_struct(&found_evt->hostdata->pool, found_evt);
+ spin_unlock_irqrestore(hostdata->host->host_lock, flags);
atomic_inc(&hostdata->request_limit);
return SUCCESS;
}
struct srp_tsk_mgmt *tsk_mgmt;
struct srp_event_struct *evt;
struct srp_event_struct *tmp_evt, *pos;
+ union viosrp_iu srp_rsp;
+ int rsp_rc;
+ unsigned long flags;
u16 lun = lun_from_dev(cmd->device);
+ spin_lock_irqsave(hostdata->host->host_lock, flags);
evt = get_event_struct(&hostdata->pool);
if (evt == NULL) {
+ spin_unlock_irqrestore(hostdata->host->host_lock, flags);
printk(KERN_ERR "ibmvscsi: failed to allocate reset event\n");
return FAILED;
}
/* Set up a lun reset SRP command */
memset(tsk_mgmt, 0x00, sizeof(*tsk_mgmt));
- tsk_mgmt->type = SRP_TSK_MGMT_TYPE;
+ tsk_mgmt->opcode = SRP_TSK_MGMT;
tsk_mgmt->lun = ((u64) lun) << 48;
- tsk_mgmt->task_mgmt_flags = 0x08; /* LUN RESET */
+ tsk_mgmt->tsk_mgmt_func = SRP_TSK_LUN_RESET;
printk(KERN_INFO "ibmvscsi: resetting device. lun 0x%lx\n",
tsk_mgmt->lun);
+ evt->sync_srp = &srp_rsp;
init_completion(&evt->comp);
- if (ibmvscsi_send_srp_event(evt, hostdata) != 0) {
+ rsp_rc = ibmvscsi_send_srp_event(evt, hostdata);
+ spin_unlock_irqrestore(hostdata->host->host_lock, flags);
+ if (rsp_rc != 0) {
printk(KERN_ERR "ibmvscsi: failed to send reset event\n");
return FAILED;
}
- spin_unlock_irq(hostdata->host->host_lock);
wait_for_completion(&evt->comp);
- spin_lock_irq(hostdata->host->host_lock);
+
+ /* make sure we got a good response */
+ if (unlikely(srp_rsp.srp.rsp.opcode != SRP_RSP)) {
+ if (printk_ratelimit())
+ printk(KERN_WARNING
+ "ibmvscsi: reset bad SRP RSP type %d\n",
+ srp_rsp.srp.rsp.opcode);
+ return FAILED;
+ }
+
+ if (srp_rsp.srp.rsp.flags & SRP_RSP_FLAG_RSPVALID)
+ rsp_rc = *((int *)srp_rsp.srp.rsp.data);
+ else
+ rsp_rc = srp_rsp.srp.rsp.status;
+
+ if (rsp_rc) {
+ if (printk_ratelimit())
+ printk(KERN_WARNING
+ "ibmvscsi: reset code %d for task tag 0x%lx\n",
+ rsp_rc, tsk_mgmt->task_tag);
+ return FAILED;
+ }
/* We need to find all commands for this LUN that have not yet been
* responded to, and fail them with DID_RESET
*/
+ spin_lock_irqsave(hostdata->host->host_lock, flags);
list_for_each_entry_safe(tmp_evt, pos, &hostdata->sent, list) {
if ((tmp_evt->cmnd) && (tmp_evt->cmnd->device == cmd->device)) {
if (tmp_evt->cmnd)
tmp_evt->cmnd->result = (DID_RESET << 16);
list_del(&tmp_evt->list);
- unmap_cmd_data(&tmp_evt->iu.srp.cmd, tmp_evt->hostdata->dev);
+ unmap_cmd_data(&tmp_evt->iu.srp.cmd, tmp_evt,
+ tmp_evt->hostdata->dev);
free_event_struct(&tmp_evt->hostdata->pool,
tmp_evt);
atomic_inc(&hostdata->request_limit);
tmp_evt->done(tmp_evt);
}
}
+ spin_unlock_irqrestore(hostdata->host->host_lock, flags);
return SUCCESS;
}
* purge_requests: Our virtual adapter just shut down. purge any sent requests
* @hostdata: the adapter
*/
-static void purge_requests(struct ibmvscsi_host_data *hostdata)
+static void purge_requests(struct ibmvscsi_host_data *hostdata, int error_code)
{
struct srp_event_struct *tmp_evt, *pos;
unsigned long flags;
list_for_each_entry_safe(tmp_evt, pos, &hostdata->sent, list) {
list_del(&tmp_evt->list);
if (tmp_evt->cmnd) {
- tmp_evt->cmnd->result = (DID_ERROR << 16);
+ 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);
printk(KERN_ERR "ibmvscsi: unknown crq message type\n");
}
return;
- case 0xFF: /* Hypervisor telling us the connection is closed */
- printk(KERN_INFO "ibmvscsi: Virtual adapter failed!\n");
-
- atomic_set(&hostdata->request_limit, -1);
- purge_requests(hostdata);
- ibmvscsi_reset_crq_queue(&hostdata->queue, hostdata);
+ case 0xFF: /* Hypervisor telling us the connection is closed */
+ scsi_block_requests(hostdata->host);
+ atomic_set(&hostdata->request_limit, 0);
+ if (crq->format == 0x06) {
+ /* We need to re-setup the interpartition connection */
+ printk(KERN_INFO
+ "ibmvscsi: Re-enabling adapter!\n");
+ purge_requests(hostdata, DID_REQUEUE);
+ if ((ibmvscsi_reenable_crq_queue(&hostdata->queue,
+ hostdata)) ||
+ (ibmvscsi_send_crq(hostdata,
+ 0xC001000000000000LL, 0))) {
+ atomic_set(&hostdata->request_limit,
+ -1);
+ printk(KERN_ERR
+ "ibmvscsi: error after"
+ " enable\n");
+ }
+ } else {
+ printk(KERN_INFO
+ "ibmvscsi: Virtual adapter failed rc %d!\n",
+ crq->format);
+
+ purge_requests(hostdata, DID_ERROR);
+ if ((ibmvscsi_reset_crq_queue(&hostdata->queue,
+ hostdata)) ||
+ (ibmvscsi_send_crq(hostdata,
+ 0xC001000000000000LL, 0))) {
+ atomic_set(&hostdata->request_limit,
+ -1);
+ printk(KERN_ERR
+ "ibmvscsi: error after reset\n");
+ }
+ }
+ scsi_unblock_requests(hostdata->host);
return;
case 0x80: /* real payload */
break;
return;
}
+ if (atomic_read(&evt_struct->free)) {
+ printk(KERN_ERR
+ "ibmvscsi: received duplicate correlation_token 0x%p!\n",
+ (void *)crq->IU_data_ptr);
+ return;
+ }
+
if (crq->format == VIOSRP_SRP_FORMAT)
- atomic_add(evt_struct->xfer_iu->srp.rsp.request_limit_delta,
+ atomic_add(evt_struct->xfer_iu->srp.rsp.req_lim_delta,
&hostdata->request_limit);
if (evt_struct->done)
{
struct viosrp_host_config *host_config;
struct srp_event_struct *evt_struct;
+ dma_addr_t addr;
int rc;
evt_struct = get_event_struct(&hostdata->pool);
memset(host_config, 0x00, sizeof(*host_config));
host_config->common.type = VIOSRP_HOST_CONFIG_TYPE;
host_config->common.length = length;
- host_config->buffer = dma_map_single(hostdata->dev, buffer, length,
- DMA_BIDIRECTIONAL);
+ host_config->buffer = addr = dma_map_single(hostdata->dev, buffer,
+ length,
+ DMA_BIDIRECTIONAL);
if (dma_mapping_error(host_config->buffer)) {
printk(KERN_ERR
init_completion(&evt_struct->comp);
rc = ibmvscsi_send_srp_event(evt_struct, hostdata);
- if (rc == 0) {
+ if (rc == 0)
wait_for_completion(&evt_struct->comp);
- dma_unmap_single(hostdata->dev, host_config->buffer,
- length, DMA_BIDIRECTIONAL);
- }
+ dma_unmap_single(hostdata->dev, addr, length, DMA_BIDIRECTIONAL);
return rc;
}
.cmd_per_lun = 16,
.can_queue = 1, /* Updated after SRP_LOGIN */
.this_id = -1,
- .sg_tablesize = MAX_INDIRECT_BUFS,
+ .sg_tablesize = SG_ALL,
.use_clustering = ENABLE_CLUSTERING,
.shost_attrs = ibmvscsi_attrs,
};
struct Scsi_Host *host;
struct device *dev = &vdev->dev;
unsigned long wait_switch = 0;
+ int rc;
vdev->dev.driver_data = NULL;
hostdata->host = host;
hostdata->dev = dev;
atomic_set(&hostdata->request_limit, -1);
+ hostdata->host->max_sectors = 32 * 8; /* default max I/O 32 pages */
- if (ibmvscsi_init_crq_queue(&hostdata->queue, hostdata,
- max_requests) != 0) {
+ rc = ibmvscsi_init_crq_queue(&hostdata->queue, hostdata, max_requests);
+ if (rc != 0 && rc != H_RESOURCE) {
printk(KERN_ERR "ibmvscsi: couldn't initialize crq\n");
goto init_crq_failed;
}
* to fail if the other end is not acive. In that case we don't
* want to scan
*/
- if (ibmvscsi_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
* initializing. When we are done initializing, we will have a
*/
for (wait_switch = jiffies + (init_timeout * HZ);
time_before(jiffies, wait_switch) &&
- atomic_read(&hostdata->request_limit) < 0;) {
+ atomic_read(&hostdata->request_limit) < 2;) {
- set_current_state(TASK_UNINTERRUPTIBLE);
- schedule_timeout(HZ / 100);
+ msleep(10);
}
/* if we now have a valid request_limit, initiate a scan */
*/
static struct vio_device_id ibmvscsi_device_table[] __devinitdata = {
{"vscsi", "IBM,v-scsi"},
- {0,}
+ { "", "" }
};
-
MODULE_DEVICE_TABLE(vio, ibmvscsi_device_table);
+
static struct vio_driver ibmvscsi_driver = {
- .name = "ibmvscsi",
.id_table = ibmvscsi_device_table,
.probe = ibmvscsi_probe,
- .remove = ibmvscsi_remove
+ .remove = ibmvscsi_remove,
+ .driver = {
+ .name = "ibmvscsi",
+ .owner = THIS_MODULE,
+ }
};
int __init ibmvscsi_module_init(void)