scsi_transport_sas.c
来自「linux 内核源代码」· C语言 代码 · 共 1,792 行 · 第 1/4 页
C
1,792 行
/* * Copyright (C) 2005-2006 Dell Inc. * Released under GPL v2. * * Serial Attached SCSI (SAS) transport class. * * The SAS transport class contains common code to deal with SAS HBAs, * an aproximated representation of SAS topologies in the driver model, * and various sysfs attributes to expose these topologies and managment * interfaces to userspace. * * In addition to the basic SCSI core objects this transport class * introduces two additional intermediate objects: The SAS PHY * as represented by struct sas_phy defines an "outgoing" PHY on * a SAS HBA or Expander, and the SAS remote PHY represented by * struct sas_rphy defines an "incoming" PHY on a SAS Expander or * end device. Note that this is purely a software concept, the * underlying hardware for a PHY and a remote PHY is the exactly * the same. * * There is no concept of a SAS port in this code, users can see * what PHYs form a wide port based on the port_identifier attribute, * which is the same for all PHYs in a port. */#include <linux/init.h>#include <linux/module.h>#include <linux/jiffies.h>#include <linux/err.h>#include <linux/slab.h>#include <linux/string.h>#include <linux/blkdev.h>#include <linux/bsg.h>#include <scsi/scsi.h>#include <scsi/scsi_device.h>#include <scsi/scsi_host.h>#include <scsi/scsi_transport.h>#include <scsi/scsi_transport_sas.h>#include "scsi_sas_internal.h"struct sas_host_attrs { struct list_head rphy_list; struct mutex lock; struct request_queue *q; u32 next_target_id; u32 next_expander_id; int next_port_id;};#define to_sas_host_attrs(host) ((struct sas_host_attrs *)(host)->shost_data)/* * Hack to allow attributes of the same name in different objects. */#define SAS_CLASS_DEVICE_ATTR(_prefix,_name,_mode,_show,_store) \ struct class_device_attribute class_device_attr_##_prefix##_##_name = \ __ATTR(_name,_mode,_show,_store)/* * Pretty printing helpers */#define sas_bitfield_name_match(title, table) \static ssize_t \get_sas_##title##_names(u32 table_key, char *buf) \{ \ char *prefix = ""; \ ssize_t len = 0; \ int i; \ \ for (i = 0; i < ARRAY_SIZE(table); i++) { \ if (table[i].value & table_key) { \ len += sprintf(buf + len, "%s%s", \ prefix, table[i].name); \ prefix = ", "; \ } \ } \ len += sprintf(buf + len, "\n"); \ return len; \}#define sas_bitfield_name_set(title, table) \static ssize_t \set_sas_##title##_names(u32 *table_key, const char *buf) \{ \ ssize_t len = 0; \ int i; \ \ for (i = 0; i < ARRAY_SIZE(table); i++) { \ len = strlen(table[i].name); \ if (strncmp(buf, table[i].name, len) == 0 && \ (buf[len] == '\n' || buf[len] == '\0')) { \ *table_key = table[i].value; \ return 0; \ } \ } \ return -EINVAL; \}#define sas_bitfield_name_search(title, table) \static ssize_t \get_sas_##title##_names(u32 table_key, char *buf) \{ \ ssize_t len = 0; \ int i; \ \ for (i = 0; i < ARRAY_SIZE(table); i++) { \ if (table[i].value == table_key) { \ len += sprintf(buf + len, "%s", \ table[i].name); \ break; \ } \ } \ len += sprintf(buf + len, "\n"); \ return len; \}static struct { u32 value; char *name;} sas_device_type_names[] = { { SAS_PHY_UNUSED, "unused" }, { SAS_END_DEVICE, "end device" }, { SAS_EDGE_EXPANDER_DEVICE, "edge expander" }, { SAS_FANOUT_EXPANDER_DEVICE, "fanout expander" },};sas_bitfield_name_search(device_type, sas_device_type_names)static struct { u32 value; char *name;} sas_protocol_names[] = { { SAS_PROTOCOL_SATA, "sata" }, { SAS_PROTOCOL_SMP, "smp" }, { SAS_PROTOCOL_STP, "stp" }, { SAS_PROTOCOL_SSP, "ssp" },};sas_bitfield_name_match(protocol, sas_protocol_names)static struct { u32 value; char *name;} sas_linkspeed_names[] = { { SAS_LINK_RATE_UNKNOWN, "Unknown" }, { SAS_PHY_DISABLED, "Phy disabled" }, { SAS_LINK_RATE_FAILED, "Link Rate failed" }, { SAS_SATA_SPINUP_HOLD, "Spin-up hold" }, { SAS_LINK_RATE_1_5_GBPS, "1.5 Gbit" }, { SAS_LINK_RATE_3_0_GBPS, "3.0 Gbit" }, { SAS_LINK_RATE_6_0_GBPS, "6.0 Gbit" },};sas_bitfield_name_search(linkspeed, sas_linkspeed_names)sas_bitfield_name_set(linkspeed, sas_linkspeed_names)static void sas_smp_request(struct request_queue *q, struct Scsi_Host *shost, struct sas_rphy *rphy){ struct request *req; int ret; int (*handler)(struct Scsi_Host *, struct sas_rphy *, struct request *); while (!blk_queue_plugged(q)) { req = elv_next_request(q); if (!req) break; blkdev_dequeue_request(req); spin_unlock_irq(q->queue_lock); handler = to_sas_internal(shost->transportt)->f->smp_handler; ret = handler(shost, rphy, req); spin_lock_irq(q->queue_lock); req->end_io(req, ret); }}static void sas_host_smp_request(struct request_queue *q){ sas_smp_request(q, (struct Scsi_Host *)q->queuedata, NULL);}static void sas_non_host_smp_request(struct request_queue *q){ struct sas_rphy *rphy = q->queuedata; sas_smp_request(q, rphy_to_shost(rphy), rphy);}static int sas_bsg_initialize(struct Scsi_Host *shost, struct sas_rphy *rphy){ struct request_queue *q; int error; struct device *dev; char namebuf[BUS_ID_SIZE]; const char *name; if (!to_sas_internal(shost->transportt)->f->smp_handler) { printk("%s can't handle SMP requests\n", shost->hostt->name); return 0; } if (rphy) { q = blk_init_queue(sas_non_host_smp_request, NULL); dev = &rphy->dev; name = dev->bus_id; } else { q = blk_init_queue(sas_host_smp_request, NULL); dev = &shost->shost_gendev; snprintf(namebuf, sizeof(namebuf), "sas_host%d", shost->host_no); name = namebuf; } if (!q) return -ENOMEM; error = bsg_register_queue(q, dev, name); if (error) { blk_cleanup_queue(q); return -ENOMEM; } if (rphy) rphy->q = q; else to_sas_host_attrs(shost)->q = q; if (rphy) q->queuedata = rphy; else q->queuedata = shost; set_bit(QUEUE_FLAG_BIDI, &q->queue_flags); return 0;}static void sas_bsg_remove(struct Scsi_Host *shost, struct sas_rphy *rphy){ struct request_queue *q; if (rphy) q = rphy->q; else q = to_sas_host_attrs(shost)->q; if (!q) return; bsg_unregister_queue(q); blk_cleanup_queue(q);}/* * SAS host attributes */static int sas_host_setup(struct transport_container *tc, struct device *dev, struct class_device *cdev){ struct Scsi_Host *shost = dev_to_shost(dev); struct sas_host_attrs *sas_host = to_sas_host_attrs(shost); INIT_LIST_HEAD(&sas_host->rphy_list); mutex_init(&sas_host->lock); sas_host->next_target_id = 0; sas_host->next_expander_id = 0; sas_host->next_port_id = 0; if (sas_bsg_initialize(shost, NULL)) dev_printk(KERN_ERR, dev, "fail to a bsg device %d\n", shost->host_no); return 0;}static int sas_host_remove(struct transport_container *tc, struct device *dev, struct class_device *cdev){ struct Scsi_Host *shost = dev_to_shost(dev); sas_bsg_remove(shost, NULL); return 0;}static DECLARE_TRANSPORT_CLASS(sas_host_class, "sas_host", sas_host_setup, sas_host_remove, NULL);static int sas_host_match(struct attribute_container *cont, struct device *dev){ struct Scsi_Host *shost; struct sas_internal *i; if (!scsi_is_host_device(dev)) return 0; shost = dev_to_shost(dev); if (!shost->transportt) return 0; if (shost->transportt->host_attrs.ac.class != &sas_host_class.class) return 0; i = to_sas_internal(shost->transportt); return &i->t.host_attrs.ac == cont;}static int do_sas_phy_delete(struct device *dev, void *data){ int pass = (int)(unsigned long)data; if (pass == 0 && scsi_is_sas_port(dev)) sas_port_delete(dev_to_sas_port(dev)); else if (pass == 1 && scsi_is_sas_phy(dev)) sas_phy_delete(dev_to_phy(dev)); return 0;}/** * sas_remove_children -- tear down a devices SAS data structures * @dev: device belonging to the sas object * * Removes all SAS PHYs and remote PHYs for a given object */void sas_remove_children(struct device *dev){ device_for_each_child(dev, (void *)0, do_sas_phy_delete); device_for_each_child(dev, (void *)1, do_sas_phy_delete);}EXPORT_SYMBOL(sas_remove_children);/** * sas_remove_host -- tear down a Scsi_Host's SAS data structures * @shost: Scsi Host that is torn down * * Removes all SAS PHYs and remote PHYs for a given Scsi_Host. * Must be called just before scsi_remove_host for SAS HBAs. */void sas_remove_host(struct Scsi_Host *shost){ sas_remove_children(&shost->shost_gendev);}EXPORT_SYMBOL(sas_remove_host);/* * SAS Phy attributes */#define sas_phy_show_simple(field, name, format_string, cast) \static ssize_t \show_sas_phy_##name(struct class_device *cdev, char *buf) \{ \ struct sas_phy *phy = transport_class_to_phy(cdev); \ \ return snprintf(buf, 20, format_string, cast phy->field); \}#define sas_phy_simple_attr(field, name, format_string, type) \ sas_phy_show_simple(field, name, format_string, (type)) \static CLASS_DEVICE_ATTR(name, S_IRUGO, show_sas_phy_##name, NULL)#define sas_phy_show_protocol(field, name) \static ssize_t \show_sas_phy_##name(struct class_device *cdev, char *buf) \{ \ struct sas_phy *phy = transport_class_to_phy(cdev); \ \ if (!phy->field) \ return snprintf(buf, 20, "none\n"); \ return get_sas_protocol_names(phy->field, buf); \}#define sas_phy_protocol_attr(field, name) \ sas_phy_show_protocol(field, name) \static CLASS_DEVICE_ATTR(name, S_IRUGO, show_sas_phy_##name, NULL)#define sas_phy_show_linkspeed(field) \static ssize_t \show_sas_phy_##field(struct class_device *cdev, char *buf) \{ \ struct sas_phy *phy = transport_class_to_phy(cdev); \ \ return get_sas_linkspeed_names(phy->field, buf); \}/* Fudge to tell if we're minimum or maximum */#define sas_phy_store_linkspeed(field) \static ssize_t \store_sas_phy_##field(struct class_device *cdev, const char *buf, \ size_t count) \{ \ struct sas_phy *phy = transport_class_to_phy(cdev); \ struct Scsi_Host *shost = dev_to_shost(phy->dev.parent); \ struct sas_internal *i = to_sas_internal(shost->transportt); \ u32 value; \ struct sas_phy_linkrates rates = {0}; \ int error; \ \ error = set_sas_linkspeed_names(&value, buf); \ if (error) \ return error; \ rates.field = value; \ error = i->f->set_phy_speed(phy, &rates); \ \ return error ? error : count; \}#define sas_phy_linkspeed_rw_attr(field) \ sas_phy_show_linkspeed(field) \ sas_phy_store_linkspeed(field) \static CLASS_DEVICE_ATTR(field, S_IRUGO, show_sas_phy_##field, \ store_sas_phy_##field)#define sas_phy_linkspeed_attr(field) \ sas_phy_show_linkspeed(field) \static CLASS_DEVICE_ATTR(field, S_IRUGO, show_sas_phy_##field, NULL)#define sas_phy_show_linkerror(field) \static ssize_t \show_sas_phy_##field(struct class_device *cdev, char *buf) \{ \ struct sas_phy *phy = transport_class_to_phy(cdev); \ struct Scsi_Host *shost = dev_to_shost(phy->dev.parent); \ struct sas_internal *i = to_sas_internal(shost->transportt); \ int error; \ \ error = i->f->get_linkerrors ? i->f->get_linkerrors(phy) : 0; \ if (error) \ return error; \ return snprintf(buf, 20, "%u\n", phy->field); \}#define sas_phy_linkerror_attr(field) \ sas_phy_show_linkerror(field) \static CLASS_DEVICE_ATTR(field, S_IRUGO, show_sas_phy_##field, NULL)static ssize_tshow_sas_device_type(struct class_device *cdev, char *buf){
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?