diff options
Diffstat (limited to 'drivers/ata/libata-core.c')
-rw-r--r-- | drivers/ata/libata-core.c | 250 |
1 files changed, 136 insertions, 114 deletions
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index ddf8e4862787..9ceb49352a84 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -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 }; @@ -98,8 +98,6 @@ static unsigned long ata_dev_blacklisted(const struct ata_device *dev); unsigned int ata_print_id = 1; -struct workqueue_struct *ata_aux_wq; - struct ata_force_param { const char *name; unsigned int cbl; @@ -1019,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", @@ -4167,15 +4165,13 @@ static const struct ata_blacklist_entry ata_device_blacklist [] = { { "WDC AC23200L", "21.10N21", ATA_HORKAGE_NODMA }, { "Compaq CRD-8241B", NULL, ATA_HORKAGE_NODMA }, { "CRD-8400B", NULL, ATA_HORKAGE_NODMA }, - { "CRD-8480B", NULL, ATA_HORKAGE_NODMA }, - { "CRD-8482B", NULL, ATA_HORKAGE_NODMA }, + { "CRD-848[02]B", NULL, ATA_HORKAGE_NODMA }, { "CRD-84", NULL, ATA_HORKAGE_NODMA }, { "SanDisk SDP3B", NULL, ATA_HORKAGE_NODMA }, { "SanDisk SDP3B-64", NULL, ATA_HORKAGE_NODMA }, { "SANYO CD-ROM CRD", NULL, ATA_HORKAGE_NODMA }, { "HITACHI CDR-8", NULL, ATA_HORKAGE_NODMA }, - { "HITACHI CDR-8335", NULL, ATA_HORKAGE_NODMA }, - { "HITACHI CDR-8435", NULL, ATA_HORKAGE_NODMA }, + { "HITACHI CDR-8[34]35",NULL, ATA_HORKAGE_NODMA }, { "Toshiba CD-ROM XM-6202B", NULL, ATA_HORKAGE_NODMA }, { "TOSHIBA CD-ROM XM-1702BC", NULL, ATA_HORKAGE_NODMA }, { "CD-532E-A", NULL, ATA_HORKAGE_NODMA }, @@ -4211,70 +4207,16 @@ static const struct ata_blacklist_entry ata_device_blacklist [] = { { "OCZ CORE_SSD", "02.10104", ATA_HORKAGE_NONCQ }, /* Seagate NCQ + FLUSH CACHE firmware bug */ - { "ST31500341AS", "SD15", ATA_HORKAGE_NONCQ | - ATA_HORKAGE_FIRMWARE_WARN }, - { "ST31500341AS", "SD16", ATA_HORKAGE_NONCQ | - ATA_HORKAGE_FIRMWARE_WARN }, - { "ST31500341AS", "SD17", ATA_HORKAGE_NONCQ | - ATA_HORKAGE_FIRMWARE_WARN }, - { "ST31500341AS", "SD18", ATA_HORKAGE_NONCQ | - ATA_HORKAGE_FIRMWARE_WARN }, - { "ST31500341AS", "SD19", ATA_HORKAGE_NONCQ | - ATA_HORKAGE_FIRMWARE_WARN }, - - { "ST31000333AS", "SD15", ATA_HORKAGE_NONCQ | - ATA_HORKAGE_FIRMWARE_WARN }, - { "ST31000333AS", "SD16", ATA_HORKAGE_NONCQ | - ATA_HORKAGE_FIRMWARE_WARN }, - { "ST31000333AS", "SD17", ATA_HORKAGE_NONCQ | - ATA_HORKAGE_FIRMWARE_WARN }, - { "ST31000333AS", "SD18", ATA_HORKAGE_NONCQ | - ATA_HORKAGE_FIRMWARE_WARN }, - { "ST31000333AS", "SD19", ATA_HORKAGE_NONCQ | - ATA_HORKAGE_FIRMWARE_WARN }, - - { "ST3640623AS", "SD15", ATA_HORKAGE_NONCQ | - ATA_HORKAGE_FIRMWARE_WARN }, - { "ST3640623AS", "SD16", ATA_HORKAGE_NONCQ | - ATA_HORKAGE_FIRMWARE_WARN }, - { "ST3640623AS", "SD17", ATA_HORKAGE_NONCQ | - ATA_HORKAGE_FIRMWARE_WARN }, - { "ST3640623AS", "SD18", ATA_HORKAGE_NONCQ | - ATA_HORKAGE_FIRMWARE_WARN }, - { "ST3640623AS", "SD19", ATA_HORKAGE_NONCQ | + { "ST31500341AS", "SD1[5-9]", ATA_HORKAGE_NONCQ | ATA_HORKAGE_FIRMWARE_WARN }, - { "ST3640323AS", "SD15", ATA_HORKAGE_NONCQ | - ATA_HORKAGE_FIRMWARE_WARN }, - { "ST3640323AS", "SD16", ATA_HORKAGE_NONCQ | - ATA_HORKAGE_FIRMWARE_WARN }, - { "ST3640323AS", "SD17", ATA_HORKAGE_NONCQ | - ATA_HORKAGE_FIRMWARE_WARN }, - { "ST3640323AS", "SD18", ATA_HORKAGE_NONCQ | - ATA_HORKAGE_FIRMWARE_WARN }, - { "ST3640323AS", "SD19", ATA_HORKAGE_NONCQ | + { "ST31000333AS", "SD1[5-9]", ATA_HORKAGE_NONCQ | ATA_HORKAGE_FIRMWARE_WARN }, - { "ST3320813AS", "SD15", ATA_HORKAGE_NONCQ | - ATA_HORKAGE_FIRMWARE_WARN }, - { "ST3320813AS", "SD16", ATA_HORKAGE_NONCQ | - ATA_HORKAGE_FIRMWARE_WARN }, - { "ST3320813AS", "SD17", ATA_HORKAGE_NONCQ | - ATA_HORKAGE_FIRMWARE_WARN }, - { "ST3320813AS", "SD18", ATA_HORKAGE_NONCQ | - ATA_HORKAGE_FIRMWARE_WARN }, - { "ST3320813AS", "SD19", ATA_HORKAGE_NONCQ | + { "ST3640[36]23AS", "SD1[5-9]", ATA_HORKAGE_NONCQ | ATA_HORKAGE_FIRMWARE_WARN }, - { "ST3320613AS", "SD15", ATA_HORKAGE_NONCQ | - ATA_HORKAGE_FIRMWARE_WARN }, - { "ST3320613AS", "SD16", ATA_HORKAGE_NONCQ | - ATA_HORKAGE_FIRMWARE_WARN }, - { "ST3320613AS", "SD17", ATA_HORKAGE_NONCQ | - ATA_HORKAGE_FIRMWARE_WARN }, - { "ST3320613AS", "SD18", ATA_HORKAGE_NONCQ | - ATA_HORKAGE_FIRMWARE_WARN }, - { "ST3320613AS", "SD19", ATA_HORKAGE_NONCQ | + { "ST3320[68]13AS", "SD1[5-9]", ATA_HORKAGE_NONCQ | ATA_HORKAGE_FIRMWARE_WARN }, /* Blacklist entries taken from Silicon Image 3124/3132 @@ -4303,12 +4245,7 @@ static const struct ata_blacklist_entry ata_device_blacklist [] = { /* Devices which get the IVB wrong */ { "QUANTUM FIREBALLlct10 05", "A03.0900", ATA_HORKAGE_IVB, }, /* Maybe we should just blacklist TSSTcorp... */ - { "TSSTcorp CDDVDW SH-S202H", "SB00", ATA_HORKAGE_IVB, }, - { "TSSTcorp CDDVDW SH-S202H", "SB01", ATA_HORKAGE_IVB, }, - { "TSSTcorp CDDVDW SH-S202J", "SB00", ATA_HORKAGE_IVB, }, - { "TSSTcorp CDDVDW SH-S202J", "SB01", ATA_HORKAGE_IVB, }, - { "TSSTcorp CDDVDW SH-S202N", "SB00", ATA_HORKAGE_IVB, }, - { "TSSTcorp CDDVDW SH-S202N", "SB01", ATA_HORKAGE_IVB, }, + { "TSSTcorp CDDVDW SH-S202[HJN]", "SB0[01]", ATA_HORKAGE_IVB, }, /* Devices that do not need bridging limits applied */ { "MTRON MSP-SATA*", NULL, ATA_HORKAGE_BRIDGE_OK, }, @@ -4326,29 +4263,73 @@ static const struct ata_blacklist_entry ata_device_blacklist [] = { { } }; -static int strn_pattern_cmp(const char *patt, const char *name, int wildchar) +/** + * glob_match - match a text string against a glob-style pattern + * @text: the string to be examined + * @pattern: the glob-style pattern to be matched against + * + * Either/both of text and pattern can be empty strings. + * + * Match text against a glob-style pattern, with wildcards and simple sets: + * + * ? matches any single character. + * * matches any run of characters. + * [xyz] matches a single character from the set: x, y, or z. + * [a-d] matches a single character from the range: a, b, c, or d. + * [a-d0-9] matches a single character from either range. + * + * The special characters ?, [, -, or *, can be matched using a set, eg. [*] + * Behaviour with malformed patterns is undefined, though generally reasonable. + * + * Sample patterns: "SD1?", "SD1[0-5]", "*R0", "SD*1?[012]*xx" + * + * This function uses one level of recursion per '*' in pattern. + * Since it calls _nothing_ else, and has _no_ explicit local variables, + * this will not cause stack problems for any reasonable use here. + * + * RETURNS: + * 0 on match, 1 otherwise. + */ +static int glob_match (const char *text, const char *pattern) { - const char *p; - int len; - - /* - * check for trailing wildcard: *\0 - */ - p = strchr(patt, wildchar); - if (p && ((*(p + 1)) == 0)) - len = p - patt; - else { - len = strlen(name); - if (!len) { - if (!*patt) - return 0; - return -1; + do { + /* Match single character or a '?' wildcard */ + if (*text == *pattern || *pattern == '?') { + if (!*pattern++) + return 0; /* End of both strings: match */ + } else { + /* Match single char against a '[' bracketed ']' pattern set */ + if (!*text || *pattern != '[') + break; /* Not a pattern set */ + while (*++pattern && *pattern != ']' && *text != *pattern) { + if (*pattern == '-' && *(pattern - 1) != '[') + if (*text > *(pattern - 1) && *text < *(pattern + 1)) { + ++pattern; + break; + } + } + if (!*pattern || *pattern == ']') + return 1; /* No match */ + while (*pattern && *pattern++ != ']'); + } + } while (*++text && *pattern); + + /* Match any run of chars against a '*' wildcard */ + if (*pattern == '*') { + if (!*++pattern) + return 0; /* Match: avoid recursion at end of pattern */ + /* Loop to handle additional pattern chars after the wildcard */ + while (*text) { + if (glob_match(text, pattern) == 0) + return 0; /* Remainder matched */ + ++text; /* Absorb (match) this char and try again */ } } - - return strncmp(patt, name, len); + if (!*text && !*pattern) + return 0; /* End of both strings: match */ + return 1; /* No match */ } - + static unsigned long ata_dev_blacklisted(const struct ata_device *dev) { unsigned char model_num[ATA_ID_PROD_LEN + 1]; @@ -4359,10 +4340,10 @@ static unsigned long ata_dev_blacklisted(const struct ata_device *dev) ata_id_c_string(dev->id, model_rev, ATA_ID_FW_REV, sizeof(model_rev)); while (ad->model_num) { - if (!strn_pattern_cmp(ad->model_num, model_num, '*')) { + if (!glob_match(model_num, ad->model_num)) { if (ad->model_rev == NULL) return ad->horkage; - if (!strn_pattern_cmp(ad->model_rev, model_rev, '*')) + if (!glob_match(model_rev, ad->model_rev)) return ad->horkage; } ad++; @@ -4962,8 +4943,13 @@ static void ata_verify_xfer(struct ata_queued_cmd *qc) * ata_qc_complete - Complete an active ATA command * @qc: Command to complete * - * Indicate to the mid and upper layers that an ATA - * command has completed, with either an ok or not-ok status. + * Indicate to the mid and upper layers that an ATA command has + * completed, with either an ok or not-ok status. + * + * Refrain from calling this function multiple times when + * successfully completing multiple NCQ commands. + * ata_qc_complete_multiple() should be used instead, which will + * properly update IRQ expect state. * * LOCKING: * spin_lock_irqsave(host lock) @@ -5056,6 +5042,10 @@ void ata_qc_complete(struct ata_queued_cmd *qc) * requests normally. ap->qc_active and @qc_active is compared * and commands are completed accordingly. * + * Always use this function when completing multiple NCQ commands + * from IRQ handlers instead of calling ata_qc_complete() + * multiple times to keep IRQ expect status properly in sync. + * * LOCKING: * spin_lock_irqsave(host lock) * @@ -5130,15 +5120,18 @@ void ata_qc_issue(struct ata_queued_cmd *qc) qc->flags |= ATA_QCFLAG_ACTIVE; ap->qc_active |= 1 << qc->tag; - /* We guarantee to LLDs that they will have at least one + /* + * We guarantee to LLDs that they will have at least one * non-zero sg if the command is a data command. */ - BUG_ON(ata_is_data(prot) && (!qc->sg || !qc->n_elem || !qc->nbytes)); + if (WARN_ON_ONCE(ata_is_data(prot) && + (!qc->sg || !qc->n_elem || !qc->nbytes))) + goto sys_err; if (ata_is_dma(prot) || (ata_is_pio(prot) && (ap->flags & ATA_FLAG_PIO_DMA))) if (ata_sg_setup(qc)) - goto sg_err; + goto sys_err; /* if device is sleeping, schedule reset and abort the link */ if (unlikely(qc->dev->flags & ATA_DFLAG_SLEEPING)) { @@ -5155,7 +5148,7 @@ void ata_qc_issue(struct ata_queued_cmd *qc) goto err; return; -sg_err: +sys_err: qc->err_mask |= AC_ERR_SYSTEM; err: ata_qc_complete(qc); @@ -5521,7 +5514,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; @@ -5595,7 +5589,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; @@ -5611,6 +5605,7 @@ struct ata_port *ata_port_alloc(struct ata_host *host) ap->msg_enable = ATA_MSG_DRV | ATA_MSG_ERR | ATA_MSG_WARN; #endif + mutex_init(&ap->scsi_scan_mutex); INIT_DELAYED_WORK(&ap->hotplug_task, ata_scsi_hotplug); INIT_WORK(&ap->scsi_rescan_task, ata_scsi_dev_rescan); INIT_LIST_HEAD(&ap->eh_done_q); @@ -6096,9 +6091,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); @@ -6139,6 +6143,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; + } /** @@ -6229,6 +6240,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); } @@ -6545,33 +6563,37 @@ static void __init ata_parse_force_param(void) static int __init ata_init(void) { - int rc = -ENOMEM; + int rc; ata_parse_force_param(); - ata_aux_wq = create_singlethread_workqueue("ata_aux"); - if (!ata_aux_wq) - goto fail; - rc = ata_sff_init(); - if (rc) - goto fail; + if (rc) { + kfree(ata_force_tbl); + 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; -fail: - kfree(ata_force_tbl); - if (ata_aux_wq) - destroy_workqueue(ata_aux_wq); +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); - destroy_workqueue(ata_aux_wq); } subsys_initcall(ata_init); |