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 + -
显示快捷键?