Commit bc7e380a authored by FUJITA Tomonori's avatar FUJITA Tomonori Committed by James Bottomley

[SCSI] tgt: fix sesnse buffer problems

This patch simplify the way to notify LLDs of the command completion
and addresses the following sense buffer problems:

- can't handle both data and sense.
- forces user-space to use aligned sense buffer

tgt copies sense_data from userspace to cmnd->sense_buffer (if
necessary), maps user-space pages (if necessary) and then calls
host->transfer_response (host->transfer_data is removed).
Signed-off-by: default avatarFUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>
Signed-off-by: default avatarMike Christie <michaelc@cs.wisc.edu>
Signed-off-by: default avatarJames Bottomley <James.Bottomley@SteelEye.com>
parent 181011e0
...@@ -273,23 +273,9 @@ static int ibmvstgt_rdma(struct scsi_cmnd *sc, struct scatterlist *sg, int nsg, ...@@ -273,23 +273,9 @@ static int ibmvstgt_rdma(struct scsi_cmnd *sc, struct scatterlist *sg, int nsg,
rest -= mlen; rest -= mlen;
} }
out: out:
return 0; return 0;
} }
static int ibmvstgt_transfer_data(struct scsi_cmnd *sc,
void (*done)(struct scsi_cmnd *))
{
struct iu_entry *iue = (struct iu_entry *) sc->SCp.ptr;
int err;
err = srp_transfer_data(sc, &vio_iu(iue)->srp.cmd, ibmvstgt_rdma, 1, 1);
done(sc);
return err;
}
static int ibmvstgt_cmd_done(struct scsi_cmnd *sc, static int ibmvstgt_cmd_done(struct scsi_cmnd *sc,
void (*done)(struct scsi_cmnd *)) void (*done)(struct scsi_cmnd *))
{ {
...@@ -297,7 +283,11 @@ static int ibmvstgt_cmd_done(struct scsi_cmnd *sc, ...@@ -297,7 +283,11 @@ static int ibmvstgt_cmd_done(struct scsi_cmnd *sc,
struct iu_entry *iue = (struct iu_entry *) sc->SCp.ptr; struct iu_entry *iue = (struct iu_entry *) sc->SCp.ptr;
struct srp_target *target = iue->target; struct srp_target *target = iue->target;
dprintk("%p %p %x\n", iue, target, vio_iu(iue)->srp.cmd.cdb[0]); dprintk("%p %p %x %u\n", iue, target, vio_iu(iue)->srp.cmd.cdb[0],
cmd->usg_sg);
if (sc->use_sg)
srp_transfer_data(sc, &vio_iu(iue)->srp.cmd, ibmvstgt_rdma, 1, 1);
spin_lock_irqsave(&target->lock, flags); spin_lock_irqsave(&target->lock, flags);
list_del(&iue->ilist); list_del(&iue->ilist);
...@@ -794,7 +784,6 @@ static struct scsi_host_template ibmvstgt_sht = { ...@@ -794,7 +784,6 @@ static struct scsi_host_template ibmvstgt_sht = {
.use_clustering = DISABLE_CLUSTERING, .use_clustering = DISABLE_CLUSTERING,
.max_sectors = DEFAULT_MAX_SECTORS, .max_sectors = DEFAULT_MAX_SECTORS,
.transfer_response = ibmvstgt_cmd_done, .transfer_response = ibmvstgt_cmd_done,
.transfer_data = ibmvstgt_transfer_data,
.eh_abort_handler = ibmvstgt_eh_abort_handler, .eh_abort_handler = ibmvstgt_eh_abort_handler,
.tsk_mgmt_response = ibmvstgt_tsk_mgmt_response, .tsk_mgmt_response = ibmvstgt_tsk_mgmt_response,
.shost_attrs = ibmvstgt_attrs, .shost_attrs = ibmvstgt_attrs,
......
...@@ -179,10 +179,12 @@ static int event_recv_msg(struct tgt_event *ev) ...@@ -179,10 +179,12 @@ static int event_recv_msg(struct tgt_event *ev)
switch (ev->hdr.type) { switch (ev->hdr.type) {
case TGT_UEVENT_CMD_RSP: case TGT_UEVENT_CMD_RSP:
err = scsi_tgt_kspace_exec(ev->p.cmd_rsp.host_no, err = scsi_tgt_kspace_exec(ev->p.cmd_rsp.host_no,
ev->p.cmd_rsp.tag,
ev->p.cmd_rsp.result, ev->p.cmd_rsp.result,
ev->p.cmd_rsp.len, ev->p.cmd_rsp.tag,
ev->p.cmd_rsp.uaddr, ev->p.cmd_rsp.uaddr,
ev->p.cmd_rsp.len,
ev->p.cmd_rsp.sense_uaddr,
ev->p.cmd_rsp.sense_len,
ev->p.cmd_rsp.rw); ev->p.cmd_rsp.rw);
break; break;
case TGT_UEVENT_TSK_MGMT_RSP: case TGT_UEVENT_TSK_MGMT_RSP:
......
...@@ -47,9 +47,6 @@ struct scsi_tgt_cmd { ...@@ -47,9 +47,6 @@ struct scsi_tgt_cmd {
struct list_head hash_list; struct list_head hash_list;
struct request *rq; struct request *rq;
u64 tag; u64 tag;
void *buffer;
unsigned bufflen;
}; };
#define TGT_HASH_ORDER 4 #define TGT_HASH_ORDER 4
...@@ -330,10 +327,14 @@ static void scsi_tgt_cmd_done(struct scsi_cmnd *cmd) ...@@ -330,10 +327,14 @@ static void scsi_tgt_cmd_done(struct scsi_cmnd *cmd)
dprintk("cmd %p %lu\n", cmd, rq_data_dir(cmd->request)); dprintk("cmd %p %lu\n", cmd, rq_data_dir(cmd->request));
scsi_tgt_uspace_send_status(cmd, tcmd->tag); scsi_tgt_uspace_send_status(cmd, tcmd->tag);
if (cmd->request_buffer)
scsi_free_sgtable(cmd->request_buffer, cmd->sglist_len);
queue_work(scsi_tgtd, &tcmd->work); queue_work(scsi_tgtd, &tcmd->work);
} }
static int __scsi_tgt_transfer_response(struct scsi_cmnd *cmd) static int scsi_tgt_transfer_response(struct scsi_cmnd *cmd)
{ {
struct Scsi_Host *shost = scsi_tgt_cmd_to_host(cmd); struct Scsi_Host *shost = scsi_tgt_cmd_to_host(cmd);
int err; int err;
...@@ -346,30 +347,12 @@ static int __scsi_tgt_transfer_response(struct scsi_cmnd *cmd) ...@@ -346,30 +347,12 @@ static int __scsi_tgt_transfer_response(struct scsi_cmnd *cmd)
case SCSI_MLQUEUE_DEVICE_BUSY: case SCSI_MLQUEUE_DEVICE_BUSY:
return -EAGAIN; return -EAGAIN;
} }
return 0; return 0;
} }
static void scsi_tgt_transfer_response(struct scsi_cmnd *cmd)
{
struct scsi_tgt_cmd *tcmd = cmd->request->end_io_data;
int err;
err = __scsi_tgt_transfer_response(cmd);
if (!err)
return;
cmd->result = DID_BUS_BUSY << 16;
err = scsi_tgt_uspace_send_status(cmd, tcmd->tag);
if (err <= 0)
/* the eh will have to pick this up */
printk(KERN_ERR "Could not send cmd %p status\n", cmd);
}
static int scsi_tgt_init_cmd(struct scsi_cmnd *cmd, gfp_t gfp_mask) static int scsi_tgt_init_cmd(struct scsi_cmnd *cmd, gfp_t gfp_mask)
{ {
struct request *rq = cmd->request; struct request *rq = cmd->request;
struct scsi_tgt_cmd *tcmd = rq->end_io_data;
int count; int count;
cmd->use_sg = rq->nr_phys_segments; cmd->use_sg = rq->nr_phys_segments;
...@@ -379,31 +362,28 @@ static int scsi_tgt_init_cmd(struct scsi_cmnd *cmd, gfp_t gfp_mask) ...@@ -379,31 +362,28 @@ static int scsi_tgt_init_cmd(struct scsi_cmnd *cmd, gfp_t gfp_mask)
cmd->request_bufflen = rq->data_len; cmd->request_bufflen = rq->data_len;
dprintk("cmd %p addr %p cnt %d %lu\n", cmd, tcmd->buffer, cmd->use_sg, dprintk("cmd %p cnt %d %lu\n", cmd, cmd->use_sg, rq_data_dir(rq));
rq_data_dir(rq));
count = blk_rq_map_sg(rq->q, rq, cmd->request_buffer); count = blk_rq_map_sg(rq->q, rq, cmd->request_buffer);
if (likely(count <= cmd->use_sg)) { if (likely(count <= cmd->use_sg)) {
cmd->use_sg = count; cmd->use_sg = count;
return 0; return 0;
} }
eprintk("cmd %p addr %p cnt %d\n", cmd, tcmd->buffer, cmd->use_sg); eprintk("cmd %p cnt %d\n", cmd, cmd->use_sg);
scsi_free_sgtable(cmd->request_buffer, cmd->sglist_len); scsi_free_sgtable(cmd->request_buffer, cmd->sglist_len);
return -EINVAL; return -EINVAL;
} }
/* TODO: test this crap and replace bio_map_user with new interface maybe */ /* TODO: test this crap and replace bio_map_user with new interface maybe */
static int scsi_map_user_pages(struct scsi_tgt_cmd *tcmd, struct scsi_cmnd *cmd, static int scsi_map_user_pages(struct scsi_tgt_cmd *tcmd, struct scsi_cmnd *cmd,
int rw) unsigned long uaddr, unsigned int len, int rw)
{ {
struct request_queue *q = cmd->request->q; struct request_queue *q = cmd->request->q;
struct request *rq = cmd->request; struct request *rq = cmd->request;
void *uaddr = tcmd->buffer;
unsigned int len = tcmd->bufflen;
int err; int err;
dprintk("%lx %u\n", (unsigned long) uaddr, len); dprintk("%lx %u\n", uaddr, len);
err = blk_rq_map_user(q, rq, uaddr, len); err = blk_rq_map_user(q, rq, (void *)uaddr, len);
if (err) { if (err) {
/* /*
* TODO: need to fixup sg_tablesize, max_segment_size, * TODO: need to fixup sg_tablesize, max_segment_size,
...@@ -430,45 +410,6 @@ static int scsi_map_user_pages(struct scsi_tgt_cmd *tcmd, struct scsi_cmnd *cmd, ...@@ -430,45 +410,6 @@ static int scsi_map_user_pages(struct scsi_tgt_cmd *tcmd, struct scsi_cmnd *cmd,
return err; return err;
} }
static int scsi_tgt_transfer_data(struct scsi_cmnd *);
static void scsi_tgt_data_transfer_done(struct scsi_cmnd *cmd)
{
struct scsi_tgt_cmd *tcmd = cmd->request->end_io_data;
int err;
/* should we free resources here on error ? */
if (cmd->result) {
err = scsi_tgt_uspace_send_status(cmd, tcmd->tag);
if (err <= 0)
/* the tgt uspace eh will have to pick this up */
printk(KERN_ERR "Could not send cmd %p status\n", cmd);
return;
}
dprintk("cmd %p request_bufflen %u bufflen %u\n",
cmd, cmd->request_bufflen, tcmd->bufflen);
scsi_free_sgtable(cmd->request_buffer, cmd->sglist_len);
tcmd->buffer += cmd->request_bufflen;
scsi_tgt_transfer_response(cmd);
}
static int scsi_tgt_transfer_data(struct scsi_cmnd *cmd)
{
int err;
struct Scsi_Host *host = scsi_tgt_cmd_to_host(cmd);
err = host->hostt->transfer_data(cmd, scsi_tgt_data_transfer_done);
switch (err) {
case SCSI_MLQUEUE_HOST_BUSY:
case SCSI_MLQUEUE_DEVICE_BUSY:
return -EAGAIN;
default:
return 0;
}
}
static int scsi_tgt_copy_sense(struct scsi_cmnd *cmd, unsigned long uaddr, static int scsi_tgt_copy_sense(struct scsi_cmnd *cmd, unsigned long uaddr,
unsigned len) unsigned len)
{ {
...@@ -518,8 +459,9 @@ static struct request *tgt_cmd_hash_lookup(struct request_queue *q, u64 tag) ...@@ -518,8 +459,9 @@ static struct request *tgt_cmd_hash_lookup(struct request_queue *q, u64 tag)
return rq; return rq;
} }
int scsi_tgt_kspace_exec(int host_no, u64 tag, int result, u32 len, int scsi_tgt_kspace_exec(int host_no, int result, u64 tag,
unsigned long uaddr, u8 rw) unsigned long uaddr, u32 len, unsigned long sense_uaddr,
u32 sense_len, u8 rw)
{ {
struct Scsi_Host *shost; struct Scsi_Host *shost;
struct scsi_cmnd *cmd; struct scsi_cmnd *cmd;
...@@ -564,36 +506,20 @@ int scsi_tgt_kspace_exec(int host_no, u64 tag, int result, u32 len, ...@@ -564,36 +506,20 @@ int scsi_tgt_kspace_exec(int host_no, u64 tag, int result, u32 len,
* in the request_* values * in the request_* values
*/ */
tcmd = cmd->request->end_io_data; tcmd = cmd->request->end_io_data;
tcmd->buffer = (void *)uaddr;
tcmd->bufflen = len;
cmd->result = result; cmd->result = result;
if (!tcmd->bufflen || cmd->request_buffer) { if (cmd->result == SAM_STAT_CHECK_CONDITION)
err = __scsi_tgt_transfer_response(cmd); scsi_tgt_copy_sense(cmd, sense_uaddr, sense_len);
goto done;
}
/* if (len) {
* TODO: Do we need to handle case where request does not err = scsi_map_user_pages(rq->end_io_data, cmd, uaddr, len, rw);
* align with LLD.
*/
err = scsi_map_user_pages(rq->end_io_data, cmd, rw);
if (err) { if (err) {
eprintk("%p %d\n", cmd, err); eprintk("%p %d\n", cmd, err);
err = -EAGAIN; err = -EAGAIN;
goto done; goto done;
} }
/* userspace failure */
if (cmd->result) {
if (status_byte(cmd->result) == CHECK_CONDITION)
scsi_tgt_copy_sense(cmd, uaddr, len);
err = __scsi_tgt_transfer_response(cmd);
goto done;
} }
/* ask the target LLD to transfer the data to the buffer */ err = scsi_tgt_transfer_response(cmd);
err = scsi_tgt_transfer_data(cmd);
done: done:
scsi_host_put(shost); scsi_host_put(shost);
return err; return err;
......
...@@ -18,8 +18,9 @@ extern int scsi_tgt_if_init(void); ...@@ -18,8 +18,9 @@ extern int scsi_tgt_if_init(void);
extern int scsi_tgt_uspace_send_cmd(struct scsi_cmnd *cmd, struct scsi_lun *lun, extern int scsi_tgt_uspace_send_cmd(struct scsi_cmnd *cmd, struct scsi_lun *lun,
u64 tag); u64 tag);
extern int scsi_tgt_uspace_send_status(struct scsi_cmnd *cmd, u64 tag); extern int scsi_tgt_uspace_send_status(struct scsi_cmnd *cmd, u64 tag);
extern int scsi_tgt_kspace_exec(int host_no, u64 tag, int result, u32 len, extern int scsi_tgt_kspace_exec(int host_no, int result, u64 tag,
unsigned long uaddr, u8 rw); unsigned long uaddr, u32 len, unsigned long sense_uaddr,
u32 sense_len, u8 rw);
extern int scsi_tgt_uspace_send_tsk_mgmt(int host_no, int function, u64 tag, extern int scsi_tgt_uspace_send_tsk_mgmt(int host_no, int function, u64 tag,
struct scsi_lun *scsilun, void *data); struct scsi_lun *scsilun, void *data);
extern int scsi_tgt_kspace_tsk_mgmt(int host_no, u64 mid, int result); extern int scsi_tgt_kspace_tsk_mgmt(int host_no, u64 mid, int result);
...@@ -129,6 +129,11 @@ struct scsi_host_template { ...@@ -129,6 +129,11 @@ struct scsi_host_template {
* the LLD. When the driver is finished processing the command * the LLD. When the driver is finished processing the command
* the done callback is invoked. * the done callback is invoked.
* *
* This is called to inform the LLD to transfer
* cmd->request_bufflen bytes. The cmd->use_sg speciefies the
* number of scatterlist entried in the command and
* cmd->request_buffer contains the scatterlist.
*
* return values: see queuecommand * return values: see queuecommand
* *
* If the LLD accepts the cmd, it should set the result to an * If the LLD accepts the cmd, it should set the result to an
...@@ -139,20 +144,6 @@ struct scsi_host_template { ...@@ -139,20 +144,6 @@ struct scsi_host_template {
/* TODO: rename */ /* TODO: rename */
int (* transfer_response)(struct scsi_cmnd *, int (* transfer_response)(struct scsi_cmnd *,
void (*done)(struct scsi_cmnd *)); void (*done)(struct scsi_cmnd *));
/*
* This is called to inform the LLD to transfer cmd->request_bufflen
* bytes of the cmd at cmd->offset in the cmd. The cmd->use_sg
* speciefies the number of scatterlist entried in the command
* and cmd->request_buffer contains the scatterlist.
*
* If the command cannot be processed in one transfer_data call
* becuase a scatterlist within the LLD's limits cannot be
* created then transfer_data will be called multiple times.
* It is initially called from process context, and later
* calls are from the interrup context.
*/
int (* transfer_data)(struct scsi_cmnd *,
void (*done)(struct scsi_cmnd *));
/* Used as callback for the completion of task management request. */ /* Used as callback for the completion of task management request. */
int (* tsk_mgmt_response)(u64 mid, int result); int (* tsk_mgmt_response)(u64 mid, int result);
......
...@@ -45,11 +45,13 @@ struct tgt_event { ...@@ -45,11 +45,13 @@ struct tgt_event {
/* user-> kernel */ /* user-> kernel */
struct { struct {
int host_no; int host_no;
uint32_t len;
int result; int result;
aligned_u64 tag;
aligned_u64 uaddr; aligned_u64 uaddr;
aligned_u64 sense_uaddr;
uint32_t len;
uint32_t sense_len;
uint8_t rw; uint8_t rw;
aligned_u64 tag;
} cmd_rsp; } cmd_rsp;
struct { struct {
int host_no; int host_no;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment