lkml.org 
[lkml]   [2008]   [Apr]   [17]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    Date
    From
    Subject[PATCH] libata: implement support for 32-bit PIO transfers
    Jeff,

    here's an update of the patch. Rebuilt and re-tested against 2.6.25.
    I've added Alan's previous Acked-by too btw.

    Regards,
    Willy


    From 734d62ad5b63108bfc244433aedaf6190543e279 Mon Sep 17 00:00:00 2001
    From: Willy Tarreau <w@1wt.eu>
    Date: Thu, 17 Apr 2008 23:59:41 +0200
    Subject: libata: implement support for 32-bit PIO transfers

    This patch implements ATA_IOC_GET_IO32/ATA_IOC_SET_IO32 ioctls and
    gets/sets the new ATA_DFLAG_32BIT_PIO ata flag accordingly for the
    device. It only supports values 0 and 1 (disable/enable), as I have
    no plan to implement the sync VLB transfers.

    When ATA_DFLAG_32BIT_PIO is set in ata flags, PIO transfers will be
    performed in 32-bit, just like with the plain old IDE drivers.

    16-bit transfers in libata are as slow as 16-bit transfers in plain
    old IDE drivers, which are about 35% slower than 32-bit transfers
    with various CF and DOM. This patch restores the performance to
    pre-libata level, which is a nice improvement for many small systems
    relying on such hardware. On an ALIX board equipped with a 64 MB CF
    card, transfer speed went up from 1.49 MB/sec to 2.41 MB/s, which
    represents a net gain of 61%.

    To enable 32-bit transfers, simply proceed like before with
    IDE :

    # hdparm -c 1 /dev/sda

    Signed-off-by: Willy Tarreau <w@1wt.eu>
    Acked-by: Alan Cox <alan@redhat.com>
    ---
    drivers/ata/libata-core.c | 46 ++++++++++++++++++--------
    drivers/ata/libata-scsi.c | 77 +++++++++++++++++++++++++++++++++++++-------
    include/linux/libata.h | 1 +
    3 files changed, 97 insertions(+), 27 deletions(-)

    diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
    index be95fdb..22c2643 100644
    --- a/drivers/ata/libata-core.c
    +++ b/drivers/ata/libata-core.c
    @@ -5036,7 +5036,7 @@ void swap_buf_le16(u16 *buf, unsigned int buf_words)
    }

    /**
    - * ata_data_xfer - Transfer data by PIO
    + * ata_data_xfer - Transfer data by PIO (16 or 32-bit)
    * @dev: device to target
    * @buf: data buffer
    * @buflen: buffer length
    @@ -5055,30 +5055,48 @@ unsigned int ata_data_xfer(struct ata_device *dev, unsigned char *buf,
    {
    struct ata_port *ap = dev->link->ap;
    void __iomem *data_addr = ap->ioaddr.data_addr;
    - unsigned int words = buflen >> 1;
    + unsigned int words;

    - /* Transfer multiple of 2 bytes */
    - if (rw == READ)
    - ioread16_rep(data_addr, buf, words);
    - else
    - iowrite16_rep(data_addr, buf, words);
    + if (dev->flags & ATA_DFLAG_32BIT_PIO) {
    + /* Transfer in 32-bit words */
    + words = buflen >> 2;
    + if (rw == READ)
    + ioread32_rep(data_addr, buf, words);
    + else
    + iowrite32_rep(data_addr, buf, words);
    + words <<= 2;
    + } else {
    + /* Transfer in 16-bit words */
    + words = buflen >> 1;
    + if (rw == READ)
    + ioread16_rep(data_addr, buf, words);
    + else
    + iowrite16_rep(data_addr, buf, words);
    + words <<= 1;
    + }

    - /* Transfer trailing 1 byte, if any. */
    - if (unlikely(buflen & 0x01)) {
    + /* Transfer trailing 1, 2 or 3 bytes, if any. We know how many bytes
    + * remain because <words> now contains the number of bytes transferred.
    + */
    + while (buflen > words) {
    __le16 align_buf[1] = { 0 };
    - unsigned char *trailing_buf = buf + buflen - 1;
    + unsigned char *trailing_buf = buf + words;
    + int len = buflen - words;
    +
    + if (len > 2)
    + len = 2;

    if (rw == READ) {
    align_buf[0] = cpu_to_le16(ioread16(data_addr));
    - memcpy(trailing_buf, align_buf, 1);
    + memcpy(trailing_buf, align_buf, len);
    } else {
    - memcpy(align_buf, trailing_buf, 1);
    + memcpy(align_buf, trailing_buf, len);
    iowrite16(le16_to_cpu(align_buf[0]), data_addr);
    }
    - words++;
    + words += len;
    }

    - return words << 1;
    + return words;
    }

    /**
    diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c
    index 1579539..c0205de 100644
    --- a/drivers/ata/libata-scsi.c
    +++ b/drivers/ata/libata-scsi.c
    @@ -448,22 +448,74 @@ int ata_task_ioctl(struct scsi_device *scsidev, void __user *arg)
    return rc;
    }

    -int ata_scsi_ioctl(struct scsi_device *scsidev, int cmd, void __user *arg)
    +/**
    + * ata_get_io32 - Handler for ATA_IOC_GET_IO32 ioctl
    + * @sdev: SCSI device to get identify data for
    + * @arg: User buffer area for return value
    + *
    + * LOCKING:
    + * Defined by the SCSI layer. We don't really care.
    + *
    + * RETURNS:
    + * Zero on success, negative errno on error.
    + */
    +static int ata_get_io32(struct scsi_device *sdev, void __user *arg)
    {
    - int val = -EINVAL, rc = -EINVAL;
    + struct ata_port *ap = ata_shost_to_port(sdev->host);
    + struct ata_device *dev = ata_scsi_find_dev(ap, sdev);
    + int val;
    +
    + if (!dev)
    + return -ENOMSG;
    +
    + val = (dev->flags & ATA_DFLAG_32BIT_PIO) != 0;
    + if (copy_to_user(arg, &val, 1))
    + return -EFAULT;
    +
    + return 0;
    +}

    +/**
    + * ata_set_io32 - Handler for ATA_IOC_SET_IO32 ioctl
    + * @sdev: SCSI device to get identify data for
    + * @arg: boolean integer value passed by user
    + *
    + * LOCKING:
    + * Defined by the SCSI layer. We don't really care.
    + *
    + * RETURNS:
    + * Zero on success, negative errno on error.
    + */
    +static int ata_set_io32(struct scsi_device *sdev, void __user *arg)
    +{
    + struct ata_port *ap = ata_shost_to_port(sdev->host);
    + struct ata_device *dev = ata_scsi_find_dev(ap, sdev);
    +
    + if (!dev)
    + return -ENOMSG;
    +
    + switch ((unsigned long) arg) {
    + case 0:
    + dev->flags &= ~ATA_DFLAG_32BIT_PIO;
    + return 0;
    + case 1:
    + dev->flags |= ATA_DFLAG_32BIT_PIO;
    + return 0;
    + default:
    + break;
    + }
    +
    + return -EFAULT;
    +}
    +
    +int ata_scsi_ioctl(struct scsi_device *scsidev, int cmd, void __user *arg)
    +{
    switch (cmd) {
    case ATA_IOC_GET_IO32:
    - val = 0;
    - if (copy_to_user(arg, &val, 1))
    - return -EFAULT;
    - return 0;
    + return ata_get_io32(scsidev, arg);

    case ATA_IOC_SET_IO32:
    - val = (unsigned long) arg;
    - if (val != 0)
    - return -EINVAL;
    - return 0;
    + return ata_set_io32(scsidev, arg);

    case HDIO_GET_IDENTITY:
    return ata_get_identity(scsidev, arg);
    @@ -479,11 +531,10 @@ int ata_scsi_ioctl(struct scsi_device *scsidev, int cmd, void __user *arg)
    return ata_task_ioctl(scsidev, arg);

    default:
    - rc = -ENOTTY;
    - break;
    + return -ENOTTY;
    }

    - return rc;
    + return -EINVAL;
    }

    /**
    diff --git a/include/linux/libata.h b/include/linux/libata.h
    index 37ee881..1f1a9be 100644
    --- a/include/linux/libata.h
    +++ b/include/linux/libata.h
    @@ -139,6 +139,7 @@ enum {
    ATA_DFLAG_HIPM = (1 << 8), /* device supports HIPM */
    ATA_DFLAG_DIPM = (1 << 9), /* device supports DIPM */
    ATA_DFLAG_DMADIR = (1 << 10), /* device requires DMADIR */
    + ATA_DFLAG_32BIT_PIO = (1 << 11), /* device supports 32-bit in PIO mode */
    ATA_DFLAG_CFG_MASK = (1 << 12) - 1,

    ATA_DFLAG_PIO = (1 << 12), /* device limited to PIO mode */
    --
    1.5.3.8


    \
     
     \ /
      Last update: 2008-04-18 00:17    [W:0.039 / U:237.076 seconds]
    ©2003-2016 Jasper Spaans. hosted at Digital OceanAdvertise on this site