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

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>

+57 -30
+37 -24
drivers/ata/libata-core.c
··· 2464 2464 dev->flags |= ATA_DFLAG_TRUSTED; 2465 2465 } 2466 2466 2467 + void ata_dev_cleanup_cdl_resources(struct ata_device *dev) 2468 + { 2469 + kfree(dev->cdl); 2470 + dev->cdl = NULL; 2471 + } 2472 + 2473 + static int ata_dev_init_cdl_resources(struct ata_device *dev) 2474 + { 2475 + struct ata_cdl *cdl = dev->cdl; 2476 + unsigned int err_mask; 2477 + 2478 + if (!cdl) { 2479 + cdl = kzalloc(sizeof(*cdl), GFP_KERNEL); 2480 + if (!cdl) 2481 + return -ENOMEM; 2482 + dev->cdl = cdl; 2483 + } 2484 + 2485 + err_mask = ata_read_log_page(dev, ATA_LOG_CDL, 0, cdl->desc_log_buf, 2486 + ATA_LOG_CDL_SIZE / ATA_SECT_SIZE); 2487 + if (err_mask) { 2488 + ata_dev_warn(dev, "Read Command Duration Limits log failed\n"); 2489 + ata_dev_cleanup_cdl_resources(dev); 2490 + return -EIO; 2491 + } 2492 + 2493 + return 0; 2494 + } 2495 + 2467 2496 static void ata_dev_config_cdl(struct ata_device *dev) 2468 2497 { 2469 - struct ata_port *ap = dev->link->ap; 2470 2498 unsigned int err_mask; 2471 2499 bool cdl_enabled; 2472 2500 u64 val; 2501 + int ret; 2473 2502 2474 2503 if (ata_id_major_version(dev->id) < 11) 2475 2504 goto not_supported; ··· 2593 2564 } 2594 2565 } 2595 2566 2596 - /* 2597 - * Allocate a buffer to handle reading the sense data for successful 2598 - * NCQ Commands log page for commands using a CDL with one of the limit 2599 - * policy set to 0xD (successful completion with sense data available 2600 - * bit set). 2601 - */ 2602 - if (!ap->ncq_sense_buf) { 2603 - ap->ncq_sense_buf = kmalloc(ATA_LOG_SENSE_NCQ_SIZE, GFP_KERNEL); 2604 - if (!ap->ncq_sense_buf) 2605 - goto not_supported; 2606 - } 2607 - 2608 - /* 2609 - * Command duration limits is supported: cache the CDL log page 18h 2610 - * (command duration descriptors). 2611 - */ 2612 - err_mask = ata_read_log_page(dev, ATA_LOG_CDL, 0, dev->sector_buf, 1); 2613 - if (err_mask) { 2614 - ata_dev_warn(dev, "Read Command Duration Limits log failed\n"); 2567 + /* CDL is supported: allocate and initialize needed resources. */ 2568 + ret = ata_dev_init_cdl_resources(dev); 2569 + if (ret) { 2570 + ata_dev_warn(dev, "Initialize CDL resources failed\n"); 2615 2571 goto not_supported; 2616 2572 } 2617 2573 2618 - memcpy(dev->cdl, dev->sector_buf, ATA_LOG_CDL_SIZE); 2619 2574 dev->flags |= ATA_DFLAG_CDL; 2620 2575 2621 2576 return; 2622 2577 2623 2578 not_supported: 2624 2579 dev->flags &= ~(ATA_DFLAG_CDL | ATA_DFLAG_CDL_ENABLED); 2625 - kfree(ap->ncq_sense_buf); 2626 - ap->ncq_sense_buf = NULL; 2580 + ata_dev_cleanup_cdl_resources(dev); 2627 2581 } 2628 2582 2629 2583 static int ata_dev_config_lba(struct ata_device *dev) ··· 5463 5451 5464 5452 kfree(ap->pmp_link); 5465 5453 kfree(ap->slave_link); 5466 - kfree(ap->ncq_sense_buf); 5467 5454 ida_free(&ata_ida, ap->print_id); 5468 5455 kfree(ap); 5469 5456 } ··· 6000 5989 { 6001 5990 if (zpodd_dev_enabled(dev)) 6002 5991 zpodd_exit(dev); 5992 + 5993 + ata_dev_cleanup_cdl_resources(dev); 6003 5994 } 6004 5995 6005 5996 /**
+1 -1
drivers/ata/libata-sata.c
··· 1505 1505 { 1506 1506 struct ata_device *dev = link->device; 1507 1507 struct ata_port *ap = dev->link->ap; 1508 - u8 *buf = ap->ncq_sense_buf; 1508 + u8 *buf = dev->cdl->ncq_sense_log_buf; 1509 1509 struct ata_queued_cmd *qc; 1510 1510 unsigned int err_mask, tag; 1511 1511 u8 *sense, sk = 0, asc = 0, ascq = 0;
+1 -1
drivers/ata/libata-scsi.c
··· 2248 2248 static unsigned int ata_msense_control_spgt2(struct ata_device *dev, u8 *buf, 2249 2249 u8 spg) 2250 2250 { 2251 - u8 *b, *cdl = dev->cdl, *desc; 2251 + u8 *b, *cdl = dev->cdl->desc_log_buf, *desc; 2252 2252 u32 policy; 2253 2253 int i; 2254 2254
+1
drivers/ata/libata.h
··· 90 90 extern const char *sata_spd_string(unsigned int spd); 91 91 extern unsigned int ata_read_log_page(struct ata_device *dev, u8 log, 92 92 u8 page, void *buf, unsigned int sectors); 93 + void ata_dev_cleanup_cdl_resources(struct ata_device *dev); 93 94 94 95 #define to_ata_port(d) container_of(d, struct ata_port, tdev) 95 96
+17 -4
include/linux/libata.h
··· 700 700 struct ata_cpr cpr[] __counted_by(nr_cpr); 701 701 }; 702 702 703 + struct ata_cdl { 704 + /* 705 + * Buffer to cache the CDL log page 18h (command duration descriptors) 706 + * for SCSI-ATA translation. 707 + */ 708 + u8 desc_log_buf[ATA_LOG_CDL_SIZE]; 709 + 710 + /* 711 + * Buffer to handle reading the sense data for successful NCQ Commands 712 + * log page for commands using a CDL with one of the limits policy set 713 + * to 0xD (successful completion with sense data available bit set). 714 + */ 715 + u8 ncq_sense_log_buf[ATA_LOG_SENSE_NCQ_SIZE]; 716 + }; 717 + 703 718 struct ata_device { 704 719 struct ata_link *link; 705 720 unsigned int devno; /* 0 or 1 */ ··· 777 762 /* Concurrent positioning ranges */ 778 763 struct ata_cpr_log *cpr_log; 779 764 780 - /* Command Duration Limits log support */ 781 - u8 cdl[ATA_LOG_CDL_SIZE]; 765 + /* Command Duration Limits support */ 766 + struct ata_cdl *cdl; 782 767 783 768 /* error history */ 784 769 int spdn_cnt; ··· 932 917 #ifdef CONFIG_ATA_ACPI 933 918 struct ata_acpi_gtm __acpi_init_gtm; /* use ata_acpi_init_gtm() */ 934 919 #endif 935 - /* owned by EH */ 936 - u8 *ncq_sense_buf; 937 920 }; 938 921 939 922 /* The following initializer overrides a method to NULL whether one of