libata-scsi: improve rbuf handling for simulated commands

Buffer length handling in simulated commands is error-prone and full
of bugs.  There are a number of places where necessary length checks
are missing and if the output buffer is passed in as sglist, nothing
works.

This patch adds a static buffer ata_scsi_rbuf which is sufficiently
large to handle the larges output from simulated commands (4k
currently), let all simulte functions write to the buffer and removes
all length checks as we know that there always is enough buffer space.
Copying in (for ATAPI inquiry fix up) and out are handled by
sg_copy_to/from_buffer() behind ata_scsi_rbuf_get/put() interface
which handles sglist properly.

This patch is inspired from buffer length check fix patch from Petr
Vandrovec.

Updated to use sg_copy_to/from_buffer() as suggested by FUJITA
Tomonori.

Signed-off-by: Tejun Heo <htejun@gmail.com>
Cc: Petr Vandrovec <petr@vmware.com>
Cc: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>
Signed-off-by: Jeff Garzik <jgarzik@redhat.com>
This commit is contained in:
Tejun Heo 2008-04-28 17:48:51 +09:00 committed by Jeff Garzik
parent f0761be344
commit 87340e9834

View file

@ -49,7 +49,11 @@
#include "libata.h" #include "libata.h"
#define SECTOR_SIZE 512 #define SECTOR_SIZE 512
#define ATA_SCSI_RBUF_SIZE 4096
static DEFINE_SPINLOCK(ata_scsi_rbuf_lock);
static u8 ata_scsi_rbuf[ATA_SCSI_RBUF_SIZE];
typedef unsigned int (*ata_xlat_func_t)(struct ata_queued_cmd *qc); typedef unsigned int (*ata_xlat_func_t)(struct ata_queued_cmd *qc);
@ -1639,53 +1643,48 @@ defer:
/** /**
* ata_scsi_rbuf_get - Map response buffer. * ata_scsi_rbuf_get - Map response buffer.
* @cmd: SCSI command containing buffer to be mapped. * @flags: unsigned long variable to store irq enable status
* @buf_out: Pointer to mapped area. * @copy_in: copy in from user buffer
* *
* Maps buffer contained within SCSI command @cmd. * Prepare buffer for simulated SCSI commands.
* *
* LOCKING: * LOCKING:
* spin_lock_irqsave(host lock) * spin_lock_irqsave(ata_scsi_rbuf_lock) on success
* *
* RETURNS: * RETURNS:
* Length of response buffer. * Pointer to response buffer.
*/ */
static void *ata_scsi_rbuf_get(struct scsi_cmnd *cmd, bool copy_in,
static unsigned int ata_scsi_rbuf_get(struct scsi_cmnd *cmd, u8 **buf_out) unsigned long *flags)
{ {
u8 *buf; spin_lock_irqsave(&ata_scsi_rbuf_lock, *flags);
unsigned int buflen;
struct scatterlist *sg = scsi_sglist(cmd); memset(ata_scsi_rbuf, 0, ATA_SCSI_RBUF_SIZE);
if (copy_in)
if (sg) { sg_copy_to_buffer(scsi_sglist(cmd), scsi_sg_count(cmd),
buf = kmap_atomic(sg_page(sg), KM_IRQ0) + sg->offset; ata_scsi_rbuf, ATA_SCSI_RBUF_SIZE);
buflen = sg->length; return ata_scsi_rbuf;
} else {
buf = NULL;
buflen = 0;
}
*buf_out = buf;
return buflen;
} }
/** /**
* ata_scsi_rbuf_put - Unmap response buffer. * ata_scsi_rbuf_put - Unmap response buffer.
* @cmd: SCSI command containing buffer to be unmapped. * @cmd: SCSI command containing buffer to be unmapped.
* @buf: buffer to unmap * @copy_out: copy out result
* @flags: @flags passed to ata_scsi_rbuf_get()
* *
* Unmaps response buffer contained within @cmd. * Returns rbuf buffer. The result is copied to @cmd's buffer if
* @copy_back is true.
* *
* LOCKING: * LOCKING:
* spin_lock_irqsave(host lock) * Unlocks ata_scsi_rbuf_lock.
*/ */
static inline void ata_scsi_rbuf_put(struct scsi_cmnd *cmd, bool copy_out,
static inline void ata_scsi_rbuf_put(struct scsi_cmnd *cmd, u8 *buf) unsigned long *flags)
{ {
struct scatterlist *sg = scsi_sglist(cmd); if (copy_out)
if (sg) sg_copy_from_buffer(scsi_sglist(cmd), scsi_sg_count(cmd),
kunmap_atomic(buf - sg->offset, KM_IRQ0); ata_scsi_rbuf, ATA_SCSI_RBUF_SIZE);
spin_unlock_irqrestore(&ata_scsi_rbuf_lock, *flags);
} }
/** /**
@ -1704,49 +1703,26 @@ static inline void ata_scsi_rbuf_put(struct scsi_cmnd *cmd, u8 *buf)
* spin_lock_irqsave(host lock) * spin_lock_irqsave(host lock)
*/ */
static void ata_scsi_rbuf_fill(struct ata_scsi_args *args, static void ata_scsi_rbuf_fill(struct ata_scsi_args *args,
unsigned int (*actor)(struct ata_scsi_args *args, unsigned int (*actor)(struct ata_scsi_args *args, u8 *rbuf))
u8 *rbuf, unsigned int buflen))
{ {
u8 *rbuf; u8 *rbuf;
unsigned int buflen, rc; unsigned int rc;
struct scsi_cmnd *cmd = args->cmd; struct scsi_cmnd *cmd = args->cmd;
unsigned long flags; unsigned long flags;
local_irq_save(flags); rbuf = ata_scsi_rbuf_get(cmd, false, &flags);
rc = actor(args, rbuf);
buflen = ata_scsi_rbuf_get(cmd, &rbuf); ata_scsi_rbuf_put(cmd, rc == 0, &flags);
memset(rbuf, 0, buflen);
rc = actor(args, rbuf, buflen);
ata_scsi_rbuf_put(cmd, rbuf);
local_irq_restore(flags);
if (rc == 0) if (rc == 0)
cmd->result = SAM_STAT_GOOD; cmd->result = SAM_STAT_GOOD;
args->done(cmd); args->done(cmd);
} }
/**
* ATA_SCSI_RBUF_SET - helper to set values in SCSI response buffer
* @idx: byte index into SCSI response buffer
* @val: value to set
*
* To be used by SCSI command simulator functions. This macros
* expects two local variables, u8 *rbuf and unsigned int buflen,
* are in scope.
*
* LOCKING:
* None.
*/
#define ATA_SCSI_RBUF_SET(idx, val) do { \
if ((idx) < buflen) rbuf[(idx)] = (u8)(val); \
} while (0)
/** /**
* ata_scsiop_inq_std - Simulate INQUIRY command * ata_scsiop_inq_std - Simulate INQUIRY command
* @args: device IDENTIFY data / SCSI command of interest. * @args: device IDENTIFY data / SCSI command of interest.
* @rbuf: Response buffer, to which simulated SCSI cmd output is sent. * @rbuf: Response buffer, to which simulated SCSI cmd output is sent.
* @buflen: Response buffer length.
* *
* Returns standard device identification data associated * Returns standard device identification data associated
* with non-VPD INQUIRY command output. * with non-VPD INQUIRY command output.
@ -1754,9 +1730,17 @@ static void ata_scsi_rbuf_fill(struct ata_scsi_args *args,
* LOCKING: * LOCKING:
* spin_lock_irqsave(host lock) * spin_lock_irqsave(host lock)
*/ */
static unsigned int ata_scsiop_inq_std(struct ata_scsi_args *args, u8 *rbuf, static unsigned int ata_scsiop_inq_std(struct ata_scsi_args *args, u8 *rbuf)
unsigned int buflen)
{ {
const u8 versions[] = {
0x60, /* SAM-3 (no version claimed) */
0x03,
0x20, /* SBC-2 (no version claimed) */
0x02,
0x60 /* SPC-3 (no version claimed) */
};
u8 hdr[] = { u8 hdr[] = {
TYPE_DISK, TYPE_DISK,
0, 0,
@ -1765,35 +1749,21 @@ static unsigned int ata_scsiop_inq_std(struct ata_scsi_args *args, u8 *rbuf,
95 - 4 95 - 4
}; };
VPRINTK("ENTER\n");
/* set scsi removeable (RMB) bit per ata bit */ /* set scsi removeable (RMB) bit per ata bit */
if (ata_id_removeable(args->id)) if (ata_id_removeable(args->id))
hdr[1] |= (1 << 7); hdr[1] |= (1 << 7);
VPRINTK("ENTER\n");
memcpy(rbuf, hdr, sizeof(hdr)); memcpy(rbuf, hdr, sizeof(hdr));
memcpy(&rbuf[8], "ATA ", 8);
ata_id_string(args->id, &rbuf[16], ATA_ID_PROD, 16);
ata_id_string(args->id, &rbuf[32], ATA_ID_FW_REV, 4);
if (buflen > 35) { if (rbuf[32] == 0 || rbuf[32] == ' ')
memcpy(&rbuf[8], "ATA ", 8); memcpy(&rbuf[32], "n/a ", 4);
ata_id_string(args->id, &rbuf[16], ATA_ID_PROD, 16);
ata_id_string(args->id, &rbuf[32], ATA_ID_FW_REV, 4);
if (rbuf[32] == 0 || rbuf[32] == ' ')
memcpy(&rbuf[32], "n/a ", 4);
}
if (buflen > 63) { memcpy(rbuf + 59, versions, sizeof(versions));
const u8 versions[] = {
0x60, /* SAM-3 (no version claimed) */
0x03,
0x20, /* SBC-2 (no version claimed) */
0x02,
0x60 /* SPC-3 (no version claimed) */
};
memcpy(rbuf + 59, versions, sizeof(versions));
}
return 0; return 0;
} }
@ -1802,26 +1772,22 @@ static unsigned int ata_scsiop_inq_std(struct ata_scsi_args *args, u8 *rbuf,
* ata_scsiop_inq_00 - Simulate INQUIRY VPD page 0, list of pages * ata_scsiop_inq_00 - Simulate INQUIRY VPD page 0, list of pages
* @args: device IDENTIFY data / SCSI command of interest. * @args: device IDENTIFY data / SCSI command of interest.
* @rbuf: Response buffer, to which simulated SCSI cmd output is sent. * @rbuf: Response buffer, to which simulated SCSI cmd output is sent.
* @buflen: Response buffer length.
* *
* Returns list of inquiry VPD pages available. * Returns list of inquiry VPD pages available.
* *
* LOCKING: * LOCKING:
* spin_lock_irqsave(host lock) * spin_lock_irqsave(host lock)
*/ */
static unsigned int ata_scsiop_inq_00(struct ata_scsi_args *args, u8 *rbuf, static unsigned int ata_scsiop_inq_00(struct ata_scsi_args *args, u8 *rbuf)
unsigned int buflen)
{ {
const u8 pages[] = { const u8 pages[] = {
0x00, /* page 0x00, this page */ 0x00, /* page 0x00, this page */
0x80, /* page 0x80, unit serial no page */ 0x80, /* page 0x80, unit serial no page */
0x83 /* page 0x83, device ident page */ 0x83 /* page 0x83, device ident page */
}; };
rbuf[3] = sizeof(pages); /* number of supported VPD pages */ rbuf[3] = sizeof(pages); /* number of supported VPD pages */
memcpy(rbuf + 4, pages, sizeof(pages));
if (buflen > 6)
memcpy(rbuf + 4, pages, sizeof(pages));
return 0; return 0;
} }
@ -1829,15 +1795,13 @@ static unsigned int ata_scsiop_inq_00(struct ata_scsi_args *args, u8 *rbuf,
* ata_scsiop_inq_80 - Simulate INQUIRY VPD page 80, device serial number * ata_scsiop_inq_80 - Simulate INQUIRY VPD page 80, device serial number
* @args: device IDENTIFY data / SCSI command of interest. * @args: device IDENTIFY data / SCSI command of interest.
* @rbuf: Response buffer, to which simulated SCSI cmd output is sent. * @rbuf: Response buffer, to which simulated SCSI cmd output is sent.
* @buflen: Response buffer length.
* *
* Returns ATA device serial number. * Returns ATA device serial number.
* *
* LOCKING: * LOCKING:
* spin_lock_irqsave(host lock) * spin_lock_irqsave(host lock)
*/ */
static unsigned int ata_scsiop_inq_80(struct ata_scsi_args *args, u8 *rbuf, static unsigned int ata_scsiop_inq_80(struct ata_scsi_args *args, u8 *rbuf)
unsigned int buflen)
{ {
const u8 hdr[] = { const u8 hdr[] = {
0, 0,
@ -1845,12 +1809,10 @@ static unsigned int ata_scsiop_inq_80(struct ata_scsi_args *args, u8 *rbuf,
0, 0,
ATA_ID_SERNO_LEN, /* page len */ ATA_ID_SERNO_LEN, /* page len */
}; };
memcpy(rbuf, hdr, sizeof(hdr)); memcpy(rbuf, hdr, sizeof(hdr));
ata_id_string(args->id, (unsigned char *) &rbuf[4],
if (buflen > (ATA_ID_SERNO_LEN + 4 - 1)) ATA_ID_SERNO, ATA_ID_SERNO_LEN);
ata_id_string(args->id, (unsigned char *) &rbuf[4],
ATA_ID_SERNO, ATA_ID_SERNO_LEN);
return 0; return 0;
} }
@ -1858,7 +1820,6 @@ static unsigned int ata_scsiop_inq_80(struct ata_scsi_args *args, u8 *rbuf,
* ata_scsiop_inq_83 - Simulate INQUIRY VPD page 83, device identity * ata_scsiop_inq_83 - Simulate INQUIRY VPD page 83, device identity
* @args: device IDENTIFY data / SCSI command of interest. * @args: device IDENTIFY data / SCSI command of interest.
* @rbuf: Response buffer, to which simulated SCSI cmd output is sent. * @rbuf: Response buffer, to which simulated SCSI cmd output is sent.
* @buflen: Response buffer length.
* *
* Yields two logical unit device identification designators: * Yields two logical unit device identification designators:
* - vendor specific ASCII containing the ATA serial number * - vendor specific ASCII containing the ATA serial number
@ -1868,40 +1829,37 @@ static unsigned int ata_scsiop_inq_80(struct ata_scsi_args *args, u8 *rbuf,
* LOCKING: * LOCKING:
* spin_lock_irqsave(host lock) * spin_lock_irqsave(host lock)
*/ */
static unsigned int ata_scsiop_inq_83(struct ata_scsi_args *args, u8 *rbuf, static unsigned int ata_scsiop_inq_83(struct ata_scsi_args *args, u8 *rbuf)
unsigned int buflen)
{ {
int num;
const int sat_model_serial_desc_len = 68; const int sat_model_serial_desc_len = 68;
int num;
rbuf[1] = 0x83; /* this page code */ rbuf[1] = 0x83; /* this page code */
num = 4; num = 4;
if (buflen > (ATA_ID_SERNO_LEN + num + 3)) { /* piv=0, assoc=lu, code_set=ACSII, designator=vendor */
/* piv=0, assoc=lu, code_set=ACSII, designator=vendor */ rbuf[num + 0] = 2;
rbuf[num + 0] = 2; rbuf[num + 3] = ATA_ID_SERNO_LEN;
rbuf[num + 3] = ATA_ID_SERNO_LEN; num += 4;
num += 4; ata_id_string(args->id, (unsigned char *) rbuf + num,
ata_id_string(args->id, (unsigned char *) rbuf + num, ATA_ID_SERNO, ATA_ID_SERNO_LEN);
ATA_ID_SERNO, ATA_ID_SERNO_LEN); num += ATA_ID_SERNO_LEN;
num += ATA_ID_SERNO_LEN;
} /* SAT defined lu model and serial numbers descriptor */
if (buflen > (sat_model_serial_desc_len + num + 3)) { /* piv=0, assoc=lu, code_set=ACSII, designator=t10 vendor id */
/* SAT defined lu model and serial numbers descriptor */ rbuf[num + 0] = 2;
/* piv=0, assoc=lu, code_set=ACSII, designator=t10 vendor id */ rbuf[num + 1] = 1;
rbuf[num + 0] = 2; rbuf[num + 3] = sat_model_serial_desc_len;
rbuf[num + 1] = 1; num += 4;
rbuf[num + 3] = sat_model_serial_desc_len; memcpy(rbuf + num, "ATA ", 8);
num += 4; num += 8;
memcpy(rbuf + num, "ATA ", 8); ata_id_string(args->id, (unsigned char *) rbuf + num, ATA_ID_PROD,
num += 8; ATA_ID_PROD_LEN);
ata_id_string(args->id, (unsigned char *) rbuf + num, num += ATA_ID_PROD_LEN;
ATA_ID_PROD, ATA_ID_PROD_LEN); ata_id_string(args->id, (unsigned char *) rbuf + num, ATA_ID_SERNO,
num += ATA_ID_PROD_LEN; ATA_ID_SERNO_LEN);
ata_id_string(args->id, (unsigned char *) rbuf + num, num += ATA_ID_SERNO_LEN;
ATA_ID_SERNO, ATA_ID_SERNO_LEN);
num += ATA_ID_SERNO_LEN;
}
rbuf[3] = num - 4; /* page len (assume less than 256 bytes) */ rbuf[3] = num - 4; /* page len (assume less than 256 bytes) */
return 0; return 0;
} }
@ -1910,34 +1868,26 @@ static unsigned int ata_scsiop_inq_83(struct ata_scsi_args *args, u8 *rbuf,
* ata_scsiop_inq_89 - Simulate INQUIRY VPD page 89, ATA info * ata_scsiop_inq_89 - Simulate INQUIRY VPD page 89, ATA info
* @args: device IDENTIFY data / SCSI command of interest. * @args: device IDENTIFY data / SCSI command of interest.
* @rbuf: Response buffer, to which simulated SCSI cmd output is sent. * @rbuf: Response buffer, to which simulated SCSI cmd output is sent.
* @buflen: Response buffer length.
* *
* Yields SAT-specified ATA VPD page. * Yields SAT-specified ATA VPD page.
* *
* LOCKING: * LOCKING:
* spin_lock_irqsave(host lock) * spin_lock_irqsave(host lock)
*/ */
static unsigned int ata_scsiop_inq_89(struct ata_scsi_args *args, u8 *rbuf, static unsigned int ata_scsiop_inq_89(struct ata_scsi_args *args, u8 *rbuf)
unsigned int buflen)
{ {
u8 pbuf[60];
struct ata_taskfile tf; struct ata_taskfile tf;
unsigned int i;
if (!buflen)
return 0;
memset(&pbuf, 0, sizeof(pbuf));
memset(&tf, 0, sizeof(tf)); memset(&tf, 0, sizeof(tf));
pbuf[1] = 0x89; /* our page code */ rbuf[1] = 0x89; /* our page code */
pbuf[2] = (0x238 >> 8); /* page size fixed at 238h */ rbuf[2] = (0x238 >> 8); /* page size fixed at 238h */
pbuf[3] = (0x238 & 0xff); rbuf[3] = (0x238 & 0xff);
memcpy(&pbuf[8], "linux ", 8); memcpy(&rbuf[8], "linux ", 8);
memcpy(&pbuf[16], "libata ", 16); memcpy(&rbuf[16], "libata ", 16);
memcpy(&pbuf[32], DRV_VERSION, 4); memcpy(&rbuf[32], DRV_VERSION, 4);
ata_id_string(args->id, &pbuf[32], ATA_ID_FW_REV, 4); ata_id_string(args->id, &rbuf[32], ATA_ID_FW_REV, 4);
/* we don't store the ATA device signature, so we fake it */ /* we don't store the ATA device signature, so we fake it */
@ -1945,19 +1895,12 @@ static unsigned int ata_scsiop_inq_89(struct ata_scsi_args *args, u8 *rbuf,
tf.lbal = 0x1; tf.lbal = 0x1;
tf.nsect = 0x1; tf.nsect = 0x1;
ata_tf_to_fis(&tf, 0, 1, &pbuf[36]); /* TODO: PMP? */ ata_tf_to_fis(&tf, 0, 1, &rbuf[36]); /* TODO: PMP? */
pbuf[36] = 0x34; /* force D2H Reg FIS (34h) */ rbuf[36] = 0x34; /* force D2H Reg FIS (34h) */
pbuf[56] = ATA_CMD_ID_ATA; rbuf[56] = ATA_CMD_ID_ATA;
i = min(buflen, 60U); memcpy(&rbuf[60], &args->id[0], 512);
memcpy(rbuf, &pbuf[0], i);
buflen -= i;
if (!buflen)
return 0;
memcpy(&rbuf[60], &args->id[0], min(buflen, 512U));
return 0; return 0;
} }
@ -1965,7 +1908,6 @@ static unsigned int ata_scsiop_inq_89(struct ata_scsi_args *args, u8 *rbuf,
* ata_scsiop_noop - Command handler that simply returns success. * ata_scsiop_noop - Command handler that simply returns success.
* @args: device IDENTIFY data / SCSI command of interest. * @args: device IDENTIFY data / SCSI command of interest.
* @rbuf: Response buffer, to which simulated SCSI cmd output is sent. * @rbuf: Response buffer, to which simulated SCSI cmd output is sent.
* @buflen: Response buffer length.
* *
* No operation. Simply returns success to caller, to indicate * No operation. Simply returns success to caller, to indicate
* that the caller should successfully complete this SCSI command. * that the caller should successfully complete this SCSI command.
@ -1973,46 +1915,16 @@ static unsigned int ata_scsiop_inq_89(struct ata_scsi_args *args, u8 *rbuf,
* LOCKING: * LOCKING:
* spin_lock_irqsave(host lock) * spin_lock_irqsave(host lock)
*/ */
static unsigned int ata_scsiop_noop(struct ata_scsi_args *args, u8 *rbuf, static unsigned int ata_scsiop_noop(struct ata_scsi_args *args, u8 *rbuf)
unsigned int buflen)
{ {
VPRINTK("ENTER\n"); VPRINTK("ENTER\n");
return 0; return 0;
} }
/**
* ata_msense_push - Push data onto MODE SENSE data output buffer
* @ptr_io: (input/output) Location to store more output data
* @last: End of output data buffer
* @buf: Pointer to BLOB being added to output buffer
* @buflen: Length of BLOB
*
* Store MODE SENSE data on an output buffer.
*
* LOCKING:
* None.
*/
static void ata_msense_push(u8 **ptr_io, const u8 *last,
const u8 *buf, unsigned int buflen)
{
u8 *ptr = *ptr_io;
if ((ptr + buflen - 1) > last)
return;
memcpy(ptr, buf, buflen);
ptr += buflen;
*ptr_io = ptr;
}
/** /**
* ata_msense_caching - Simulate MODE SENSE caching info page * ata_msense_caching - Simulate MODE SENSE caching info page
* @id: device IDENTIFY data * @id: device IDENTIFY data
* @ptr_io: (input/output) Location to store more output data * @buf: output buffer
* @last: End of output data buffer
* *
* Generate a caching info page, which conditionally indicates * Generate a caching info page, which conditionally indicates
* write caching to the SCSI layer, depending on device * write caching to the SCSI layer, depending on device
@ -2021,58 +1933,43 @@ static void ata_msense_push(u8 **ptr_io, const u8 *last,
* LOCKING: * LOCKING:
* None. * None.
*/ */
static unsigned int ata_msense_caching(u16 *id, u8 *buf)
static unsigned int ata_msense_caching(u16 *id, u8 **ptr_io,
const u8 *last)
{ {
u8 page[CACHE_MPAGE_LEN]; memcpy(buf, def_cache_mpage, sizeof(def_cache_mpage));
memcpy(page, def_cache_mpage, sizeof(page));
if (ata_id_wcache_enabled(id)) if (ata_id_wcache_enabled(id))
page[2] |= (1 << 2); /* write cache enable */ buf[2] |= (1 << 2); /* write cache enable */
if (!ata_id_rahead_enabled(id)) if (!ata_id_rahead_enabled(id))
page[12] |= (1 << 5); /* disable read ahead */ buf[12] |= (1 << 5); /* disable read ahead */
return sizeof(def_cache_mpage);
ata_msense_push(ptr_io, last, page, sizeof(page));
return sizeof(page);
} }
/** /**
* ata_msense_ctl_mode - Simulate MODE SENSE control mode page * ata_msense_ctl_mode - Simulate MODE SENSE control mode page
* @dev: Device associated with this MODE SENSE command * @buf: output buffer
* @ptr_io: (input/output) Location to store more output data
* @last: End of output data buffer
* *
* Generate a generic MODE SENSE control mode page. * Generate a generic MODE SENSE control mode page.
* *
* LOCKING: * LOCKING:
* None. * None.
*/ */
static unsigned int ata_msense_ctl_mode(u8 *buf)
static unsigned int ata_msense_ctl_mode(u8 **ptr_io, const u8 *last)
{ {
ata_msense_push(ptr_io, last, def_control_mpage, memcpy(buf, def_control_mpage, sizeof(def_control_mpage));
sizeof(def_control_mpage));
return sizeof(def_control_mpage); return sizeof(def_control_mpage);
} }
/** /**
* ata_msense_rw_recovery - Simulate MODE SENSE r/w error recovery page * ata_msense_rw_recovery - Simulate MODE SENSE r/w error recovery page
* @dev: Device associated with this MODE SENSE command * @bufp: output buffer
* @ptr_io: (input/output) Location to store more output data
* @last: End of output data buffer
* *
* Generate a generic MODE SENSE r/w error recovery page. * Generate a generic MODE SENSE r/w error recovery page.
* *
* LOCKING: * LOCKING:
* None. * None.
*/ */
static unsigned int ata_msense_rw_recovery(u8 *buf)
static unsigned int ata_msense_rw_recovery(u8 **ptr_io, const u8 *last)
{ {
memcpy(buf, def_rw_recovery_mpage, sizeof(def_rw_recovery_mpage));
ata_msense_push(ptr_io, last, def_rw_recovery_mpage,
sizeof(def_rw_recovery_mpage));
return sizeof(def_rw_recovery_mpage); return sizeof(def_rw_recovery_mpage);
} }
@ -2104,7 +2001,6 @@ static int ata_dev_supports_fua(u16 *id)
* ata_scsiop_mode_sense - Simulate MODE SENSE 6, 10 commands * ata_scsiop_mode_sense - Simulate MODE SENSE 6, 10 commands
* @args: device IDENTIFY data / SCSI command of interest. * @args: device IDENTIFY data / SCSI command of interest.
* @rbuf: Response buffer, to which simulated SCSI cmd output is sent. * @rbuf: Response buffer, to which simulated SCSI cmd output is sent.
* @buflen: Response buffer length.
* *
* Simulate MODE SENSE commands. Assume this is invoked for direct * Simulate MODE SENSE commands. Assume this is invoked for direct
* access devices (e.g. disks) only. There should be no block * access devices (e.g. disks) only. There should be no block
@ -2113,19 +2009,17 @@ static int ata_dev_supports_fua(u16 *id)
* LOCKING: * LOCKING:
* spin_lock_irqsave(host lock) * spin_lock_irqsave(host lock)
*/ */
static unsigned int ata_scsiop_mode_sense(struct ata_scsi_args *args, u8 *rbuf)
unsigned int ata_scsiop_mode_sense(struct ata_scsi_args *args, u8 *rbuf,
unsigned int buflen)
{ {
struct ata_device *dev = args->dev; struct ata_device *dev = args->dev;
u8 *scsicmd = args->cmd->cmnd, *p, *last; u8 *scsicmd = args->cmd->cmnd, *p = rbuf;
const u8 sat_blk_desc[] = { const u8 sat_blk_desc[] = {
0, 0, 0, 0, /* number of blocks: sat unspecified */ 0, 0, 0, 0, /* number of blocks: sat unspecified */
0, 0,
0, 0x2, 0x0 /* block length: 512 bytes */ 0, 0x2, 0x0 /* block length: 512 bytes */
}; };
u8 pg, spg; u8 pg, spg;
unsigned int ebd, page_control, six_byte, output_len, alloc_len, minlen; unsigned int ebd, page_control, six_byte;
u8 dpofua; u8 dpofua;
VPRINTK("ENTER\n"); VPRINTK("ENTER\n");
@ -2148,17 +2042,10 @@ unsigned int ata_scsiop_mode_sense(struct ata_scsi_args *args, u8 *rbuf,
goto invalid_fld; goto invalid_fld;
} }
if (six_byte) { if (six_byte)
output_len = 4 + (ebd ? 8 : 0); p += 4 + (ebd ? 8 : 0);
alloc_len = scsicmd[4]; else
} else { p += 8 + (ebd ? 8 : 0);
output_len = 8 + (ebd ? 8 : 0);
alloc_len = (scsicmd[7] << 8) + scsicmd[8];
}
minlen = (alloc_len < buflen) ? alloc_len : buflen;
p = rbuf + output_len;
last = rbuf + minlen - 1;
pg = scsicmd[2] & 0x3f; pg = scsicmd[2] & 0x3f;
spg = scsicmd[3]; spg = scsicmd[3];
@ -2171,61 +2058,48 @@ unsigned int ata_scsiop_mode_sense(struct ata_scsi_args *args, u8 *rbuf,
switch(pg) { switch(pg) {
case RW_RECOVERY_MPAGE: case RW_RECOVERY_MPAGE:
output_len += ata_msense_rw_recovery(&p, last); p += ata_msense_rw_recovery(p);
break; break;
case CACHE_MPAGE: case CACHE_MPAGE:
output_len += ata_msense_caching(args->id, &p, last); p += ata_msense_caching(args->id, p);
break; break;
case CONTROL_MPAGE: { case CONTROL_MPAGE:
output_len += ata_msense_ctl_mode(&p, last); p += ata_msense_ctl_mode(p);
break; break;
}
case ALL_MPAGES: case ALL_MPAGES:
output_len += ata_msense_rw_recovery(&p, last); p += ata_msense_rw_recovery(p);
output_len += ata_msense_caching(args->id, &p, last); p += ata_msense_caching(args->id, p);
output_len += ata_msense_ctl_mode(&p, last); p += ata_msense_ctl_mode(p);
break; break;
default: /* invalid page code */ default: /* invalid page code */
goto invalid_fld; goto invalid_fld;
} }
if (minlen < 1)
return 0;
dpofua = 0; dpofua = 0;
if (ata_dev_supports_fua(args->id) && (dev->flags & ATA_DFLAG_LBA48) && if (ata_dev_supports_fua(args->id) && (dev->flags & ATA_DFLAG_LBA48) &&
(!(dev->flags & ATA_DFLAG_PIO) || dev->multi_count)) (!(dev->flags & ATA_DFLAG_PIO) || dev->multi_count))
dpofua = 1 << 4; dpofua = 1 << 4;
if (six_byte) { if (six_byte) {
output_len--; rbuf[0] = p - rbuf - 1;
rbuf[0] = output_len; rbuf[2] |= dpofua;
if (minlen > 2)
rbuf[2] |= dpofua;
if (ebd) { if (ebd) {
if (minlen > 3) rbuf[3] = sizeof(sat_blk_desc);
rbuf[3] = sizeof(sat_blk_desc); memcpy(rbuf + 4, sat_blk_desc, sizeof(sat_blk_desc));
if (minlen > 11)
memcpy(rbuf + 4, sat_blk_desc,
sizeof(sat_blk_desc));
} }
} else { } else {
output_len -= 2; unsigned int output_len = p - rbuf - 2;
rbuf[0] = output_len >> 8; rbuf[0] = output_len >> 8;
if (minlen > 1) rbuf[1] = output_len;
rbuf[1] = output_len; rbuf[3] |= dpofua;
if (minlen > 3)
rbuf[3] |= dpofua;
if (ebd) { if (ebd) {
if (minlen > 7) rbuf[7] = sizeof(sat_blk_desc);
rbuf[7] = sizeof(sat_blk_desc); memcpy(rbuf + 8, sat_blk_desc, sizeof(sat_blk_desc));
if (minlen > 15)
memcpy(rbuf + 8, sat_blk_desc,
sizeof(sat_blk_desc));
} }
} }
return 0; return 0;
@ -2245,15 +2119,13 @@ saving_not_supp:
* ata_scsiop_read_cap - Simulate READ CAPACITY[ 16] commands * ata_scsiop_read_cap - Simulate READ CAPACITY[ 16] commands
* @args: device IDENTIFY data / SCSI command of interest. * @args: device IDENTIFY data / SCSI command of interest.
* @rbuf: Response buffer, to which simulated SCSI cmd output is sent. * @rbuf: Response buffer, to which simulated SCSI cmd output is sent.
* @buflen: Response buffer length.
* *
* Simulate READ CAPACITY commands. * Simulate READ CAPACITY commands.
* *
* LOCKING: * LOCKING:
* None. * None.
*/ */
unsigned int ata_scsiop_read_cap(struct ata_scsi_args *args, u8 *rbuf, static unsigned int ata_scsiop_read_cap(struct ata_scsi_args *args, u8 *rbuf)
unsigned int buflen)
{ {
u64 last_lba = args->dev->n_sectors - 1; /* LBA of the last block */ u64 last_lba = args->dev->n_sectors - 1; /* LBA of the last block */
@ -2264,28 +2136,28 @@ unsigned int ata_scsiop_read_cap(struct ata_scsi_args *args, u8 *rbuf,
last_lba = 0xffffffff; last_lba = 0xffffffff;
/* sector count, 32-bit */ /* sector count, 32-bit */
ATA_SCSI_RBUF_SET(0, last_lba >> (8 * 3)); rbuf[0] = last_lba >> (8 * 3);
ATA_SCSI_RBUF_SET(1, last_lba >> (8 * 2)); rbuf[1] = last_lba >> (8 * 2);
ATA_SCSI_RBUF_SET(2, last_lba >> (8 * 1)); rbuf[2] = last_lba >> (8 * 1);
ATA_SCSI_RBUF_SET(3, last_lba); rbuf[3] = last_lba;
/* sector size */ /* sector size */
ATA_SCSI_RBUF_SET(6, ATA_SECT_SIZE >> 8); rbuf[6] = ATA_SECT_SIZE >> 8;
ATA_SCSI_RBUF_SET(7, ATA_SECT_SIZE & 0xff); rbuf[7] = ATA_SECT_SIZE & 0xff;
} else { } else {
/* sector count, 64-bit */ /* sector count, 64-bit */
ATA_SCSI_RBUF_SET(0, last_lba >> (8 * 7)); rbuf[0] = last_lba >> (8 * 7);
ATA_SCSI_RBUF_SET(1, last_lba >> (8 * 6)); rbuf[1] = last_lba >> (8 * 6);
ATA_SCSI_RBUF_SET(2, last_lba >> (8 * 5)); rbuf[2] = last_lba >> (8 * 5);
ATA_SCSI_RBUF_SET(3, last_lba >> (8 * 4)); rbuf[3] = last_lba >> (8 * 4);
ATA_SCSI_RBUF_SET(4, last_lba >> (8 * 3)); rbuf[4] = last_lba >> (8 * 3);
ATA_SCSI_RBUF_SET(5, last_lba >> (8 * 2)); rbuf[5] = last_lba >> (8 * 2);
ATA_SCSI_RBUF_SET(6, last_lba >> (8 * 1)); rbuf[6] = last_lba >> (8 * 1);
ATA_SCSI_RBUF_SET(7, last_lba); rbuf[7] = last_lba;
/* sector size */ /* sector size */
ATA_SCSI_RBUF_SET(10, ATA_SECT_SIZE >> 8); rbuf[10] = ATA_SECT_SIZE >> 8;
ATA_SCSI_RBUF_SET(11, ATA_SECT_SIZE & 0xff); rbuf[11] = ATA_SECT_SIZE & 0xff;
} }
return 0; return 0;
@ -2295,16 +2167,13 @@ unsigned int ata_scsiop_read_cap(struct ata_scsi_args *args, u8 *rbuf,
* ata_scsiop_report_luns - Simulate REPORT LUNS command * ata_scsiop_report_luns - Simulate REPORT LUNS command
* @args: device IDENTIFY data / SCSI command of interest. * @args: device IDENTIFY data / SCSI command of interest.
* @rbuf: Response buffer, to which simulated SCSI cmd output is sent. * @rbuf: Response buffer, to which simulated SCSI cmd output is sent.
* @buflen: Response buffer length.
* *
* Simulate REPORT LUNS command. * Simulate REPORT LUNS command.
* *
* LOCKING: * LOCKING:
* spin_lock_irqsave(host lock) * spin_lock_irqsave(host lock)
*/ */
static unsigned int ata_scsiop_report_luns(struct ata_scsi_args *args, u8 *rbuf)
unsigned int ata_scsiop_report_luns(struct ata_scsi_args *args, u8 *rbuf,
unsigned int buflen)
{ {
VPRINTK("ENTER\n"); VPRINTK("ENTER\n");
rbuf[3] = 8; /* just one lun, LUN 0, size 8 bytes */ rbuf[3] = 8; /* just one lun, LUN 0, size 8 bytes */
@ -2438,13 +2307,10 @@ static void atapi_qc_complete(struct ata_queued_cmd *qc)
u8 *scsicmd = cmd->cmnd; u8 *scsicmd = cmd->cmnd;
if ((scsicmd[0] == INQUIRY) && ((scsicmd[1] & 0x03) == 0)) { if ((scsicmd[0] == INQUIRY) && ((scsicmd[1] & 0x03) == 0)) {
u8 *buf = NULL;
unsigned int buflen;
unsigned long flags; unsigned long flags;
u8 *buf;
local_irq_save(flags); buf = ata_scsi_rbuf_get(cmd, true, &flags);
buflen = ata_scsi_rbuf_get(cmd, &buf);
/* ATAPI devices typically report zero for their SCSI version, /* ATAPI devices typically report zero for their SCSI version,
* and sometimes deviate from the spec WRT response data * and sometimes deviate from the spec WRT response data
@ -2459,9 +2325,7 @@ static void atapi_qc_complete(struct ata_queued_cmd *qc)
buf[3] = 0x32; buf[3] = 0x32;
} }
ata_scsi_rbuf_put(cmd, buf); ata_scsi_rbuf_put(cmd, true, &flags);
local_irq_restore(flags);
} }
cmd->result = SAM_STAT_GOOD; cmd->result = SAM_STAT_GOOD;