Commit d9027470 authored by Gwendal Grignou's avatar Gwendal Grignou Committed by Jeff Garzik

[libata] Add ATA transport class

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: default avatarGwendal Grignou <gwendal@google.com>
Reviewed-by: default avatarGrant Grundler <grundler@google.com>
Signed-off-by: default avatarJeff Garzik <jgarzik@redhat.com>
parent f6f94e2a
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.
...@@ -99,7 +99,7 @@ obj-$(CONFIG_ATA_GENERIC) += ata_generic.o ...@@ -99,7 +99,7 @@ obj-$(CONFIG_ATA_GENERIC) += ata_generic.o
# Should be last libata driver # Should be last libata driver
obj-$(CONFIG_PATA_LEGACY) += pata_legacy.o 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_ATA_SFF) += libata-sff.o
libata-$(CONFIG_SATA_PMP) += libata-pmp.o libata-$(CONFIG_SATA_PMP) += libata-pmp.o
libata-$(CONFIG_ATA_ACPI) += libata-acpi.o libata-$(CONFIG_ATA_ACPI) += libata-acpi.o
...@@ -68,7 +68,7 @@ ...@@ -68,7 +68,7 @@
#include <linux/ratelimit.h> #include <linux/ratelimit.h>
#include "libata.h" #include "libata.h"
#include "libata-transport.h"
/* debounce timing parameters in msecs { interval, duration, timeout } */ /* debounce timing parameters in msecs { interval, duration, timeout } */
const unsigned long sata_deb_timing_normal[] = { 5, 100, 2000 }; const unsigned long sata_deb_timing_normal[] = { 5, 100, 2000 };
...@@ -1017,7 +1017,7 @@ const char *ata_mode_string(unsigned long xfer_mask) ...@@ -1017,7 +1017,7 @@ const char *ata_mode_string(unsigned long xfer_mask)
return "<n/a>"; 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[] = { static const char * const spd_str[] = {
"1.5 Gbps", "1.5 Gbps",
...@@ -5517,7 +5517,8 @@ void ata_link_init(struct ata_port *ap, struct ata_link *link, int pmp) ...@@ -5517,7 +5517,8 @@ void ata_link_init(struct ata_port *ap, struct ata_link *link, int pmp)
int i; int i;
/* clear everything except for devices */ /* 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->ap = ap;
link->pmp = pmp; link->pmp = pmp;
...@@ -6093,9 +6094,18 @@ int ata_host_register(struct ata_host *host, struct scsi_host_template *sht) ...@@ -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++) for (i = 0; i < host->n_ports; i++)
host->ports[i]->print_id = ata_print_id++; 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); rc = ata_scsi_add_hosts(host, sht);
if (rc) if (rc)
return rc; goto err_tadd;
/* associate with ACPI nodes */ /* associate with ACPI nodes */
ata_acpi_associate(host); ata_acpi_associate(host);
...@@ -6136,6 +6146,13 @@ int ata_host_register(struct ata_host *host, struct scsi_host_template *sht) ...@@ -6136,6 +6146,13 @@ int ata_host_register(struct ata_host *host, struct scsi_host_template *sht)
} }
return 0; return 0;
err_tadd:
while (--i >= 0) {
ata_tport_delete(host->ports[i]);
}
return rc;
} }
/** /**
...@@ -6226,6 +6243,13 @@ static void ata_port_detach(struct ata_port *ap) ...@@ -6226,6 +6243,13 @@ static void ata_port_detach(struct ata_port *ap)
cancel_rearming_delayed_work(&ap->hotplug_task); cancel_rearming_delayed_work(&ap->hotplug_task);
skip_eh: 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 */ /* remove the associated SCSI host */
scsi_remove_host(ap->scsi_host); scsi_remove_host(ap->scsi_host);
} }
...@@ -6542,7 +6566,7 @@ static void __init ata_parse_force_param(void) ...@@ -6542,7 +6566,7 @@ static void __init ata_parse_force_param(void)
static int __init ata_init(void) static int __init ata_init(void)
{ {
int rc = -ENOMEM; int rc;
ata_parse_force_param(); ata_parse_force_param();
...@@ -6552,12 +6576,25 @@ static int __init ata_init(void) ...@@ -6552,12 +6576,25 @@ static int __init ata_init(void)
return rc; 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"); printk(KERN_DEBUG "libata version " DRV_VERSION " loaded.\n");
return 0; return 0;
err_out:
return rc;
} }
static void __exit ata_exit(void) static void __exit ata_exit(void)
{ {
ata_release_transport(ata_scsi_transport_template);
libata_transport_exit();
ata_sff_exit(); ata_sff_exit();
kfree(ata_force_tbl); kfree(ata_force_tbl);
} }
......
...@@ -57,6 +57,7 @@ enum { ...@@ -57,6 +57,7 @@ enum {
/* error flags */ /* error flags */
ATA_EFLAG_IS_IO = (1 << 0), ATA_EFLAG_IS_IO = (1 << 0),
ATA_EFLAG_DUBIOUS_XFER = (1 << 1), ATA_EFLAG_DUBIOUS_XFER = (1 << 1),
ATA_EFLAG_OLD_ER = (1 << 31),
/* error categories */ /* error categories */
ATA_ECAT_NONE = 0, ATA_ECAT_NONE = 0,
...@@ -396,12 +397,7 @@ static struct ata_ering_entry *ata_ering_top(struct ata_ering *ering) ...@@ -396,12 +397,7 @@ static struct ata_ering_entry *ata_ering_top(struct ata_ering *ering)
return NULL; return NULL;
} }
static void ata_ering_clear(struct ata_ering *ering) int ata_ering_map(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 *), int (*map_fn)(struct ata_ering_entry *, void *),
void *arg) void *arg)
{ {
...@@ -422,6 +418,17 @@ static int ata_ering_map(struct ata_ering *ering, ...@@ -422,6 +418,17 @@ static int ata_ering_map(struct ata_ering *ering,
return rc; 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) static unsigned int ata_eh_dev_action(struct ata_device *dev)
{ {
struct ata_eh_context *ehc = &dev->link->eh_context; struct ata_eh_context *ehc = &dev->link->eh_context;
...@@ -1755,7 +1762,7 @@ static int speed_down_verdict_cb(struct ata_ering_entry *ent, void *void_arg) ...@@ -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; struct speed_down_verdict_arg *arg = void_arg;
int cat; int cat;
if (ent->timestamp < arg->since) if ((ent->eflags & ATA_EFLAG_OLD_ER) || (ent->timestamp < arg->since))
return -1; return -1;
cat = ata_eh_categorize_error(ent->eflags, ent->err_mask, cat = ata_eh_categorize_error(ent->eflags, ent->err_mask,
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include <linux/libata.h> #include <linux/libata.h>
#include <linux/slab.h> #include <linux/slab.h>
#include "libata.h" #include "libata.h"
#include "libata-transport.h"
const struct ata_port_operations sata_pmp_port_ops = { const struct ata_port_operations sata_pmp_port_ops = {
.inherits = &sata_port_ops, .inherits = &sata_port_ops,
...@@ -312,10 +313,10 @@ static int sata_pmp_configure(struct ata_device *dev, int print_info) ...@@ -312,10 +313,10 @@ static int sata_pmp_configure(struct ata_device *dev, int print_info)
return rc; 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; struct ata_link *pmp_link = ap->pmp_link;
int i; int i, err;
if (!pmp_link) { if (!pmp_link) {
pmp_link = kzalloc(sizeof(pmp_link[0]) * SATA_PMP_MAX_PORTS, pmp_link = kzalloc(sizeof(pmp_link[0]) * SATA_PMP_MAX_PORTS,
...@@ -327,6 +328,13 @@ static int sata_pmp_init_links(struct ata_port *ap, int nr_ports) ...@@ -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); ata_link_init(ap, &pmp_link[i], i);
ap->pmp_link = pmp_link; 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++) { for (i = 0; i < nr_ports; i++) {
...@@ -339,6 +347,12 @@ static int sata_pmp_init_links(struct ata_port *ap, int nr_ports) ...@@ -339,6 +347,12 @@ static int sata_pmp_init_links(struct ata_port *ap, int nr_ports)
} }
return 0; 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) static void sata_pmp_quirks(struct ata_port *ap)
......
...@@ -51,6 +51,7 @@ ...@@ -51,6 +51,7 @@
#include <asm/unaligned.h> #include <asm/unaligned.h>
#include "libata.h" #include "libata.h"
#include "libata-transport.h"
#define SECTOR_SIZE 512 #define SECTOR_SIZE 512
#define ATA_SCSI_RBUF_SIZE 4096 #define ATA_SCSI_RBUF_SIZE 4096
...@@ -64,9 +65,6 @@ static struct ata_device *__ata_scsi_find_dev(struct ata_port *ap, ...@@ -64,9 +65,6 @@ static struct ata_device *__ata_scsi_find_dev(struct ata_port *ap,
const struct scsi_device *scsidev); const struct scsi_device *scsidev);
static struct ata_device *ata_scsi_find_dev(struct ata_port *ap, static struct ata_device *ata_scsi_find_dev(struct ata_port *ap,
const struct scsi_device *scsidev); const struct scsi_device *scsidev);
static int ata_scsi_user_scan(struct Scsi_Host *shost, unsigned int channel,
unsigned int id, unsigned int lun);
#define RW_RECOVERY_MPAGE 0x1 #define RW_RECOVERY_MPAGE 0x1
#define RW_RECOVERY_MPAGE_LEN 12 #define RW_RECOVERY_MPAGE_LEN 12
...@@ -106,17 +104,6 @@ static const u8 def_control_mpage[CONTROL_MPAGE_LEN] = { ...@@ -106,17 +104,6 @@ static const u8 def_control_mpage[CONTROL_MPAGE_LEN] = {
0, 30 /* extended self test time, see 05-359r1 */ 0, 30 /* extended self test time, see 05-359r1 */
}; };
/*
* libata transport template. libata doesn't do real transport stuff.
* It just needs the eh_timed_out hook.
*/
static struct scsi_transport_template ata_scsi_transport_template = {
.eh_strategy_handler = ata_scsi_error,
.eh_timed_out = ata_scsi_timed_out,
.user_scan = ata_scsi_user_scan,
};
static const struct { static const struct {
enum link_pm value; enum link_pm value;
const char *name; const char *name;
...@@ -3334,7 +3321,7 @@ int ata_scsi_add_hosts(struct ata_host *host, struct scsi_host_template *sht) ...@@ -3334,7 +3321,7 @@ int ata_scsi_add_hosts(struct ata_host *host, struct scsi_host_template *sht)
*(struct ata_port **)&shost->hostdata[0] = ap; *(struct ata_port **)&shost->hostdata[0] = ap;
ap->scsi_host = shost; ap->scsi_host = shost;
shost->transportt = &ata_scsi_transport_template; shost->transportt = ata_scsi_transport_template;
shost->unique_id = ap->print_id; shost->unique_id = ap->print_id;
shost->max_id = 16; shost->max_id = 16;
shost->max_lun = 1; shost->max_lun = 1;
...@@ -3616,7 +3603,7 @@ void ata_scsi_hotplug(struct work_struct *work) ...@@ -3616,7 +3603,7 @@ void ata_scsi_hotplug(struct work_struct *work)
* RETURNS: * RETURNS:
* Zero. * Zero.
*/ */
static int ata_scsi_user_scan(struct Scsi_Host *shost, unsigned int channel, int ata_scsi_user_scan(struct Scsi_Host *shost, unsigned int channel,
unsigned int id, unsigned int lun) unsigned int id, unsigned int lun)
{ {
struct ata_port *ap = ata_shost_to_port(shost); struct ata_port *ap = ata_shost_to_port(shost);
......
This diff is collapsed.
#ifndef _LIBATA_TRANSPORT_H
#define _LIBATA_TRANSPORT_H
extern struct scsi_transport_template *ata_scsi_transport_template;
int ata_tlink_add(struct ata_link *link);
void ata_tlink_delete(struct ata_link *link);
int ata_tport_add(struct device *parent, struct ata_port *ap);
void ata_tport_delete(struct ata_port *ap);
struct scsi_transport_template *ata_attach_transport(void);
void ata_release_transport(struct scsi_transport_template *t);
__init int libata_transport_init(void);
void __exit libata_transport_exit(void);
#endif
...@@ -102,6 +102,7 @@ extern int ata_cmd_ioctl(struct scsi_device *scsidev, void __user *arg); ...@@ -102,6 +102,7 @@ extern int ata_cmd_ioctl(struct scsi_device *scsidev, void __user *arg);
extern struct ata_port *ata_port_alloc(struct ata_host *host); extern struct ata_port *ata_port_alloc(struct ata_host *host);
extern void ata_dev_enable_pm(struct ata_device *dev, enum link_pm policy); extern void ata_dev_enable_pm(struct ata_device *dev, enum link_pm policy);
extern void ata_lpm_schedule(struct ata_port *ap, enum link_pm); extern void ata_lpm_schedule(struct ata_port *ap, enum link_pm);
extern const char *sata_spd_string(unsigned int spd);
/* libata-acpi.c */ /* libata-acpi.c */
#ifdef CONFIG_ATA_ACPI #ifdef CONFIG_ATA_ACPI
...@@ -137,6 +138,9 @@ extern void ata_scsi_hotplug(struct work_struct *work); ...@@ -137,6 +138,9 @@ extern void ata_scsi_hotplug(struct work_struct *work);
extern void ata_schedule_scsi_eh(struct Scsi_Host *shost); extern void ata_schedule_scsi_eh(struct Scsi_Host *shost);
extern void ata_scsi_dev_rescan(struct work_struct *work); extern void ata_scsi_dev_rescan(struct work_struct *work);
extern int ata_bus_probe(struct ata_port *ap); extern int ata_bus_probe(struct ata_port *ap);
extern int ata_scsi_user_scan(struct Scsi_Host *shost, unsigned int channel,
unsigned int id, unsigned int lun);
/* libata-eh.c */ /* libata-eh.c */
extern unsigned long ata_internal_cmd_timeout(struct ata_device *dev, u8 cmd); extern unsigned long ata_internal_cmd_timeout(struct ata_device *dev, u8 cmd);
...@@ -164,6 +168,9 @@ extern int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset, ...@@ -164,6 +168,9 @@ extern int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset,
ata_postreset_fn_t postreset, ata_postreset_fn_t postreset,
struct ata_link **r_failed_disk); struct ata_link **r_failed_disk);
extern void ata_eh_finish(struct ata_port *ap); extern void ata_eh_finish(struct ata_port *ap);
extern int ata_ering_map(struct ata_ering *ering,
int (*map_fn)(struct ata_ering_entry *, void *),
void *arg);
/* libata-pmp.c */ /* libata-pmp.c */
#ifdef CONFIG_SATA_PMP #ifdef CONFIG_SATA_PMP
......
...@@ -604,6 +604,7 @@ struct ata_device { ...@@ -604,6 +604,7 @@ struct ata_device {
union acpi_object *gtf_cache; union acpi_object *gtf_cache;
unsigned int gtf_filter; unsigned int gtf_filter;
#endif #endif
struct device tdev;
/* n_sector is CLEAR_BEGIN, read comment above CLEAR_BEGIN */ /* n_sector is CLEAR_BEGIN, read comment above CLEAR_BEGIN */
u64 n_sectors; /* size of device, if ATA */ u64 n_sectors; /* size of device, if ATA */
u64 n_native_sectors; /* native size, if ATA */ u64 n_native_sectors; /* native size, if ATA */
...@@ -690,6 +691,7 @@ struct ata_link { ...@@ -690,6 +691,7 @@ struct ata_link {
struct ata_port *ap; struct ata_port *ap;
int pmp; /* port multiplier port # */ int pmp; /* port multiplier port # */
struct device tdev;
unsigned int active_tag; /* active tag on this link */ unsigned int active_tag; /* active tag on this link */
u32 sactive; /* active NCQ commands */ u32 sactive; /* active NCQ commands */
...@@ -707,6 +709,8 @@ struct ata_link { ...@@ -707,6 +709,8 @@ struct ata_link {
struct ata_device device[ATA_MAX_DEVICES]; struct ata_device device[ATA_MAX_DEVICES];
}; };
#define ATA_LINK_CLEAR_BEGIN offsetof(struct ata_link, active_tag)
#define ATA_LINK_CLEAR_END offsetof(struct ata_link, device[0])
struct ata_port { struct ata_port {
struct Scsi_Host *scsi_host; /* our co-allocated scsi host */ struct Scsi_Host *scsi_host; /* our co-allocated scsi host */
...@@ -752,6 +756,7 @@ struct ata_port { ...@@ -752,6 +756,7 @@ struct ata_port {
struct ata_port_stats stats; struct ata_port_stats stats;
struct ata_host *host; struct ata_host *host;
struct device *dev; struct device *dev;
struct device tdev;
struct mutex scsi_scan_mutex; struct mutex scsi_scan_mutex;
struct delayed_work hotplug_task; struct delayed_work hotplug_task;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment