diff options
| author | Hannes Reinecke <hare@suse.de> | 2016-04-25 06:45:52 -0400 |
|---|---|---|
| committer | Tejun Heo <tj@kernel.org> | 2016-05-09 12:36:46 -0400 |
| commit | 28a3fc2295a744a0d2ddf86b2ccdf03fbab123f9 (patch) | |
| tree | 37302dd2083fec2347b1805ebe8c57dbd82abb68 | |
| parent | 3a92945b24c7ff46757a3d5d5112bfc62d2e45b2 (diff) | |
libata: implement ZBC IN translation
ZAC drives implement a 'ZAC Management In' command template,
which maps onto the ZBC IN command.
Signed-off-by: Hannes Reinecke <hare@suse.de>
Signed-off-by: Tejun Heo <tj@kernel.org>
| -rw-r--r-- | drivers/ata/libata-eh.c | 1 | ||||
| -rw-r--r-- | drivers/ata/libata-scsi.c | 157 | ||||
| -rw-r--r-- | drivers/ata/libata-trace.c | 10 | ||||
| -rw-r--r-- | include/linux/ata.h | 10 | ||||
| -rw-r--r-- | include/linux/libata.h | 7 | ||||
| -rw-r--r-- | include/trace/events/libata.h | 1 |
6 files changed, 185 insertions, 1 deletions
diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index e81661981a09..ee6c572d2a4a 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c | |||
| @@ -2482,6 +2482,7 @@ const char *ata_get_cmd_descript(u8 command) | |||
| 2482 | { ATA_CMD_CFA_WRITE_MULT_NE, "CFA WRITE MULTIPLE WITHOUT ERASE" }, | 2482 | { ATA_CMD_CFA_WRITE_MULT_NE, "CFA WRITE MULTIPLE WITHOUT ERASE" }, |
| 2483 | { ATA_CMD_REQ_SENSE_DATA, "REQUEST SENSE DATA EXT" }, | 2483 | { ATA_CMD_REQ_SENSE_DATA, "REQUEST SENSE DATA EXT" }, |
| 2484 | { ATA_CMD_SANITIZE_DEVICE, "SANITIZE DEVICE" }, | 2484 | { ATA_CMD_SANITIZE_DEVICE, "SANITIZE DEVICE" }, |
| 2485 | { ATA_CMD_ZAC_MGMT_IN, "ZAC MANAGEMENT IN" }, | ||
| 2485 | { ATA_CMD_READ_LONG, "READ LONG (with retries)" }, | 2486 | { ATA_CMD_READ_LONG, "READ LONG (with retries)" }, |
| 2486 | { ATA_CMD_READ_LONG_ONCE, "READ LONG (without retries)" }, | 2487 | { ATA_CMD_READ_LONG_ONCE, "READ LONG (without retries)" }, |
| 2487 | { ATA_CMD_WRITE_LONG, "WRITE LONG (with retries)" }, | 2488 | { ATA_CMD_WRITE_LONG, "WRITE LONG (with retries)" }, |
diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 6d78b4b422a4..06d5a62f507d 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c | |||
| @@ -3318,6 +3318,160 @@ invalid_opcode: | |||
| 3318 | } | 3318 | } |
| 3319 | 3319 | ||
| 3320 | /** | 3320 | /** |
| 3321 | * ata_scsi_report_zones_complete - convert ATA output | ||
| 3322 | * @qc: command structure returning the data | ||
| 3323 | * | ||
| 3324 | * Convert T-13 little-endian field representation into | ||
| 3325 | * T-10 big-endian field representation. | ||
| 3326 | * What a mess. | ||
| 3327 | */ | ||
| 3328 | static void ata_scsi_report_zones_complete(struct ata_queued_cmd *qc) | ||
| 3329 | { | ||
| 3330 | struct scsi_cmnd *scmd = qc->scsicmd; | ||
| 3331 | struct sg_mapping_iter miter; | ||
| 3332 | unsigned long flags; | ||
| 3333 | unsigned int bytes = 0; | ||
| 3334 | |||
| 3335 | sg_miter_start(&miter, scsi_sglist(scmd), scsi_sg_count(scmd), | ||
| 3336 | SG_MITER_TO_SG | SG_MITER_ATOMIC); | ||
| 3337 | |||
| 3338 | local_irq_save(flags); | ||
| 3339 | while (sg_miter_next(&miter)) { | ||
| 3340 | unsigned int offset = 0; | ||
| 3341 | |||
| 3342 | if (bytes == 0) { | ||
| 3343 | char *hdr; | ||
| 3344 | u32 list_length; | ||
| 3345 | u64 max_lba, opt_lba; | ||
| 3346 | u16 same; | ||
| 3347 | |||
| 3348 | /* Swizzle header */ | ||
| 3349 | hdr = miter.addr; | ||
| 3350 | list_length = get_unaligned_le32(&hdr[0]); | ||
| 3351 | same = get_unaligned_le16(&hdr[4]); | ||
| 3352 | max_lba = get_unaligned_le64(&hdr[8]); | ||
| 3353 | opt_lba = get_unaligned_le64(&hdr[16]); | ||
| 3354 | put_unaligned_be32(list_length, &hdr[0]); | ||
| 3355 | hdr[4] = same & 0xf; | ||
| 3356 | put_unaligned_be64(max_lba, &hdr[8]); | ||
| 3357 | put_unaligned_be64(opt_lba, &hdr[16]); | ||
| 3358 | offset += 64; | ||
| 3359 | bytes += 64; | ||
| 3360 | } | ||
| 3361 | while (offset < miter.length) { | ||
| 3362 | char *rec; | ||
| 3363 | u8 cond, type, non_seq, reset; | ||
| 3364 | u64 size, start, wp; | ||
| 3365 | |||
| 3366 | /* Swizzle zone descriptor */ | ||
| 3367 | rec = miter.addr + offset; | ||
| 3368 | type = rec[0] & 0xf; | ||
| 3369 | cond = (rec[1] >> 4) & 0xf; | ||
| 3370 | non_seq = (rec[1] & 2); | ||
| 3371 | reset = (rec[1] & 1); | ||
| 3372 | size = get_unaligned_le64(&rec[8]); | ||
| 3373 | start = get_unaligned_le64(&rec[16]); | ||
| 3374 | wp = get_unaligned_le64(&rec[24]); | ||
| 3375 | rec[0] = type; | ||
| 3376 | rec[1] = (cond << 4) | non_seq | reset; | ||
| 3377 | put_unaligned_be64(size, &rec[8]); | ||
| 3378 | put_unaligned_be64(start, &rec[16]); | ||
| 3379 | put_unaligned_be64(wp, &rec[24]); | ||
| 3380 | WARN_ON(offset + 64 > miter.length); | ||
| 3381 | offset += 64; | ||
| 3382 | bytes += 64; | ||
| 3383 | } | ||
| 3384 | } | ||
| 3385 | sg_miter_stop(&miter); | ||
| 3386 | local_irq_restore(flags); | ||
| 3387 | |||
| 3388 | ata_scsi_qc_complete(qc); | ||
| 3389 | } | ||
| 3390 | |||
| 3391 | static unsigned int ata_scsi_zbc_in_xlat(struct ata_queued_cmd *qc) | ||
| 3392 | { | ||
| 3393 | struct ata_taskfile *tf = &qc->tf; | ||
| 3394 | struct scsi_cmnd *scmd = qc->scsicmd; | ||
| 3395 | const u8 *cdb = scmd->cmnd; | ||
| 3396 | u16 sect, fp = (u16)-1; | ||
| 3397 | u8 sa, options, bp = 0xff; | ||
| 3398 | u64 block; | ||
| 3399 | u32 n_block; | ||
| 3400 | |||
| 3401 | if (unlikely(scmd->cmd_len < 16)) { | ||
| 3402 | ata_dev_warn(qc->dev, "invalid cdb length %d\n", | ||
| 3403 | scmd->cmd_len); | ||
| 3404 | fp = 15; | ||
| 3405 | goto invalid_fld; | ||
| 3406 | } | ||
| 3407 | scsi_16_lba_len(cdb, &block, &n_block); | ||
| 3408 | if (n_block != scsi_bufflen(scmd)) { | ||
| 3409 | ata_dev_warn(qc->dev, "non-matching transfer count (%d/%d)\n", | ||
| 3410 | n_block, scsi_bufflen(scmd)); | ||
| 3411 | goto invalid_param_len; | ||
| 3412 | } | ||
| 3413 | sa = cdb[1] & 0x1f; | ||
| 3414 | if (sa != ZI_REPORT_ZONES) { | ||
| 3415 | ata_dev_warn(qc->dev, "invalid service action %d\n", sa); | ||
| 3416 | fp = 1; | ||
| 3417 | goto invalid_fld; | ||
| 3418 | } | ||
| 3419 | /* | ||
| 3420 | * ZAC allows only for transfers in 512 byte blocks, | ||
| 3421 | * and uses a 16 bit value for the transfer count. | ||
| 3422 | */ | ||
| 3423 | if ((n_block / 512) > 0xffff || n_block < 512 || (n_block % 512)) { | ||
| 3424 | ata_dev_warn(qc->dev, "invalid transfer count %d\n", n_block); | ||
| 3425 | goto invalid_param_len; | ||
| 3426 | } | ||
| 3427 | sect = n_block / 512; | ||
| 3428 | options = cdb[14]; | ||
| 3429 | |||
| 3430 | if (ata_ncq_enabled(qc->dev) && | ||
| 3431 | ata_fpdma_zac_mgmt_in_supported(qc->dev)) { | ||
| 3432 | tf->protocol = ATA_PROT_NCQ; | ||
| 3433 | tf->command = ATA_CMD_FPDMA_RECV; | ||
| 3434 | tf->hob_nsect = ATA_SUBCMD_FPDMA_RECV_ZAC_MGMT_IN & 0x1f; | ||
| 3435 | tf->nsect = qc->tag << 3; | ||
| 3436 | tf->feature = sect & 0xff; | ||
| 3437 | tf->hob_feature = (sect >> 8) & 0xff; | ||
| 3438 | tf->auxiliary = ATA_SUBCMD_ZAC_MGMT_IN_REPORT_ZONES; | ||
| 3439 | } else { | ||
| 3440 | tf->command = ATA_CMD_ZAC_MGMT_IN; | ||
| 3441 | tf->feature = ATA_SUBCMD_ZAC_MGMT_IN_REPORT_ZONES; | ||
| 3442 | tf->protocol = ATA_PROT_DMA; | ||
| 3443 | tf->hob_feature = options; | ||
| 3444 | tf->hob_nsect = (sect >> 8) & 0xff; | ||
| 3445 | tf->nsect = sect & 0xff; | ||
| 3446 | } | ||
| 3447 | tf->device = ATA_LBA; | ||
| 3448 | tf->lbah = (block >> 16) & 0xff; | ||
| 3449 | tf->lbam = (block >> 8) & 0xff; | ||
| 3450 | tf->lbal = block & 0xff; | ||
| 3451 | tf->hob_lbah = (block >> 40) & 0xff; | ||
| 3452 | tf->hob_lbam = (block >> 32) & 0xff; | ||
| 3453 | tf->hob_lbal = (block >> 24) & 0xff; | ||
| 3454 | |||
| 3455 | tf->flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE | ATA_TFLAG_LBA48; | ||
| 3456 | qc->flags |= ATA_QCFLAG_RESULT_TF; | ||
| 3457 | |||
| 3458 | ata_qc_set_pc_nbytes(qc); | ||
| 3459 | |||
| 3460 | qc->complete_fn = ata_scsi_report_zones_complete; | ||
| 3461 | |||
| 3462 | return 0; | ||
| 3463 | |||
| 3464 | invalid_fld: | ||
| 3465 | ata_scsi_set_invalid_field(qc->dev, scmd, fp, bp); | ||
| 3466 | return 1; | ||
| 3467 | |||
| 3468 | invalid_param_len: | ||
| 3469 | /* "Parameter list length error" */ | ||
| 3470 | ata_scsi_set_sense(qc->dev, scmd, ILLEGAL_REQUEST, 0x1a, 0x0); | ||
| 3471 | return 1; | ||
| 3472 | } | ||
| 3473 | |||
| 3474 | /** | ||
| 3321 | * ata_mselect_caching - Simulate MODE SELECT for caching info page | 3475 | * ata_mselect_caching - Simulate MODE SELECT for caching info page |
| 3322 | * @qc: Storage for translated ATA taskfile | 3476 | * @qc: Storage for translated ATA taskfile |
| 3323 | * @buf: input buffer | 3477 | * @buf: input buffer |
| @@ -3632,6 +3786,9 @@ static inline ata_xlat_func_t ata_get_xlat_func(struct ata_device *dev, u8 cmd) | |||
| 3632 | return ata_scsi_mode_select_xlat; | 3786 | return ata_scsi_mode_select_xlat; |
| 3633 | break; | 3787 | break; |
| 3634 | 3788 | ||
| 3789 | case ZBC_IN: | ||
| 3790 | return ata_scsi_zbc_in_xlat; | ||
| 3791 | |||
| 3635 | case START_STOP: | 3792 | case START_STOP: |
| 3636 | return ata_scsi_start_stop_xlat; | 3793 | return ata_scsi_start_stop_xlat; |
| 3637 | } | 3794 | } |
diff --git a/drivers/ata/libata-trace.c b/drivers/ata/libata-trace.c index 99ec1e8cb95d..9caeabd69ccb 100644 --- a/drivers/ata/libata-trace.c +++ b/drivers/ata/libata-trace.c | |||
| @@ -162,6 +162,9 @@ libata_trace_parse_subcmd(struct trace_seq *p, unsigned char cmd, | |||
| 162 | case ATA_SUBCMD_FPDMA_RECV_RD_LOG_DMA_EXT: | 162 | case ATA_SUBCMD_FPDMA_RECV_RD_LOG_DMA_EXT: |
| 163 | trace_seq_printf(p, " READ_LOG_DMA_EXT"); | 163 | trace_seq_printf(p, " READ_LOG_DMA_EXT"); |
| 164 | break; | 164 | break; |
| 165 | case ATA_SUBCMD_FPDMA_RECV_ZAC_MGMT_IN: | ||
