Skip to content

Commit

Permalink
[libata] Add ATA transport class
Browse files Browse the repository at this point in the history
This is a scheleton for libata transport class.
All information is read only, exporting information from libata:
- ata_port class: one per ATA port
- ata_link class: one per ATA port or 15 for SATA Port Multiplier
- ata_device class: up to 2 for PATA link, usually one for SATA.

Signed-off-by: Gwendal Grignou <gwendal@google.com>
Reviewed-by: Grant Grundler <grundler@google.com>
Signed-off-by: Jeff Garzik <jgarzik@redhat.com>
  • Loading branch information
Gwendal Grignou authored and Jeff Garzik committed Oct 22, 2010
1 parent f6f94e2 commit d902747
Show file tree
Hide file tree
Showing 10 changed files with 987 additions and 40 deletions.
99 changes: 99 additions & 0 deletions Documentation/ABI/testing/sysfs-ata
@@ -0,0 +1,99 @@
What: /sys/class/ata_...
Date: August 2008
Contact: Gwendal Grignou<gwendal@google.com>
Description:

Provide a place in sysfs for storing the ATA topology of the system. This allows
retrieving various information about ATA objects.

Files under /sys/class/ata_port
-------------------------------

For each port, a directory ataX is created where X is the ata_port_id of
the port. The device parent is the ata host device.

idle_irq (read)

Number of IRQ received by the port while idle [some ata HBA only].

nr_pmp_links (read)

If a SATA Port Multiplier (PM) is connected, number of link behind it.

Files under /sys/class/ata_link
-------------------------------

Behind each port, there is a ata_link. If there is a SATA PM in the
topology, 15 ata_link objects are created.

If a link is behind a port, the directory name is linkX, where X is
ata_port_id of the port.
If a link is behind a PM, its name is linkX.Y where X is ata_port_id
of the parent port and Y the PM port.

hw_sata_spd_limit

Maximum speed supported by the connected SATA device.

sata_spd_limit

Maximum speed imposed by libata.

sata_spd

Current speed of the link [1.5, 3Gps,...].

Files under /sys/class/ata_device
---------------------------------

Behind each link, up to two ata device are created.
The name of the directory is devX[.Y].Z where:
- X is ata_port_id of the port where the device is connected,
- Y the port of the PM if any, and
- Z the device id: for PATA, there is usually 2 devices [0,1],
only 1 for SATA.

class
Device class. Can be "ata" for disk, "atapi" for packet device,
"pmp" for PM, or "none" if no device was found behind the link.

dma_mode

Transfer modes supported by the device when in DMA mode.
Mostly used by PATA device.

pio_mode

Transfer modes supported by the device when in PIO mode.
Mostly used by PATA device.

xfer_mode

Current transfer mode.

id

Cached result of IDENTIFY command, as described in ATA8 7.16 and 7.17.
Only valid if the device is not a PM.

gscr

Cached result of the dump of PM GSCR register.
Valid registers are:
0: SATA_PMP_GSCR_PROD_ID,
1: SATA_PMP_GSCR_REV,
2: SATA_PMP_GSCR_PORT_INFO,
32: SATA_PMP_GSCR_ERROR,
33: SATA_PMP_GSCR_ERROR_EN,
64: SATA_PMP_GSCR_FEAT,
96: SATA_PMP_GSCR_FEAT_EN,
130: SATA_PMP_GSCR_SII_GPIO
Only valid if the device is a PM.

spdn_cnt

Number of time libata decided to lower the speed of link due to errors.

ering

Formatted output of the error ring of the device.
2 changes: 1 addition & 1 deletion drivers/ata/Makefile
Expand Up @@ -99,7 +99,7 @@ obj-$(CONFIG_ATA_GENERIC) += ata_generic.o
# Should be last libata driver
obj-$(CONFIG_PATA_LEGACY) += pata_legacy.o

libata-objs := libata-core.o libata-scsi.o libata-eh.o
libata-objs := libata-core.o libata-scsi.o libata-eh.o libata-transport.o
libata-$(CONFIG_ATA_SFF) += libata-sff.o
libata-$(CONFIG_SATA_PMP) += libata-pmp.o
libata-$(CONFIG_ATA_ACPI) += libata-acpi.o
49 changes: 43 additions & 6 deletions drivers/ata/libata-core.c
Expand Up @@ -68,7 +68,7 @@
#include <linux/ratelimit.h>

#include "libata.h"

#include "libata-transport.h"

/* debounce timing parameters in msecs { interval, duration, timeout } */
const unsigned long sata_deb_timing_normal[] = { 5, 100, 2000 };
Expand Down Expand Up @@ -1017,7 +1017,7 @@ const char *ata_mode_string(unsigned long xfer_mask)
return "<n/a>";
}

static const char *sata_spd_string(unsigned int spd)
const char *sata_spd_string(unsigned int spd)
{
static const char * const spd_str[] = {
"1.5 Gbps",
Expand Down Expand Up @@ -5517,7 +5517,8 @@ void ata_link_init(struct ata_port *ap, struct ata_link *link, int pmp)
int i;

/* clear everything except for devices */
memset(link, 0, offsetof(struct ata_link, device[0]));
memset((void *)link + ATA_LINK_CLEAR_BEGIN, 0,
ATA_LINK_CLEAR_END - ATA_LINK_CLEAR_BEGIN);

link->ap = ap;
link->pmp = pmp;
Expand Down Expand Up @@ -5591,7 +5592,7 @@ struct ata_port *ata_port_alloc(struct ata_host *host)
ap = kzalloc(sizeof(*ap), GFP_KERNEL);
if (!ap)
return NULL;

ap->pflags |= ATA_PFLAG_INITIALIZING;
ap->lock = &host->lock;
ap->print_id = -1;
Expand Down Expand Up @@ -6093,9 +6094,18 @@ int ata_host_register(struct ata_host *host, struct scsi_host_template *sht)
for (i = 0; i < host->n_ports; i++)
host->ports[i]->print_id = ata_print_id++;


/* Create associated sysfs transport objects */
for (i = 0; i < host->n_ports; i++) {
rc = ata_tport_add(host->dev,host->ports[i]);
if (rc) {
goto err_tadd;
}
}

rc = ata_scsi_add_hosts(host, sht);
if (rc)
return rc;
goto err_tadd;

/* associate with ACPI nodes */
ata_acpi_associate(host);
Expand Down Expand Up @@ -6136,6 +6146,13 @@ int ata_host_register(struct ata_host *host, struct scsi_host_template *sht)
}

return 0;

err_tadd:
while (--i >= 0) {
ata_tport_delete(host->ports[i]);
}
return rc;

}

/**
Expand Down Expand Up @@ -6226,6 +6243,13 @@ static void ata_port_detach(struct ata_port *ap)
cancel_rearming_delayed_work(&ap->hotplug_task);

skip_eh:
if (ap->pmp_link) {
int i;
for (i = 0; i < SATA_PMP_MAX_PORTS; i++)
ata_tlink_delete(&ap->pmp_link[i]);
}
ata_tport_delete(ap);

/* remove the associated SCSI host */
scsi_remove_host(ap->scsi_host);
}
Expand Down Expand Up @@ -6542,7 +6566,7 @@ static void __init ata_parse_force_param(void)

static int __init ata_init(void)
{
int rc = -ENOMEM;
int rc;

ata_parse_force_param();

Expand All @@ -6552,12 +6576,25 @@ static int __init ata_init(void)
return rc;
}

libata_transport_init();
ata_scsi_transport_template = ata_attach_transport();
if (!ata_scsi_transport_template) {
ata_sff_exit();
rc = -ENOMEM;
goto err_out;
}

printk(KERN_DEBUG "libata version " DRV_VERSION " loaded.\n");
return 0;

err_out:
return rc;
}

static void __exit ata_exit(void)
{
ata_release_transport(ata_scsi_transport_template);
libata_transport_exit();
ata_sff_exit();
kfree(ata_force_tbl);
}
Expand Down
35 changes: 21 additions & 14 deletions drivers/ata/libata-eh.c
Expand Up @@ -57,6 +57,7 @@ enum {
/* error flags */
ATA_EFLAG_IS_IO = (1 << 0),
ATA_EFLAG_DUBIOUS_XFER = (1 << 1),
ATA_EFLAG_OLD_ER = (1 << 31),

/* error categories */
ATA_ECAT_NONE = 0,
Expand Down Expand Up @@ -396,14 +397,9 @@ static struct ata_ering_entry *ata_ering_top(struct ata_ering *ering)
return NULL;
}

static void ata_ering_clear(struct ata_ering *ering)
{
memset(ering, 0, sizeof(*ering));
}

static int ata_ering_map(struct ata_ering *ering,
int (*map_fn)(struct ata_ering_entry *, void *),
void *arg)
int ata_ering_map(struct ata_ering *ering,
int (*map_fn)(struct ata_ering_entry *, void *),
void *arg)
{
int idx, rc = 0;
struct ata_ering_entry *ent;
Expand All @@ -422,6 +418,17 @@ static int ata_ering_map(struct ata_ering *ering,
return rc;
}

int ata_ering_clear_cb(struct ata_ering_entry *ent, void *void_arg)
{
ent->eflags |= ATA_EFLAG_OLD_ER;
return 0;
}

static void ata_ering_clear(struct ata_ering *ering)
{
ata_ering_map(ering, ata_ering_clear_cb, NULL);
}

static unsigned int ata_eh_dev_action(struct ata_device *dev)
{
struct ata_eh_context *ehc = &dev->link->eh_context;
Expand Down Expand Up @@ -572,19 +579,19 @@ void ata_scsi_error(struct Scsi_Host *host)
int nr_timedout = 0;

spin_lock_irqsave(ap->lock, flags);

/* This must occur under the ap->lock as we don't want
a polled recovery to race the real interrupt handler
The lost_interrupt handler checks for any completed but
non-notified command and completes much like an IRQ handler.
We then fall into the error recovery code which will treat
this as if normal completion won the race */

if (ap->ops->lost_interrupt)
ap->ops->lost_interrupt(ap);

list_for_each_entry_safe(scmd, tmp, &host->eh_cmd_q, eh_entry) {
struct ata_queued_cmd *qc;

Expand Down Expand Up @@ -628,7 +635,7 @@ void ata_scsi_error(struct Scsi_Host *host)
ap->eh_tries = ATA_EH_MAX_TRIES;
} else
spin_unlock_wait(ap->lock);

/* If we timed raced normal completion and there is nothing to
recover nr_timedout == 0 why exactly are we doing error recovery ? */

Expand Down Expand Up @@ -1755,7 +1762,7 @@ static int speed_down_verdict_cb(struct ata_ering_entry *ent, void *void_arg)
struct speed_down_verdict_arg *arg = void_arg;
int cat;

if (ent->timestamp < arg->since)
if ((ent->eflags & ATA_EFLAG_OLD_ER) || (ent->timestamp < arg->since))
return -1;

cat = ata_eh_categorize_error(ent->eflags, ent->err_mask,
Expand Down
18 changes: 16 additions & 2 deletions drivers/ata/libata-pmp.c
Expand Up @@ -11,6 +11,7 @@
#include <linux/libata.h>
#include <linux/slab.h>
#include "libata.h"
#include "libata-transport.h"

const struct ata_port_operations sata_pmp_port_ops = {
.inherits = &sata_port_ops,
Expand Down Expand Up @@ -312,10 +313,10 @@ static int sata_pmp_configure(struct ata_device *dev, int print_info)
return rc;
}

static int sata_pmp_init_links(struct ata_port *ap, int nr_ports)
static int sata_pmp_init_links (struct ata_port *ap, int nr_ports)
{
struct ata_link *pmp_link = ap->pmp_link;
int i;
int i, err;

if (!pmp_link) {
pmp_link = kzalloc(sizeof(pmp_link[0]) * SATA_PMP_MAX_PORTS,
Expand All @@ -327,6 +328,13 @@ static int sata_pmp_init_links(struct ata_port *ap, int nr_ports)
ata_link_init(ap, &pmp_link[i], i);

ap->pmp_link = pmp_link;

for (i = 0; i < SATA_PMP_MAX_PORTS; i++) {
err = ata_tlink_add(&pmp_link[i]);
if (err) {
goto err_tlink;
}
}
}

for (i = 0; i < nr_ports; i++) {
Expand All @@ -339,6 +347,12 @@ static int sata_pmp_init_links(struct ata_port *ap, int nr_ports)
}

return 0;
err_tlink:
while (--i >= 0)
ata_tlink_delete(&pmp_link[i]);
kfree(pmp_link);
ap->pmp_link = NULL;
return err;
}

static void sata_pmp_quirks(struct ata_port *ap)
Expand Down

0 comments on commit d902747

Please sign in to comment.