Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

libata: Safely overwrite attached page in WRITE SAME xlat

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>

authored by

Shaun Tancheff and committed by
Tejun Heo
9379e6b8 cd27396e

+51 -31
+51 -5
drivers/ata/libata-scsi.c
··· 3282 3282 return 1; 3283 3283 } 3284 3284 3285 + /** 3286 + * ata_format_dsm_trim_descr() - SATL Write Same to DSM Trim 3287 + * @cmd: SCSI command being translated 3288 + * @num: Maximum number of entries (nominally 64). 3289 + * @sector: Starting sector 3290 + * @count: Total Range of request 3291 + * 3292 + * Rewrite the WRITE SAME descriptor to be a DSM TRIM little-endian formatted 3293 + * descriptor. 3294 + * 3295 + * Upto 64 entries of the format: 3296 + * 63:48 Range Length 3297 + * 47:0 LBA 3298 + * 3299 + * Range Length of 0 is ignored. 3300 + * LBA's should be sorted order and not overlap. 3301 + * 3302 + * NOTE: this is the same format as ADD LBA(S) TO NV CACHE PINNED SET 3303 + */ 3304 + static unsigned int ata_format_dsm_trim_descr(struct scsi_cmnd *cmd, u32 num, 3305 + u64 sector, u32 count) 3306 + { 3307 + __le64 *buffer; 3308 + u32 i = 0, used_bytes; 3309 + unsigned long flags; 3310 + 3311 + BUILD_BUG_ON(512 > ATA_SCSI_RBUF_SIZE); 3312 + 3313 + spin_lock_irqsave(&ata_scsi_rbuf_lock, flags); 3314 + buffer = ((void *)ata_scsi_rbuf); 3315 + while (i < num) { 3316 + u64 entry = sector | 3317 + ((u64)(count > 0xffff ? 0xffff : count) << 48); 3318 + buffer[i++] = __cpu_to_le64(entry); 3319 + if (count <= 0xffff) 3320 + break; 3321 + count -= 0xffff; 3322 + sector += 0xffff; 3323 + } 3324 + 3325 + used_bytes = ALIGN(i * 8, 512); 3326 + memset(buffer + i, 0, used_bytes - i * 8); 3327 + sg_copy_from_buffer(scsi_sglist(cmd), scsi_sg_count(cmd), buffer, 512); 3328 + spin_unlock_irqrestore(&ata_scsi_rbuf_lock, flags); 3329 + 3330 + return used_bytes; 3331 + } 3332 + 3285 3333 static unsigned int ata_scsi_write_same_xlat(struct ata_queued_cmd *qc) 3286 3334 { 3287 3335 struct ata_taskfile *tf = &qc->tf; ··· 3338 3290 const u8 *cdb = scmd->cmnd; 3339 3291 u64 block; 3340 3292 u32 n_block; 3293 + const u32 trmax = ATA_MAX_TRIM_RNUM; 3341 3294 u32 size; 3342 - void *buf; 3343 3295 u16 fp; 3344 3296 u8 bp = 0xff; 3345 3297 ··· 3367 3319 if (!scsi_sg_count(scmd)) 3368 3320 goto invalid_param_len; 3369 3321 3370 - buf = page_address(sg_page(scsi_sglist(scmd))); 3371 - 3372 - if (n_block <= 65535 * ATA_MAX_TRIM_RNUM) { 3373 - size = ata_set_lba_range_entries(buf, ATA_MAX_TRIM_RNUM, block, n_block); 3322 + if (n_block <= 0xffff * trmax) { 3323 + size = ata_format_dsm_trim_descr(scmd, trmax, block, n_block); 3374 3324 } else { 3375 3325 fp = 2; 3376 3326 goto invalid_fld;
-26
include/linux/ata.h
··· 1071 1071 #endif 1072 1072 } 1073 1073 1074 - /* 1075 - * Write LBA Range Entries to the buffer that will cover the extent from 1076 - * sector to sector + count. This is used for TRIM and for ADD LBA(S) 1077 - * TO NV CACHE PINNED SET. 1078 - */ 1079 - static inline unsigned ata_set_lba_range_entries(void *_buffer, 1080 - unsigned num, u64 sector, unsigned long count) 1081 - { 1082 - __le64 *buffer = _buffer; 1083 - unsigned i = 0, used_bytes; 1084 - 1085 - while (i < num) { 1086 - u64 entry = sector | 1087 - ((u64)(count > 0xffff ? 0xffff : count) << 48); 1088 - buffer[i++] = __cpu_to_le64(entry); 1089 - if (count <= 0xffff) 1090 - break; 1091 - count -= 0xffff; 1092 - sector += 0xffff; 1093 - } 1094 - 1095 - used_bytes = ALIGN(i * 8, 512); 1096 - memset(buffer + i, 0, used_bytes - i * 8); 1097 - return used_bytes; 1098 - } 1099 - 1100 1074 static inline bool ata_ok(u8 status) 1101 1075 { 1102 1076 return ((status & (ATA_BUSY | ATA_DRDY | ATA_DF | ATA_DRQ | ATA_ERR))