Skip to content

Commit df60f9c

Browse files
damien-lemoalmartinkpetersen
authored andcommitted
scsi: ata: libata: Add ATA feature control sub-page translation
Add support for the ATA feature control sub-page of the control mode page to enable/disable the command duration limits feature using the cdl_ctrl field of the ATA feature control sub-page. Both mode sense and mode select translation are supported. For mode sense, the ata device flag ATA_DFLAG_CDL_ENABLED is used to cache the status of the command duration limits feature. Enabling this feature is done using a SET FEATURES command with a cdl action set to 1 when the page cdl_ctrl field value is 0x2 (T2A and T2B pages supported). If this field is 0, CDL is disabled using the SET FEATURES command with a cdl action set to 0. Since a device CDL and NCQ priority features should not be used simultaneously, ata_mselect_control_ata_feature() returns an error when attempting to enable CDL with the device priority feature enabled. Conversely, the function ata_ncq_prio_enable_store() used to enable the use of the device NCQ priority feature through sysfs is modified to return an error if the device CDL feature is enabled. Signed-off-by: Damien Le Moal <[email protected]> Reviewed-by: Hannes Reinecke <[email protected]> Co-developed-by: Niklas Cassel <[email protected]> Signed-off-by: Niklas Cassel <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Martin K. Petersen <[email protected]>
1 parent 673b2fe commit df60f9c

File tree

5 files changed

+193
-29
lines changed

5 files changed

+193
-29
lines changed

drivers/ata/libata-core.c

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2371,13 +2371,15 @@ static void ata_dev_config_cdl(struct ata_device *dev)
23712371
{
23722372
struct ata_port *ap = dev->link->ap;
23732373
unsigned int err_mask;
2374+
bool cdl_enabled;
23742375
u64 val;
23752376

23762377
if (ata_id_major_version(dev->id) < 12)
23772378
goto not_supported;
23782379

23792380
if (!ata_log_supported(dev, ATA_LOG_IDENTIFY_DEVICE) ||
2380-
!ata_identify_page_supported(dev, ATA_LOG_SUPPORTED_CAPABILITIES))
2381+
!ata_identify_page_supported(dev, ATA_LOG_SUPPORTED_CAPABILITIES) ||
2382+
!ata_identify_page_supported(dev, ATA_LOG_CURRENT_SETTINGS))
23812383
goto not_supported;
23822384

23832385
err_mask = ata_read_log_page(dev, ATA_LOG_IDENTIFY_DEVICE,
@@ -2396,6 +2398,40 @@ static void ata_dev_config_cdl(struct ata_device *dev)
23962398
ata_dev_warn(dev,
23972399
"Command duration guideline is not supported\n");
23982400

2401+
/*
2402+
* If CDL is marked as enabled, make sure the feature is enabled too.
2403+
* Conversely, if CDL is disabled, make sure the feature is turned off.
2404+
*/
2405+
err_mask = ata_read_log_page(dev, ATA_LOG_IDENTIFY_DEVICE,
2406+
ATA_LOG_CURRENT_SETTINGS,
2407+
ap->sector_buf, 1);
2408+
if (err_mask)
2409+
goto not_supported;
2410+
2411+
val = get_unaligned_le64(&ap->sector_buf[8]);
2412+
cdl_enabled = val & BIT_ULL(63) && val & BIT_ULL(21);
2413+
if (dev->flags & ATA_DFLAG_CDL_ENABLED) {
2414+
if (!cdl_enabled) {
2415+
/* Enable CDL on the device */
2416+
err_mask = ata_dev_set_feature(dev, SETFEATURES_CDL, 1);
2417+
if (err_mask) {
2418+
ata_dev_err(dev,
2419+
"Enable CDL feature failed\n");
2420+
goto not_supported;
2421+
}
2422+
}
2423+
} else {
2424+
if (cdl_enabled) {
2425+
/* Disable CDL on the device */
2426+
err_mask = ata_dev_set_feature(dev, SETFEATURES_CDL, 0);
2427+
if (err_mask) {
2428+
ata_dev_err(dev,
2429+
"Disable CDL feature failed\n");
2430+
goto not_supported;
2431+
}
2432+
}
2433+
}
2434+
23992435
/*
24002436
* Command duration limits is supported: cache the CDL log page 18h
24012437
* (command duration descriptors).
@@ -2412,7 +2448,7 @@ static void ata_dev_config_cdl(struct ata_device *dev)
24122448
return;
24132449

24142450
not_supported:
2415-
dev->flags &= ~ATA_DFLAG_CDL;
2451+
dev->flags &= ~(ATA_DFLAG_CDL | ATA_DFLAG_CDL_ENABLED);
24162452
}
24172453

24182454
static int ata_dev_config_lba(struct ata_device *dev)

drivers/ata/libata-sata.c

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -907,10 +907,17 @@ static ssize_t ata_ncq_prio_enable_store(struct device *device,
907907
goto unlock;
908908
}
909909

910-
if (input)
910+
if (input) {
911+
if (dev->flags & ATA_DFLAG_CDL_ENABLED) {
912+
ata_dev_err(dev,
913+
"CDL must be disabled to enable NCQ priority\n");
914+
rc = -EINVAL;
915+
goto unlock;
916+
}
911917
dev->flags |= ATA_DFLAG_NCQ_PRIO_ENABLED;
912-
else
918+
} else {
913919
dev->flags &= ~ATA_DFLAG_NCQ_PRIO_ENABLED;
920+
}
914921

915922
unlock:
916923
spin_unlock_irq(ap->lock);

drivers/ata/libata-scsi.c

Lines changed: 142 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ static struct ata_device *__ata_scsi_find_dev(struct ata_port *ap,
5858
#define CDL_T2A_SUB_MPAGE 0x07
5959
#define CDL_T2B_SUB_MPAGE 0x08
6060
#define CDL_T2_SUB_MPAGE_LEN 232
61+
#define ATA_FEATURE_SUB_MPAGE 0xf2
62+
#define ATA_FEATURE_SUB_MPAGE_LEN 16
6163

6264
static const u8 def_rw_recovery_mpage[RW_RECOVERY_MPAGE_LEN] = {
6365
RW_RECOVERY_MPAGE,
@@ -2286,6 +2288,31 @@ static unsigned int ata_msense_control_spgt2(struct ata_device *dev, u8 *buf,
22862288
return CDL_T2_SUB_MPAGE_LEN;
22872289
}
22882290

2291+
/*
2292+
* Simulate MODE SENSE control mode page, sub-page f2h
2293+
* (ATA feature control mode page).
2294+
*/
2295+
static unsigned int ata_msense_control_ata_feature(struct ata_device *dev,
2296+
u8 *buf)
2297+
{
2298+
/* PS=0, SPF=1 */
2299+
buf[0] = CONTROL_MPAGE | (1 << 6);
2300+
buf[1] = ATA_FEATURE_SUB_MPAGE;
2301+
2302+
/*
2303+
* The first four bytes of ATA Feature Control mode page are a header.
2304+
* The PAGE LENGTH field is the size of the page excluding the header.
2305+
*/
2306+
put_unaligned_be16(ATA_FEATURE_SUB_MPAGE_LEN - 4, &buf[2]);
2307+
2308+
if (dev->flags & ATA_DFLAG_CDL)
2309+
buf[4] = 0x02; /* Support T2A and T2B pages */
2310+
else
2311+
buf[4] = 0;
2312+
2313+
return ATA_FEATURE_SUB_MPAGE_LEN;
2314+
}
2315+
22892316
/**
22902317
* ata_msense_control - Simulate MODE SENSE control mode page
22912318
* @dev: ATA device of interest
@@ -2309,10 +2336,13 @@ static unsigned int ata_msense_control(struct ata_device *dev, u8 *buf,
23092336
case CDL_T2A_SUB_MPAGE:
23102337
case CDL_T2B_SUB_MPAGE:
23112338
return ata_msense_control_spgt2(dev, buf, spg);
2339+
case ATA_FEATURE_SUB_MPAGE:
2340+
return ata_msense_control_ata_feature(dev, buf);
23122341
case ALL_SUB_MPAGES:
23132342
n = ata_msense_control_spg0(dev, buf, changeable);
23142343
n += ata_msense_control_spgt2(dev, buf + n, CDL_T2A_SUB_MPAGE);
23152344
n += ata_msense_control_spgt2(dev, buf + n, CDL_T2A_SUB_MPAGE);
2345+
n += ata_msense_control_ata_feature(dev, buf + n);
23162346
return n;
23172347
default:
23182348
return 0;
@@ -2391,7 +2421,7 @@ static unsigned int ata_scsiop_mode_sense(struct ata_scsi_args *args, u8 *rbuf)
23912421
spg = scsicmd[3];
23922422

23932423
/*
2394-
* Supported subpages: all subpages and sub-pages 07h and 08h of
2424+
* Supported subpages: all subpages and sub-pages 07h, 08h and f2h of
23952425
* the control page.
23962426
*/
23972427
if (spg) {
@@ -2400,6 +2430,7 @@ static unsigned int ata_scsiop_mode_sense(struct ata_scsi_args *args, u8 *rbuf)
24002430
break;
24012431
case CDL_T2A_SUB_MPAGE:
24022432
case CDL_T2B_SUB_MPAGE:
2433+
case ATA_FEATURE_SUB_MPAGE:
24032434
if (dev->flags & ATA_DFLAG_CDL && pg == CONTROL_MPAGE)
24042435
break;
24052436
fallthrough;
@@ -3708,20 +3739,11 @@ static int ata_mselect_caching(struct ata_queued_cmd *qc,
37083739
return 0;
37093740
}
37103741

3711-
/**
3712-
* ata_mselect_control - Simulate MODE SELECT for control page
3713-
* @qc: Storage for translated ATA taskfile
3714-
* @buf: input buffer
3715-
* @len: number of valid bytes in the input buffer
3716-
* @fp: out parameter for the failed field on error
3717-
*
3718-
* Prepare a taskfile to modify caching information for the device.
3719-
*
3720-
* LOCKING:
3721-
* None.
3742+
/*
3743+
* Simulate MODE SELECT control mode page, sub-page 0.
37223744
*/
3723-
static int ata_mselect_control(struct ata_queued_cmd *qc,
3724-
const u8 *buf, int len, u16 *fp)
3745+
static int ata_mselect_control_spg0(struct ata_queued_cmd *qc,
3746+
const u8 *buf, int len, u16 *fp)
37253747
{
37263748
struct ata_device *dev = qc->dev;
37273749
u8 mpage[CONTROL_MPAGE_LEN];
@@ -3759,6 +3781,83 @@ static int ata_mselect_control(struct ata_queued_cmd *qc,
37593781
return 0;
37603782
}
37613783

3784+
/*
3785+
* Translate MODE SELECT control mode page, sub-pages f2h (ATA feature mode
3786+
* page) into a SET FEATURES command.
3787+
*/
3788+
static unsigned int ata_mselect_control_ata_feature(struct ata_queued_cmd *qc,
3789+
const u8 *buf, int len,
3790+
u16 *fp)
3791+
{
3792+
struct ata_device *dev = qc->dev;
3793+
struct ata_taskfile *tf = &qc->tf;
3794+
u8 cdl_action;
3795+
3796+
/*
3797+
* The first four bytes of ATA Feature Control mode page are a header,
3798+
* so offsets in mpage are off by 4 compared to buf. Same for len.
3799+
*/
3800+
if (len != ATA_FEATURE_SUB_MPAGE_LEN - 4) {
3801+
*fp = min(len, ATA_FEATURE_SUB_MPAGE_LEN - 4);
3802+
return -EINVAL;
3803+
}
3804+
3805+
/* Check cdl_ctrl */
3806+
switch (buf[0] & 0x03) {
3807+
case 0:
3808+
/* Disable CDL */
3809+
cdl_action = 0;
3810+
dev->flags &= ~ATA_DFLAG_CDL_ENABLED;
3811+
break;
3812+
case 0x02:
3813+
/* Enable CDL T2A/T2B: NCQ priority must be disabled */
3814+
if (dev->flags & ATA_DFLAG_NCQ_PRIO_ENABLED) {
3815+
ata_dev_err(dev,
3816+
"NCQ priority must be disabled to enable CDL\n");
3817+
return -EINVAL;
3818+
}
3819+
cdl_action = 1;
3820+
dev->flags |= ATA_DFLAG_CDL_ENABLED;
3821+
break;
3822+
default:
3823+
*fp = 0;
3824+
return -EINVAL;
3825+
}
3826+
3827+
tf->flags |= ATA_TFLAG_DEVICE | ATA_TFLAG_ISADDR;
3828+
tf->protocol = ATA_PROT_NODATA;
3829+
tf->command = ATA_CMD_SET_FEATURES;
3830+
tf->feature = SETFEATURES_CDL;
3831+
tf->nsect = cdl_action;
3832+
3833+
return 1;
3834+
}
3835+
3836+
/**
3837+
* ata_mselect_control - Simulate MODE SELECT for control page
3838+
* @qc: Storage for translated ATA taskfile
3839+
* @buf: input buffer
3840+
* @len: number of valid bytes in the input buffer
3841+
* @fp: out parameter for the failed field on error
3842+
*
3843+
* Prepare a taskfile to modify caching information for the device.
3844+
*
3845+
* LOCKING:
3846+
* None.
3847+
*/
3848+
static int ata_mselect_control(struct ata_queued_cmd *qc, u8 spg,
3849+
const u8 *buf, int len, u16 *fp)
3850+
{
3851+
switch (spg) {
3852+
case 0:
3853+
return ata_mselect_control_spg0(qc, buf, len, fp);
3854+
case ATA_FEATURE_SUB_MPAGE:
3855+
return ata_mselect_control_ata_feature(qc, buf, len, fp);
3856+
default:
3857+
return -EINVAL;
3858+
}
3859+
}
3860+
37623861
/**
37633862
* ata_scsi_mode_select_xlat - Simulate MODE SELECT 6, 10 commands
37643863
* @qc: Storage for translated ATA taskfile
@@ -3776,7 +3875,7 @@ static unsigned int ata_scsi_mode_select_xlat(struct ata_queued_cmd *qc)
37763875
const u8 *cdb = scmd->cmnd;
37773876
u8 pg, spg;
37783877
unsigned six_byte, pg_len, hdr_len, bd_len;
3779-
int len;
3878+
int len, ret;
37803879
u16 fp = (u16)-1;
37813880
u8 bp = 0xff;
37823881
u8 buffer[64];
@@ -3861,13 +3960,29 @@ static unsigned int ata_scsi_mode_select_xlat(struct ata_queued_cmd *qc)
38613960
}
38623961

38633962
/*
3864-
* No mode subpages supported (yet) but asking for _all_
3865-
* subpages may be valid
3963+
* Supported subpages: all subpages and ATA feature sub-page f2h of
3964+
* the control page.
38663965
*/
3867-
if (spg && (spg != ALL_SUB_MPAGES)) {
3868-
fp = (p[0] & 0x40) ? 1 : 0;
3869-
fp += hdr_len + bd_len;
3870-
goto invalid_param;
3966+
if (spg) {
3967+
switch (spg) {
3968+
case ALL_SUB_MPAGES:
3969+
/* All subpages is not supported for the control page */
3970+
if (pg == CONTROL_MPAGE) {
3971+
fp = (p[0] & 0x40) ? 1 : 0;
3972+
fp += hdr_len + bd_len;
3973+
goto invalid_param;
3974+
}
3975+
break;
3976+
case ATA_FEATURE_SUB_MPAGE:
3977+
if (qc->dev->flags & ATA_DFLAG_CDL &&
3978+
pg == CONTROL_MPAGE)
3979+
break;
3980+
fallthrough;
3981+
default:
3982+
fp = (p[0] & 0x40) ? 1 : 0;
3983+
fp += hdr_len + bd_len;
3984+
goto invalid_param;
3985+
}
38713986
}
38723987
if (pg_len > len)
38733988
goto invalid_param_len;
@@ -3880,14 +3995,16 @@ static unsigned int ata_scsi_mode_select_xlat(struct ata_queued_cmd *qc)
38803995
}
38813996
break;
38823997
case CONTROL_MPAGE:
3883-
if (ata_mselect_control(qc, p, pg_len, &fp) < 0) {
3998+
ret = ata_mselect_control(qc, spg, p, pg_len, &fp);
3999+
if (ret < 0) {
38844000
fp += hdr_len + bd_len;
38854001
goto invalid_param;
3886-
} else {
3887-
goto skip; /* No ATA command to send */
38884002
}
4003+
if (!ret)
4004+
goto skip; /* No ATA command to send */
38894005
break;
3890-
default: /* invalid page code */
4006+
default:
4007+
/* Invalid page code */
38914008
fp = bd_len + hdr_len;
38924009
goto invalid_param;
38934010
}

include/linux/ata.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,7 @@ enum {
329329

330330
/* Identify device log pages: */
331331
ATA_LOG_SUPPORTED_CAPABILITIES = 0x03,
332+
ATA_LOG_CURRENT_SETTINGS = 0x04,
332333
ATA_LOG_SECURITY = 0x06,
333334
ATA_LOG_SATA_SETTINGS = 0x08,
334335
ATA_LOG_ZONED_INFORMATION = 0x09,
@@ -418,6 +419,8 @@ enum {
418419
SETFEATURES_SATA_ENABLE = 0x10, /* Enable use of SATA feature */
419420
SETFEATURES_SATA_DISABLE = 0x90, /* Disable use of SATA feature */
420421

422+
SETFEATURES_CDL = 0x0d, /* Enable/disable cmd duration limits */
423+
421424
/* SETFEATURE Sector counts for SATA features */
422425
SATA_FPDMA_OFFSET = 0x01, /* FPDMA non-zero buffer offsets */
423426
SATA_FPDMA_AA = 0x02, /* FPDMA Setup FIS Auto-Activate */

include/linux/libata.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ enum {
106106
ATA_DFLAG_INIT_MASK = (1 << 20) - 1,
107107

108108
ATA_DFLAG_NCQ_PRIO_ENABLED = (1 << 20), /* Priority cmds sent to dev */
109+
ATA_DFLAG_CDL_ENABLED = (1 << 21), /* cmd duration limits is enabled */
109110
ATA_DFLAG_DETACH = (1 << 24),
110111
ATA_DFLAG_DETACHED = (1 << 25),
111112
ATA_DFLAG_DA = (1 << 26), /* device supports Device Attention */

0 commit comments

Comments
 (0)