mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2025-01-01 10:45:49 +00:00
ata: libata: Improve CDL resource management
The ncq_sense_buf buffer field of struct ata_port is allocated and used only for devices that support the Command Duration Limits (CDL) feature. However, the cdl buffer of struct ata_device, which is used to cache the command duration limits log page for devices supporting CDL is always allocated as part of struct ata_device, which is wasteful of memory for devices that do not support this feature. Clean this up by defining both buffers as part of the new ata_cdl structure and allocating this structure only for devices that support the CDL feature. This new structure is attached to struct ata_device using the cdl pointer. The functions ata_dev_init_cdl_resources() and ata_dev_cleanup_cdl_resources() are defined to manage this new structure allocation, initialization and freeing when a port is removed or a device disabled. ata_dev_init_cdl_resources() is called from ata_dev_config_cdl() only for devices that support CDL. ata_dev_cleanup_cdl_resources() is called from ata_dev_free_resources() to free the ata_cdl structure when a device is being disabled by EH. Note that the name of the former cdl log buffer of struct ata_device is changed to desc_log_buf to make it clearer that it is a buffer for the limit descriptors log page. This change reduces the size of struct ata_device, thus reducing memory usage for ATA devices that do not support the CDL feature. Signed-off-by: Damien Le Moal <dlemoal@kernel.org> Reviewed-by: Niklas Cassel <cassel@kernel.org>
This commit is contained in:
parent
5f8319c4b3
commit
602bcf2126
@ -2464,12 +2464,41 @@ static void ata_dev_config_trusted(struct ata_device *dev)
|
|||||||
dev->flags |= ATA_DFLAG_TRUSTED;
|
dev->flags |= ATA_DFLAG_TRUSTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ata_dev_cleanup_cdl_resources(struct ata_device *dev)
|
||||||
|
{
|
||||||
|
kfree(dev->cdl);
|
||||||
|
dev->cdl = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ata_dev_init_cdl_resources(struct ata_device *dev)
|
||||||
|
{
|
||||||
|
struct ata_cdl *cdl = dev->cdl;
|
||||||
|
unsigned int err_mask;
|
||||||
|
|
||||||
|
if (!cdl) {
|
||||||
|
cdl = kzalloc(sizeof(*cdl), GFP_KERNEL);
|
||||||
|
if (!cdl)
|
||||||
|
return -ENOMEM;
|
||||||
|
dev->cdl = cdl;
|
||||||
|
}
|
||||||
|
|
||||||
|
err_mask = ata_read_log_page(dev, ATA_LOG_CDL, 0, cdl->desc_log_buf,
|
||||||
|
ATA_LOG_CDL_SIZE / ATA_SECT_SIZE);
|
||||||
|
if (err_mask) {
|
||||||
|
ata_dev_warn(dev, "Read Command Duration Limits log failed\n");
|
||||||
|
ata_dev_cleanup_cdl_resources(dev);
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static void ata_dev_config_cdl(struct ata_device *dev)
|
static void ata_dev_config_cdl(struct ata_device *dev)
|
||||||
{
|
{
|
||||||
struct ata_port *ap = dev->link->ap;
|
|
||||||
unsigned int err_mask;
|
unsigned int err_mask;
|
||||||
bool cdl_enabled;
|
bool cdl_enabled;
|
||||||
u64 val;
|
u64 val;
|
||||||
|
int ret;
|
||||||
|
|
||||||
if (ata_id_major_version(dev->id) < 11)
|
if (ata_id_major_version(dev->id) < 11)
|
||||||
goto not_supported;
|
goto not_supported;
|
||||||
@ -2564,37 +2593,20 @@ static void ata_dev_config_cdl(struct ata_device *dev)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/* CDL is supported: allocate and initialize needed resources. */
|
||||||
* Allocate a buffer to handle reading the sense data for successful
|
ret = ata_dev_init_cdl_resources(dev);
|
||||||
* NCQ Commands log page for commands using a CDL with one of the limit
|
if (ret) {
|
||||||
* policy set to 0xD (successful completion with sense data available
|
ata_dev_warn(dev, "Initialize CDL resources failed\n");
|
||||||
* bit set).
|
|
||||||
*/
|
|
||||||
if (!ap->ncq_sense_buf) {
|
|
||||||
ap->ncq_sense_buf = kmalloc(ATA_LOG_SENSE_NCQ_SIZE, GFP_KERNEL);
|
|
||||||
if (!ap->ncq_sense_buf)
|
|
||||||
goto not_supported;
|
goto not_supported;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Command duration limits is supported: cache the CDL log page 18h
|
|
||||||
* (command duration descriptors).
|
|
||||||
*/
|
|
||||||
err_mask = ata_read_log_page(dev, ATA_LOG_CDL, 0, dev->sector_buf, 1);
|
|
||||||
if (err_mask) {
|
|
||||||
ata_dev_warn(dev, "Read Command Duration Limits log failed\n");
|
|
||||||
goto not_supported;
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(dev->cdl, dev->sector_buf, ATA_LOG_CDL_SIZE);
|
|
||||||
dev->flags |= ATA_DFLAG_CDL;
|
dev->flags |= ATA_DFLAG_CDL;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
not_supported:
|
not_supported:
|
||||||
dev->flags &= ~(ATA_DFLAG_CDL | ATA_DFLAG_CDL_ENABLED);
|
dev->flags &= ~(ATA_DFLAG_CDL | ATA_DFLAG_CDL_ENABLED);
|
||||||
kfree(ap->ncq_sense_buf);
|
ata_dev_cleanup_cdl_resources(dev);
|
||||||
ap->ncq_sense_buf = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ata_dev_config_lba(struct ata_device *dev)
|
static int ata_dev_config_lba(struct ata_device *dev)
|
||||||
@ -5451,7 +5463,6 @@ void ata_port_free(struct ata_port *ap)
|
|||||||
|
|
||||||
kfree(ap->pmp_link);
|
kfree(ap->pmp_link);
|
||||||
kfree(ap->slave_link);
|
kfree(ap->slave_link);
|
||||||
kfree(ap->ncq_sense_buf);
|
|
||||||
ida_free(&ata_ida, ap->print_id);
|
ida_free(&ata_ida, ap->print_id);
|
||||||
kfree(ap);
|
kfree(ap);
|
||||||
}
|
}
|
||||||
@ -5989,6 +6000,8 @@ void ata_dev_free_resources(struct ata_device *dev)
|
|||||||
{
|
{
|
||||||
if (zpodd_dev_enabled(dev))
|
if (zpodd_dev_enabled(dev))
|
||||||
zpodd_exit(dev);
|
zpodd_exit(dev);
|
||||||
|
|
||||||
|
ata_dev_cleanup_cdl_resources(dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1505,7 +1505,7 @@ int ata_eh_get_ncq_success_sense(struct ata_link *link)
|
|||||||
{
|
{
|
||||||
struct ata_device *dev = link->device;
|
struct ata_device *dev = link->device;
|
||||||
struct ata_port *ap = dev->link->ap;
|
struct ata_port *ap = dev->link->ap;
|
||||||
u8 *buf = ap->ncq_sense_buf;
|
u8 *buf = dev->cdl->ncq_sense_log_buf;
|
||||||
struct ata_queued_cmd *qc;
|
struct ata_queued_cmd *qc;
|
||||||
unsigned int err_mask, tag;
|
unsigned int err_mask, tag;
|
||||||
u8 *sense, sk = 0, asc = 0, ascq = 0;
|
u8 *sense, sk = 0, asc = 0, ascq = 0;
|
||||||
|
@ -2248,7 +2248,7 @@ static inline u16 ata_xlat_cdl_limit(u8 *buf)
|
|||||||
static unsigned int ata_msense_control_spgt2(struct ata_device *dev, u8 *buf,
|
static unsigned int ata_msense_control_spgt2(struct ata_device *dev, u8 *buf,
|
||||||
u8 spg)
|
u8 spg)
|
||||||
{
|
{
|
||||||
u8 *b, *cdl = dev->cdl, *desc;
|
u8 *b, *cdl = dev->cdl->desc_log_buf, *desc;
|
||||||
u32 policy;
|
u32 policy;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
@ -90,6 +90,7 @@ extern int ata_cmd_ioctl(struct scsi_device *scsidev, void __user *arg);
|
|||||||
extern const char *sata_spd_string(unsigned int spd);
|
extern const char *sata_spd_string(unsigned int spd);
|
||||||
extern unsigned int ata_read_log_page(struct ata_device *dev, u8 log,
|
extern unsigned int ata_read_log_page(struct ata_device *dev, u8 log,
|
||||||
u8 page, void *buf, unsigned int sectors);
|
u8 page, void *buf, unsigned int sectors);
|
||||||
|
void ata_dev_cleanup_cdl_resources(struct ata_device *dev);
|
||||||
|
|
||||||
#define to_ata_port(d) container_of(d, struct ata_port, tdev)
|
#define to_ata_port(d) container_of(d, struct ata_port, tdev)
|
||||||
|
|
||||||
|
@ -700,6 +700,21 @@ struct ata_cpr_log {
|
|||||||
struct ata_cpr cpr[] __counted_by(nr_cpr);
|
struct ata_cpr cpr[] __counted_by(nr_cpr);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ata_cdl {
|
||||||
|
/*
|
||||||
|
* Buffer to cache the CDL log page 18h (command duration descriptors)
|
||||||
|
* for SCSI-ATA translation.
|
||||||
|
*/
|
||||||
|
u8 desc_log_buf[ATA_LOG_CDL_SIZE];
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Buffer to handle reading the sense data for successful NCQ Commands
|
||||||
|
* log page for commands using a CDL with one of the limits policy set
|
||||||
|
* to 0xD (successful completion with sense data available bit set).
|
||||||
|
*/
|
||||||
|
u8 ncq_sense_log_buf[ATA_LOG_SENSE_NCQ_SIZE];
|
||||||
|
};
|
||||||
|
|
||||||
struct ata_device {
|
struct ata_device {
|
||||||
struct ata_link *link;
|
struct ata_link *link;
|
||||||
unsigned int devno; /* 0 or 1 */
|
unsigned int devno; /* 0 or 1 */
|
||||||
@ -762,8 +777,8 @@ struct ata_device {
|
|||||||
/* Concurrent positioning ranges */
|
/* Concurrent positioning ranges */
|
||||||
struct ata_cpr_log *cpr_log;
|
struct ata_cpr_log *cpr_log;
|
||||||
|
|
||||||
/* Command Duration Limits log support */
|
/* Command Duration Limits support */
|
||||||
u8 cdl[ATA_LOG_CDL_SIZE];
|
struct ata_cdl *cdl;
|
||||||
|
|
||||||
/* error history */
|
/* error history */
|
||||||
int spdn_cnt;
|
int spdn_cnt;
|
||||||
@ -917,8 +932,6 @@ struct ata_port {
|
|||||||
#ifdef CONFIG_ATA_ACPI
|
#ifdef CONFIG_ATA_ACPI
|
||||||
struct ata_acpi_gtm __acpi_init_gtm; /* use ata_acpi_init_gtm() */
|
struct ata_acpi_gtm __acpi_init_gtm; /* use ata_acpi_init_gtm() */
|
||||||
#endif
|
#endif
|
||||||
/* owned by EH */
|
|
||||||
u8 *ncq_sense_buf;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/* The following initializer overrides a method to NULL whether one of
|
/* The following initializer overrides a method to NULL whether one of
|
||||||
|
Loading…
Reference in New Issue
Block a user