Skip to content

Commit

Permalink
libata: Safely overwrite attached page in WRITE SAME xlat
Browse files Browse the repository at this point in the history
Safely overwriting the attached page to ATA format from the SCSI formatted
variant.

Signed-off-by: Shaun Tancheff <shaun.tancheff@seagate.com>
Reviewed-by: Hannes Reinecke <hare@suse.com>
Acked-by: Tejun Heo <tj@kernel.org>
  • Loading branch information
Shaun Tancheff authored and Tejun Heo committed Aug 25, 2016
1 parent cd27396 commit 9379e6b
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 31 deletions.
56 changes: 51 additions & 5 deletions drivers/ata/libata-scsi.c
Original file line number Diff line number Diff line change
Expand Up @@ -3282,6 +3282,54 @@ static unsigned int ata_scsi_pass_thru(struct ata_queued_cmd *qc)
return 1;
}

/**
* ata_format_dsm_trim_descr() - SATL Write Same to DSM Trim
* @cmd: SCSI command being translated
* @num: Maximum number of entries (nominally 64).
* @sector: Starting sector
* @count: Total Range of request
*
* Rewrite the WRITE SAME descriptor to be a DSM TRIM little-endian formatted
* descriptor.
*
* Upto 64 entries of the format:
* 63:48 Range Length
* 47:0 LBA
*
* Range Length of 0 is ignored.
* LBA's should be sorted order and not overlap.
*
* NOTE: this is the same format as ADD LBA(S) TO NV CACHE PINNED SET
*/
static unsigned int ata_format_dsm_trim_descr(struct scsi_cmnd *cmd, u32 num,
u64 sector, u32 count)
{
__le64 *buffer;
u32 i = 0, used_bytes;
unsigned long flags;

BUILD_BUG_ON(512 > ATA_SCSI_RBUF_SIZE);

spin_lock_irqsave(&ata_scsi_rbuf_lock, flags);
buffer = ((void *)ata_scsi_rbuf);
while (i < num) {
u64 entry = sector |
((u64)(count > 0xffff ? 0xffff : count) << 48);
buffer[i++] = __cpu_to_le64(entry);
if (count <= 0xffff)
break;
count -= 0xffff;
sector += 0xffff;
}

used_bytes = ALIGN(i * 8, 512);
memset(buffer + i, 0, used_bytes - i * 8);
sg_copy_from_buffer(scsi_sglist(cmd), scsi_sg_count(cmd), buffer, 512);
spin_unlock_irqrestore(&ata_scsi_rbuf_lock, flags);

return used_bytes;
}

static unsigned int ata_scsi_write_same_xlat(struct ata_queued_cmd *qc)
{
struct ata_taskfile *tf = &qc->tf;
Expand All @@ -3290,8 +3338,8 @@ static unsigned int ata_scsi_write_same_xlat(struct ata_queued_cmd *qc)
const u8 *cdb = scmd->cmnd;
u64 block;
u32 n_block;
const u32 trmax = ATA_MAX_TRIM_RNUM;
u32 size;
void *buf;
u16 fp;
u8 bp = 0xff;

Expand Down Expand Up @@ -3319,10 +3367,8 @@ static unsigned int ata_scsi_write_same_xlat(struct ata_queued_cmd *qc)
if (!scsi_sg_count(scmd))
goto invalid_param_len;

buf = page_address(sg_page(scsi_sglist(scmd)));

if (n_block <= 65535 * ATA_MAX_TRIM_RNUM) {
size = ata_set_lba_range_entries(buf, ATA_MAX_TRIM_RNUM, block, n_block);
if (n_block <= 0xffff * trmax) {
size = ata_format_dsm_trim_descr(scmd, trmax, block, n_block);
} else {
fp = 2;
goto invalid_fld;
Expand Down
26 changes: 0 additions & 26 deletions include/linux/ata.h
Original file line number Diff line number Diff line change
Expand Up @@ -1071,32 +1071,6 @@ static inline void ata_id_to_hd_driveid(u16 *id)
#endif
}

/*
* Write LBA Range Entries to the buffer that will cover the extent from
* sector to sector + count. This is used for TRIM and for ADD LBA(S)
* TO NV CACHE PINNED SET.
*/
static inline unsigned ata_set_lba_range_entries(void *_buffer,
unsigned num, u64 sector, unsigned long count)
{
__le64 *buffer = _buffer;
unsigned i = 0, used_bytes;

while (i < num) {
u64 entry = sector |
((u64)(count > 0xffff ? 0xffff : count) << 48);
buffer[i++] = __cpu_to_le64(entry);
if (count <= 0xffff)
break;
count -= 0xffff;
sector += 0xffff;
}

used_bytes = ALIGN(i * 8, 512);
memset(buffer + i, 0, used_bytes - i * 8);
return used_bytes;
}

static inline bool ata_ok(u8 status)
{
return ((status & (ATA_BUSY | ATA_DRDY | ATA_DF | ATA_DRQ | ATA_ERR))
Expand Down

0 comments on commit 9379e6b

Please sign in to comment.