]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
ata: libata-core: Cache the general purpose log directory
authorDamien Le Moal <dlemoal@kernel.org>
Thu, 3 Jul 2025 10:36:19 +0000 (19:36 +0900)
committerNiklas Cassel <cassel@kernel.org>
Fri, 4 Jul 2025 08:36:00 +0000 (10:36 +0200)
The function ata_log_supported() tests if a log page is supported by a
device using the General Purpose Log Directory log page, which lists the
size of all surported log pages. However, this log page is read from the
device using ata_read_log_page() every time ata_log_supported() is
called. That is not necessary.

Avoid reading the General Purpose Log Directory log page by caching its
content in the gp_log_dir buffer defined as part of struct ata_device.
The functions ata_read_log_directory() and ata_clear_log_directory() are
introduced to manage this buffer. ata_clear_log_directory() zero-fill
the gp_log_dir buffer every time ata_dev_configure() is called, that is,
when the device is first scanned and when it is being revalidated.
The function ata_log_supported() is modified to call
ata_read_log_directory() instead of ata_read_log_page().

The function ata_read_log_directory() calls ata_read_log_page() to read
the General Purpose Log Directory log page from the device only if the
first 16-bits word of the log is not equal to 0x0001, that is, it is not
equal to the ACS mandated value for the log version.

With this, the log page is read from the device only once for every
ata_dev_configure() call. For instance, with pr_debug enabled, a call
to ata_dev_configure() before this patch generates the following log
page accesses:

ata3.00: read log page - log 0x0, page 0x0
ata3.00: read log page - log 0x13, page 0x0
ata3.00: read log page - log 0x0, page 0x0
ata3.00: read log page - log 0x12, page 0x0
ata3.00: read log page - log 0x0, page 0x0
ata3.00: read log page - log 0x30, page 0x0
ata3.00: read log page - log 0x30, page 0x8
ata3.00: read log page - log 0x0, page 0x0
ata3.00: read log page - log 0x0, page 0x0
ata3.00: read log page - log 0x0, page 0x0
ata3.00: read log page - log 0x30, page 0x0
ata3.00: read log page - log 0x0, page 0x0
ata3.00: read log page - log 0x30, page 0x0
ata3.00: read log page - log 0x30, page 0x3
ata3.00: read log page - log 0x30, page 0x4
ata3.00: read log page - log 0x18, page 0x0

That is, the general purpose log directory page is read 7 times.
With this patch applied, the number of accesses to this log page is
reduced to one:

ata3.00: read log page - log 0x0, page 0x0
ata3.00: read log page - log 0x13, page 0x0
ata3.00: read log page - log 0x12, page 0x0
ata3.00: read log page - log 0x30, page 0x0
ata3.00: read log page - log 0x30, page 0x8
ata3.00: read log page - log 0x30, page 0x0
ata3.00: read log page - log 0x30, page 0x0
ata3.00: read log page - log 0x30, page 0x3
ata3.00: read log page - log 0x30, page 0x4
ata3.00: read log page - log 0x18, page 0x0

Signed-off-by: Damien Le Moal <dlemoal@kernel.org>
Reviewed-by: Niklas Cassel <cassel@kernel.org>
Link: https://lore.kernel.org/r/20250703103622.291272-2-dlemoal@kernel.org
Signed-off-by: Niklas Cassel <cassel@kernel.org>
drivers/ata/libata-core.c
include/linux/libata.h

index 7f6cebe61b33736351108a9ac9c80012edac9f8f..30913bc6fe2162ff39b190989994cbb4c564939b 100644 (file)
@@ -2154,14 +2154,46 @@ retry:
        return err_mask;
 }
 
+static inline void ata_clear_log_directory(struct ata_device *dev)
+{
+       memset(dev->gp_log_dir, 0, ATA_SECT_SIZE);
+}
+
+static int ata_read_log_directory(struct ata_device *dev)
+{
+       u16 version;
+
+       /* If the log page is already cached, do nothing. */
+       version = get_unaligned_le16(&dev->gp_log_dir[0]);
+       if (version == 0x0001)
+               return 0;
+
+       if (ata_read_log_page(dev, ATA_LOG_DIRECTORY, 0, dev->gp_log_dir, 1)) {
+               ata_clear_log_directory(dev);
+               return -EIO;
+       }
+
+       version = get_unaligned_le16(&dev->gp_log_dir[0]);
+       if (version != 0x0001) {
+               ata_dev_err(dev, "Invalid log directory version 0x%04x\n",
+                           version);
+               ata_clear_log_directory(dev);
+               dev->quirks |= ATA_QUIRK_NO_LOG_DIR;
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
 static int ata_log_supported(struct ata_device *dev, u8 log)
 {
        if (dev->quirks & ATA_QUIRK_NO_LOG_DIR)
                return 0;
 
-       if (ata_read_log_page(dev, ATA_LOG_DIRECTORY, 0, dev->sector_buf, 1))
+       if (ata_read_log_directory(dev))
                return 0;
-       return get_unaligned_le16(&dev->sector_buf[log * 2]);
+
+       return get_unaligned_le16(&dev->gp_log_dir[log * 2]);
 }
 
 static bool ata_identify_page_supported(struct ata_device *dev, u8 page)
@@ -2890,6 +2922,9 @@ int ata_dev_configure(struct ata_device *dev)
                return 0;
        }
 
+       /* Clear the general purpose log directory cache. */
+       ata_clear_log_directory(dev);
+
        /* Set quirks */
        dev->quirks |= ata_dev_quirks(dev);
        ata_force_quirks(dev);
index 7462218312ad5c0132dd5d1c7678e2987b280628..78a4addc6659cf94bd3dbbbf0b6f09798ceb3db5 100644 (file)
@@ -761,6 +761,9 @@ struct ata_device {
                u32             gscr[SATA_PMP_GSCR_DWORDS]; /* PMP GSCR block */
        } ____cacheline_aligned;
 
+       /* General Purpose Log Directory log page */
+       u8                      gp_log_dir[ATA_SECT_SIZE] ____cacheline_aligned;
+
        /* DEVSLP Timing Variables from Identify Device Data Log */
        u8                      devslp_timing[ATA_LOG_DEVSLP_SIZE];