mirror of
https://github.com/armbian/linux-cix.git
synced 2026-01-06 12:30:45 -08:00
[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: Gwendal Grignou <gwendal@google.com> Reviewed-by: Grant Grundler <grundler@google.com> Signed-off-by: Jeff Garzik <jgarzik@redhat.com>
This commit is contained in:
committed by
Jeff Garzik
parent
f6f94e2ab1
commit
d9027470b8
99
Documentation/ABI/testing/sysfs-ata
Normal file
99
Documentation/ABI/testing/sysfs-ata
Normal file
@@ -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.
|
||||
@@ -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
|
||||
|
||||
@@ -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 };
|
||||
@@ -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",
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
|
||||
@@ -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 ? */
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
@@ -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,
|
||||
@@ -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++) {
|
||||
@@ -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)
|
||||
|
||||
@@ -51,6 +51,7 @@
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
#include "libata.h"
|
||||
#include "libata-transport.h"
|
||||
|
||||
#define SECTOR_SIZE 512
|
||||
#define ATA_SCSI_RBUF_SIZE 4096
|
||||
@@ -64,9 +65,6 @@ static struct ata_device *__ata_scsi_find_dev(struct ata_port *ap,
|
||||
const struct scsi_device *scsidev);
|
||||
static struct ata_device *ata_scsi_find_dev(struct ata_port *ap,
|
||||
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_LEN 12
|
||||
@@ -106,17 +104,6 @@ static const u8 def_control_mpage[CONTROL_MPAGE_LEN] = {
|
||||
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 {
|
||||
enum link_pm value;
|
||||
const char *name;
|
||||
@@ -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;
|
||||
ap->scsi_host = shost;
|
||||
|
||||
shost->transportt = &ata_scsi_transport_template;
|
||||
shost->transportt = ata_scsi_transport_template;
|
||||
shost->unique_id = ap->print_id;
|
||||
shost->max_id = 16;
|
||||
shost->max_lun = 1;
|
||||
@@ -3616,8 +3603,8 @@ void ata_scsi_hotplug(struct work_struct *work)
|
||||
* RETURNS:
|
||||
* Zero.
|
||||
*/
|
||||
static int ata_scsi_user_scan(struct Scsi_Host *shost, unsigned int channel,
|
||||
unsigned int id, unsigned int lun)
|
||||
int ata_scsi_user_scan(struct Scsi_Host *shost, unsigned int channel,
|
||||
unsigned int id, unsigned int lun)
|
||||
{
|
||||
struct ata_port *ap = ata_shost_to_port(shost);
|
||||
unsigned long flags;
|
||||
|
||||
773
drivers/ata/libata-transport.c
Normal file
773
drivers/ata/libata-transport.c
Normal file
File diff suppressed because it is too large
Load Diff
18
drivers/ata/libata-transport.h
Normal file
18
drivers/ata/libata-transport.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#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);
|
||||
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_lpm_schedule(struct ata_port *ap, enum link_pm);
|
||||
extern const char *sata_spd_string(unsigned int spd);
|
||||
|
||||
/* libata-acpi.c */
|
||||
#ifdef CONFIG_ATA_ACPI
|
||||
@@ -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_scsi_dev_rescan(struct work_struct *work);
|
||||
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 */
|
||||
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,
|
||||
ata_postreset_fn_t postreset,
|
||||
struct ata_link **r_failed_disk);
|
||||
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 */
|
||||
#ifdef CONFIG_SATA_PMP
|
||||
|
||||
@@ -604,6 +604,7 @@ struct ata_device {
|
||||
union acpi_object *gtf_cache;
|
||||
unsigned int gtf_filter;
|
||||
#endif
|
||||
struct device tdev;
|
||||
/* n_sector is CLEAR_BEGIN, read comment above CLEAR_BEGIN */
|
||||
u64 n_sectors; /* size of device, if ATA */
|
||||
u64 n_native_sectors; /* native size, if ATA */
|
||||
@@ -690,6 +691,7 @@ struct ata_link {
|
||||
struct ata_port *ap;
|
||||
int pmp; /* port multiplier port # */
|
||||
|
||||
struct device tdev;
|
||||
unsigned int active_tag; /* active tag on this link */
|
||||
u32 sactive; /* active NCQ commands */
|
||||
|
||||
@@ -707,6 +709,8 @@ struct ata_link {
|
||||
|
||||
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 Scsi_Host *scsi_host; /* our co-allocated scsi host */
|
||||
@@ -752,6 +756,7 @@ struct ata_port {
|
||||
struct ata_port_stats stats;
|
||||
struct ata_host *host;
|
||||
struct device *dev;
|
||||
struct device tdev;
|
||||
|
||||
struct mutex scsi_scan_mutex;
|
||||
struct delayed_work hotplug_task;
|
||||
|
||||
Reference in New Issue
Block a user