// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2005-2006 Dell Inc. * * 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 management * 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.
*/
/* * Hack to allow attributes of the same name in different objects.
*/ #define SAS_DEVICE_ATTR(_prefix,_name,_mode,_show,_store) \ struct device_attribute dev_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, constchar *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; \
}
/** * 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 and remove the * Scsi_Host as well. * * Note: Do not call scsi_remove_host() on the Scsi_Host any more, as it is * already removed.
*/ void sas_remove_host(struct Scsi_Host *shost)
{
sas_remove_children(&shost->shost_gendev);
scsi_remove_host(shost);
}
EXPORT_SYMBOL(sas_remove_host);
/** * sas_get_address - return the SAS address of the device * @sdev: scsi device * * Returns the SAS address of the scsi device
*/
u64 sas_get_address(struct scsi_device *sdev)
{ struct sas_end_device *rdev = sas_sdev_to_rdev(sdev);
/** * sas_tlr_supported - checking TLR bit in vpd 0x90 * @sdev: scsi device struct * * Check Transport Layer Retries are supported or not. * If vpd page 0x90 is present, TRL is supported. *
*/ unsignedint
sas_tlr_supported(struct scsi_device *sdev)
{ constint vpd_len = 32; struct sas_end_device *rdev = sas_sdev_to_rdev(sdev); char *buffer = kzalloc(vpd_len, GFP_KERNEL); int ret = 0;
if (!buffer) goto out;
if (scsi_get_vpd_page(sdev, 0x90, buffer, vpd_len)) goto out;
/* * Magic numbers: the VPD Protocol page (0x90) * has a 4 byte header and then one entry per device port * the TLR bit is at offset 8 on each port entry * if we take the first port, that's at total offset 12
*/
ret = buffer[12] & 0x01;
/** * sas_ata_ncq_prio_supported - Check for ATA NCQ command priority support * @sdev: SCSI device * * Check if an ATA device supports NCQ priority using VPD page 89h (ATA * Information). Since this VPD page is implemented only for ATA devices, * this function always returns false for SCSI devices.
*/ bool sas_ata_ncq_prio_supported(struct scsi_device *sdev)
{ struct scsi_vpd *vpd; bool ncq_prio_supported = false;
if (i->f->phy_release)
i->f->phy_release(phy);
put_device(dev->parent);
kfree(phy);
}
/** * sas_phy_alloc - allocates and initialize a SAS PHY structure * @parent: Parent device * @number: Phy index * * Allocates an SAS PHY structure. It will be added in the device tree * below the device specified by @parent, which has to be either a Scsi_Host * or sas_rphy. * * Returns: * SAS PHY allocated or %NULL if the allocation failed.
*/ struct sas_phy *sas_phy_alloc(struct device *parent, int number)
{ struct Scsi_Host *shost = dev_to_shost(parent); struct sas_phy *phy;
phy = kzalloc(sizeof(*phy), GFP_KERNEL); if (!phy) return NULL;
/** * sas_phy_add - add a SAS PHY to the device hierarchy * @phy: The PHY to be added * * Publishes a SAS PHY to the rest of the system.
*/ int sas_phy_add(struct sas_phy *phy)
{ int error;
error = device_add(&phy->dev); if (error) return error;
/** * sas_phy_free - free a SAS PHY * @phy: SAS PHY to free * * Frees the specified SAS PHY. * * Note: * This function must only be called on a PHY that has not * successfully been added using sas_phy_add().
*/ void sas_phy_free(struct sas_phy *phy)
{
transport_destroy_device(&phy->dev);
put_device(&phy->dev);
}
EXPORT_SYMBOL(sas_phy_free);
/** * sas_phy_delete - remove SAS PHY * @phy: SAS PHY to remove * * Removes the specified SAS PHY. If the SAS PHY has an * associated remote PHY it is removed before.
*/ void
sas_phy_delete(struct sas_phy *phy)
{ struct device *dev = &phy->dev;
/* this happens if the phy is still part of a port when deleted */
BUG_ON(!list_empty(&phy->port_siblings));
/** * scsi_is_sas_phy - check if a struct device represents a SAS PHY * @dev: device to check * * Returns: * %1 if the device represents a SAS PHY, %0 else
*/ int scsi_is_sas_phy(conststruct device *dev)
{ return dev->release == sas_phy_release;
}
EXPORT_SYMBOL(scsi_is_sas_phy);
/** * sas_port_alloc - allocate and initialize a SAS port structure * * @parent: parent device * @port_id: port number * * Allocates a SAS port structure. It will be added to the device tree * below the device specified by @parent which must be either a Scsi_Host * or a sas_expander_device. * * Returns: %NULL on error
*/ struct sas_port *sas_port_alloc(struct device *parent, int port_id)
{ struct Scsi_Host *shost = dev_to_shost(parent); struct sas_port *port;
port = kzalloc(sizeof(*port), GFP_KERNEL); if (!port) return NULL;
/** * sas_port_alloc_num - allocate and initialize a SAS port structure * * @parent: parent device * * Allocates a SAS port structure and a number to go with it. This * interface is really for adapters where the port number has no * meansing, so the sas class should manage them. It will be added to * the device tree below the device specified by @parent which must be * either a Scsi_Host or a sas_expander_device. * * Returns: %NULL on error
*/ struct sas_port *sas_port_alloc_num(struct device *parent)
{ int index; struct Scsi_Host *shost = dev_to_shost(parent); struct sas_host_attrs *sas_host = to_sas_host_attrs(shost);
/* FIXME: use idr for this eventually */
mutex_lock(&sas_host->lock); if (scsi_is_sas_expander_device(parent)) { struct sas_rphy *rphy = dev_to_rphy(parent); struct sas_expander_device *exp = rphy_to_expander_device(rphy);
index = exp->next_port_id++;
} else
index = sas_host->next_port_id++;
mutex_unlock(&sas_host->lock); return sas_port_alloc(parent, index);
}
EXPORT_SYMBOL(sas_port_alloc_num);
/** * sas_port_add - add a SAS port to the device hierarchy * @port: port to be added * * publishes a port to the rest of the system
*/ int sas_port_add(struct sas_port *port)
{ int error;
/* No phys should be added until this is made visible */
BUG_ON(!list_empty(&port->phy_list));
/** * sas_port_free - free a SAS PORT * @port: SAS PORT to free * * Frees the specified SAS PORT. * * Note: * This function must only be called on a PORT that has not * successfully been added using sas_port_add().
*/ void sas_port_free(struct sas_port *port)
{
transport_destroy_device(&port->dev);
put_device(&port->dev);
}
EXPORT_SYMBOL(sas_port_free);
/** * sas_port_delete - remove SAS PORT * @port: SAS PORT to remove * * Removes the specified SAS PORT. If the SAS PORT has an * associated phys, unlink them from the port as well.
*/ void sas_port_delete(struct sas_port *port)
{ struct device *dev = &port->dev; struct sas_phy *phy, *tmp_phy;
if (port->rphy) {
sas_rphy_delete(port->rphy);
port->rphy = NULL;
}
/** * scsi_is_sas_port - check if a struct device represents a SAS port * @dev: device to check * * Returns: * %1 if the device represents a SAS Port, %0 else
*/ int scsi_is_sas_port(conststruct device *dev)
{ return dev->release == sas_port_release;
}
EXPORT_SYMBOL(scsi_is_sas_port);
/** * sas_port_get_phy - try to take a reference on a port member * @port: port to check
*/ struct sas_phy *sas_port_get_phy(struct sas_port *port)
{ struct sas_phy *phy;
/** * sas_port_add_phy - add another phy to a port to form a wide port * @port: port to add the phy to * @phy: phy to add * * When a port is initially created, it is empty (has no phys). All * ports must have at least one phy to operated, and all wide ports * must have at least two. The current code makes no difference * between ports and wide ports, but the only object that can be * connected to a remote device is a port, so ports must be formed on * all devices with phys if they're connected to anything.
*/ void sas_port_add_phy(struct sas_port *port, struct sas_phy *phy)
{
mutex_lock(&port->phy_list_mutex); if (unlikely(!list_empty(&phy->port_siblings))) { /* make sure we're already on this port */ struct sas_phy *tmp;
list_for_each_entry(tmp, &port->phy_list, port_siblings) if (tmp == phy) break; /* If this trips, you added a phy that was already
* part of a different port */ if (unlikely(tmp != phy)) {
dev_printk(KERN_ERR, &port->dev, "trying to add phy %s fails: it's already part of another port\n",
dev_name(&phy->dev));
BUG();
}
} else {
sas_port_create_link(port, phy);
list_add_tail(&phy->port_siblings, &port->phy_list);
port->num_phys++;
}
mutex_unlock(&port->phy_list_mutex);
}
EXPORT_SYMBOL(sas_port_add_phy);
/** * sas_port_delete_phy - remove a phy from a port or wide port * @port: port to remove the phy from * @phy: phy to remove * * This operation is used for tearing down ports again. It must be * done to every port or wide port before calling sas_port_delete.
*/ void sas_port_delete_phy(struct sas_port *port, struct sas_phy *phy)
{
mutex_lock(&port->phy_list_mutex);
sas_port_delete_link(port, phy);
list_del_init(&phy->port_siblings);
port->num_phys--;
mutex_unlock(&port->phy_list_mutex);
}
EXPORT_SYMBOL(sas_port_delete_phy);
/** * sas_rphy_initialize - common rphy initialization * @rphy: rphy to initialise * * Used by both sas_end_device_alloc() and sas_expander_alloc() to * initialise the common rphy component of each.
*/ staticvoid sas_rphy_initialize(struct sas_rphy *rphy)
{
INIT_LIST_HEAD(&rphy->list);
}
/** * sas_end_device_alloc - allocate an rphy for an end device * @parent: which port * * Allocates an SAS remote PHY structure, connected to @parent. * * Returns: * SAS PHY allocated or %NULL if the allocation failed.
*/ struct sas_rphy *sas_end_device_alloc(struct sas_port *parent)
{ struct Scsi_Host *shost = dev_to_shost(&parent->dev); struct sas_end_device *rdev;
rdev = kzalloc(sizeof(*rdev), GFP_KERNEL); if (!rdev) { return NULL;
}
/** * sas_expander_alloc - allocate an rphy for an end device * @parent: which port * @type: SAS_EDGE_EXPANDER_DEVICE or SAS_FANOUT_EXPANDER_DEVICE * * Allocates an SAS remote PHY structure, connected to @parent. * * Returns: * SAS PHY allocated or %NULL if the allocation failed.
*/ struct sas_rphy *sas_expander_alloc(struct sas_port *parent, enum sas_device_type type)
{ struct Scsi_Host *shost = dev_to_shost(&parent->dev); struct sas_expander_device *rdev; struct sas_host_attrs *sas_host = to_sas_host_attrs(shost);
BUG_ON(type != SAS_EDGE_EXPANDER_DEVICE &&
type != SAS_FANOUT_EXPANDER_DEVICE);
rdev = kzalloc(sizeof(*rdev), GFP_KERNEL); if (!rdev) { return NULL;
}
/** * sas_rphy_add - add a SAS remote PHY to the device hierarchy * @rphy: The remote PHY to be added * * Publishes a SAS remote PHY to the rest of the system.
*/ int sas_rphy_add(struct sas_rphy *rphy)
{ struct sas_port *parent = dev_to_sas_port(rphy->dev.parent); struct Scsi_Host *shost = dev_to_shost(parent->dev.parent); struct sas_host_attrs *sas_host = to_sas_host_attrs(shost); struct sas_identify *identify = &rphy->identify; int error;
if (parent->rphy) return -ENXIO;
parent->rphy = rphy;
error = device_add(&rphy->dev); if (error) return error;
transport_add_device(&rphy->dev);
transport_configure_device(&rphy->dev); if (sas_bsg_initialize(shost, rphy))
printk("fail to a bsg device %s\n", dev_name(&rphy->dev));
/** * sas_rphy_free - free a SAS remote PHY * @rphy: SAS remote PHY to free * * Frees the specified SAS remote PHY. * * Note: * This function must only be called on a remote * PHY that has not successfully been added using * sas_rphy_add() (or has been sas_rphy_remove()'d)
*/ void sas_rphy_free(struct sas_rphy *rphy)
{ struct device *dev = &rphy->dev; struct Scsi_Host *shost = dev_to_shost(rphy->dev.parent->parent); struct sas_host_attrs *sas_host = to_sas_host_attrs(shost);
/** * sas_rphy_delete - remove and free SAS remote PHY * @rphy: SAS remote PHY to remove and free * * Removes the specified SAS remote PHY and frees it.
*/ void
sas_rphy_delete(struct sas_rphy *rphy)
{
sas_rphy_remove(rphy);
sas_rphy_free(rphy);
}
EXPORT_SYMBOL(sas_rphy_delete);
/** * sas_rphy_unlink - unlink SAS remote PHY * @rphy: SAS remote phy to unlink from its parent port * * Removes port reference to an rphy
*/ void sas_rphy_unlink(struct sas_rphy *rphy)
{ struct sas_port *parent = dev_to_sas_port(rphy->dev.parent);
/** * scsi_is_sas_rphy - check if a struct device represents a SAS remote PHY * @dev: device to check * * Returns: * %1 if the device represents a SAS remote PHY, %0 else
*/ int scsi_is_sas_rphy(conststruct device *dev)
{ return dev->release == sas_end_device_release ||
dev->release == sas_expander_release;
}
EXPORT_SYMBOL(scsi_is_sas_rphy);
/** * sas_attach_transport - instantiate SAS transport template * @ft: SAS transport class function template
*/ struct scsi_transport_template *
sas_attach_transport(struct sas_function_template *ft)
{ struct sas_internal *i; int count;
i = kzalloc(sizeof(struct sas_internal), GFP_KERNEL); if (!i) return NULL;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.