diff options
-rw-r--r-- | Documentation/scsi/scsi_fc_transport.txt | 450 | ||||
-rw-r--r-- | drivers/scsi/scsi_transport_fc.c | 805 | ||||
-rw-r--r-- | include/scsi/scsi_transport_fc.h | 173 |
3 files changed, 1399 insertions, 29 deletions
diff --git a/Documentation/scsi/scsi_fc_transport.txt b/Documentation/scsi/scsi_fc_transport.txt new file mode 100644 index 00000000000..ab057afc757 --- /dev/null +++ b/Documentation/scsi/scsi_fc_transport.txt @@ -0,0 +1,450 @@ + SCSI FC Tansport + ============================================= + +Date: 4/12/2007 +Kernel Revisions for features: + rports : <<TBS>> + vports : 2.6.22 (? TBD) + + +Introduction +============ +This file documents the features and components of the SCSI FC Transport. +It also provides documents the API between the transport and FC LLDDs. +The FC transport can be found at: + drivers/scsi/scsi_transport_fc.c + include/scsi/scsi_transport_fc.h + include/scsi/scsi_netlink_fc.h + +This file is found at Documentation/scsi/scsi_fc_transport.txt + + +FC Remote Ports (rports) +======================================================================== +<< To Be Supplied >> + + +FC Virtual Ports (vports) +======================================================================== + +Overview: +------------------------------- + + New FC standards have defined mechanisms which allows for a single physical + port to appear on as multiple communication ports. Using the N_Port Id + Virtualization (NPIV) mechanism, a point-to-point connection to a Fabric + can be assigned more than 1 N_Port_ID. Each N_Port_ID appears as a + separate port to other endpoints on the fabric, even though it shares one + physical link to the switch for communication. Each N_Port_ID can have a + unique view of the fabric based on fabric zoning and array lun-masking + (just like a normal non-NPIV adapter). Using the Virtual Fabric (VF) + mechanism, adding a fabric header to each frame allows the port to + interact with the Fabric Port to join multiple fabrics. The port will + obtain an N_Port_ID on each fabric it joins. Each fabric will have its + own unique view of endpoints and configuration parameters. NPIV may be + used together with VF so that the port can obtain multiple N_Port_IDs + on each virtual fabric. + + The FC transport is now recognizing a new object - a vport. A vport is + an entity that has a world-wide unique World Wide Port Name (wwpn) and + World Wide Node Name (wwnn). The transport also allows for the FC4's to + be specified for the vport, with FCP_Initiator being the primary role + expected. Once instantiated by one of the above methods, it will have a + distinct N_Port_ID and view of fabric endpoints and storage entities. + The fc_host associated with the physical adapter will export the ability + to create vports. The transport will create the vport object within the + Linux device tree, and instruct the fc_host's driver to instantiate the + virtual port. Typically, the driver will create a new scsi_host instance + on the vport, resulting in a unique <H,C,T,L> namespace for the vport. + Thus, whether a FC port is based on a physical port or on a virtual port, + each will appear as a unique scsi_host with its own target and lun space. + + Note: At this time, the transport is written to create only NPIV-based + vports. However, consideration was given to VF-based vports and it + should be a minor change to add support if needed. The remaining + discussion will concentrate on NPIV. + + Note: World Wide Name assignment (and uniqueness guarantees) are left + up to an administrative entity controling the vport. For example, + if vports are to be associated with virtual machines, a XEN mgmt + utility would be responsible for creating wwpn/wwnn's for the vport, + using it's own naming authority and OUI. (Note: it already does this + for virtual MAC addresses). + + +Device Trees and Vport Objects: +------------------------------- + + Today, the device tree typically contains the scsi_host object, + with rports and scsi target objects underneath it. Currently the FC + transport creates the vport object and places it under the scsi_host + object corresponding to the physical adapter. The LLDD will allocate + a new scsi_host for the vport and link it's object under the vport. + The remainder of the tree under the vports scsi_host is the same + as the non-NPIV case. The transport is written currently to easily + allow the parent of the vport to be something other than the scsi_host. + This could be used in the future to link the object onto a vm-specific + device tree. If the vport's parent is not the physical port's scsi_host, + a symbolic link to the vport object will be placed in the physical + port's scsi_host. + + Here's what to expect in the device tree : + The typical Physical Port's Scsi_Host: + /sys/devices/.../host17/ + and it has the typical decendent tree: + /sys/devices/.../host17/rport-17:0-0/target17:0:0/17:0:0:0: + and then the vport is created on the Physical Port: + /sys/devices/.../host17/vport-17:0-0 + and the vport's Scsi_Host is then created: + /sys/devices/.../host17/vport-17:0-0/host18 + and then the rest of the tree progresses, such as: + /sys/devices/.../host17/vport-17:0-0/host18/rport-18:0-0/target18:0:0/18:0:0:0: + + Here's what to expect in the sysfs tree : + scsi_hosts: + /sys/class/scsi_host/host17 physical port's scsi_host + /sys/class/scsi_host/host18 vport's scsi_host + fc_hosts: + /sys/class/fc_host/host17 physical port's fc_host + /sys/class/fc_host/host18 vport's fc_host + fc_vports: + /sys/class/fc_vports/vport-17:0-0 the vport's fc_vport + fc_rports: + /sys/class/fc_remote_ports/rport-17:0-0 rport on the physical port + /sys/class/fc_remote_ports/rport-18:0-0 rport on the vport + + +Vport Attributes: +------------------------------- + + The new fc_vport class object has the following attributes + + node_name: Read_Only + The WWNN of the vport + + port_name: Read_Only + The WWPN of the vport + + roles: Read_Only + Indicates the FC4 roles enabled on the vport. + + symbolic_name: Read_Write + A string, appended to the driver's symbolic port name string, which + is registered with the switch to identify the vport. For example, + a hypervisor could set this string to "Xen Domain 2 VM 5 Vport 2", + and this set of identifiers can be seen on switch management screens + to identify the port. + + vport_delete: Write_Only + When written with a "1", will tear down the vport. + + vport_disable: Write_Only + When written with a "1", will transition the vport to a disabled. + state. The vport will still be instantiated with the Linux kernel, + but it will not be active on the FC link. + When written with a "0", will enable the vport. + + vport_last_state: Read_Only + Indicates the previous state of the vport. See the section below on + "Vport States". + + vport_state: Read_Only + Indicates the state of the vport. See the section below on + "Vport States". + + vport_type: Read_Only + Reflects the FC mechanism used to create the virtual port. + Only NPIV is supported currently. + + + For the fc_host class object, the following attributes are added for vports: + + max_npiv_vports: Read_Only + Indicates the maximum number of NPIV-based vports that the + driver/adapter can support on the fc_host. + + npiv_vports_inuse: Read_Only + Indicates how many NPIV-based vports have been instantiated on the + fc_host. + + vport_create: Write_Only + A "simple" create interface to instantiate a vport on an fc_host. + A "<WWPN>:<WWNN>" string is written to the attribute. The transport + then instantiates the vport object and calls the LLDD to create the + vport with the role of FCP_Initiator. Each WWN is specified as 16 + hex characters and may *not* contain any prefixes (e.g. 0x, x, etc). + + vport_delete: Write_Only + A "simple" delete interface to teardown a vport. A "<WWPN>:<WWNN>" + string is written to the attribute. The transport will locate the + vport on the fc_host with the same WWNs and tear it down. Each WWN + is specified as 16 hex characters and may *not* contain any prefixes + (e.g. 0x, x, etc). + + +Vport States: +------------------------------- + + Vport instantiation consists of two parts: + - Creation with the kernel and LLDD. This means all transport and + driver data structures are built up, and device objects created. + This is equivalent to a driver "attach" on an adapter, which is + independent of the adapter's link state. + - Instantiation of the vport on the FC link via ELS traffic, etc. + This is equivalent to a "link up" and successfull link initialization. + Futher information can be found in the interfaces section below for + Vport Creation. + + Once a vport has been instantiated with the kernel/LLDD, a vport state + can be reported via the sysfs attribute. The following states exist: + + FC_VPORT_UNKNOWN - Unknown + An temporary state, typically set only while the vport is being + instantiated with the kernel and LLDD. + + FC_VPORT_ACTIVE - Active + The vport has been successfully been created on the FC link. + It is fully functional. + + FC_VPORT_DISABLED - Disabled + The vport instantiated, but "disabled". The vport is not instantiated + on the FC link. This is equivalent to a physical port with the + link "down". + + FC_VPORT_LINKDOWN - Linkdown + The vport is not operational as the physical link is not operational. + + FC_VPORT_INITIALIZING - Initializing + The vport is in the process of instantiating on the FC link. + The LLDD will set this state just prior to starting the ELS traffic + to create the vport. This state will persist until the vport is + successfully created (state becomes FC_VPORT_ACTIVE) or it fails + (state is one of the values below). As this state is transitory, + it will not be preserved in the "vport_last_state". + + FC_VPORT_NO_FABRIC_SUPP - No Fabric Support + The vport is not operational. One of the following conditions were + encountered: + - The FC topology is not Point-to-Point + - The FC port is not connected to an F_Port + - The F_Port has indicated that NPIV is not supported. + + FC_VPORT_NO_FABRIC_RSCS - No Fabric Resources + The vport is not operational. The Fabric failed FDISC with a status + indicating that it does not have sufficient resources to complete + the operation. + + FC_VPORT_FABRIC_LOGOUT - Fabric Logout + The vport is not operational. The Fabric has LOGO'd the N_Port_ID + associated with the vport. + + FC_VPORT_FABRIC_REJ_WWN - Fabric Rejected WWN + The vport is not operational. The Fabric failed FDISC with a status + indicating that the WWN's are not valid. + + FC_VPORT_FAILED - VPort Failed + The vport is not operational. This is a catchall for all other + error conditions. + + + The following state table indicates the different state transitions: + + State Event New State + -------------------------------------------------------------------- + n/a Initialization Unknown + Unknown: Link Down Linkdown + Link Up & Loop No Fabric Support + Link Up & no Fabric No Fabric Support + Link Up & FLOGI response No Fabric Support + indicates no NPIV support + Link Up & FDISC being sent Initializing + Disable request Disable + Linkdown: Link Up Unknown + Initializing: FDISC ACC Active + FDISC LS_RJT w/ no resources No Fabric Resources + FDISC LS_RJT w/ invalid Fabric Rejected WWN + pname or invalid nport_id + FDISC LS_RJT failed for Vport Failed + other reasons + Link Down Linkdown + Disable request Disable + Disable: Enable request Unknown + Active: LOGO received from fabric Fabric Logout + Link Down Linkdown + Disable request Disable + Fabric Logout: Link still up Unknown + + The following 4 error states all have the same transitions: + No Fabric Support: + No Fabric Resources: + Fabric Rejected WWN: + Vport Failed: + Disable request Disable + Link goes down Linkdown + + +Transport <-> LLDD Interfaces : +------------------------------- + +Vport support by LLDD: + + The LLDD indicates support for vports by supplying a vport_create() + function in the transport template. The presense of this function will + cause the creation of the new attributes on the fc_host. As part of + the physical port completing its initialization relative to the + transport, it should set the max_npiv_vports attribute to indicate the + maximum number of vports the driver and/or adapter supports. + + +Vport Creation: + + The LLDD vport_create() syntax is: + + int vport_create(struct fc_vport *vport, bool disable) + + where: + vport: Is the newly allocated vport object + disable: If "true", the vport is to be created in a disabled stated. + If "false", the vport is to be enabled upon creation. + + When a request is made to create a new vport (via sgio/netlink, or the + vport_create fc_host attribute), the transport will validate that the LLDD + can support another vport (e.g. max_npiv_vports > npiv_vports_inuse). + If not, the create request will be failed. If space remains, the transport + will increment the vport count, create the vport object, and then call the + LLDD's vport_create() function with the newly allocated vport object. + + As mentioned above, vport creation is divided into two parts: + - Creation with the kernel and LLDD. This means all transport and + driver data structures are built up, and device objects created. + This is equivalent to a driver "attach" on an adapter, which is + independent of the adapter's link state. + - Instantiation of the vport on the FC link via ELS traffic, etc. + This is equivalent to a "link up" and successfull link initialization. + + The LLDD's vport_create() function will not synchronously wait for both + parts to be fully completed before returning. It must validate that the + infrastructure exists to support NPIV, and complete the first part of + vport creation (data structure build up) before returning. We do not + hinge vport_create() on the link-side operation mainly because: + - The link may be down. It is not a failure if it is. It simply + means the vport is in an inoperable state until the link comes up. + This is consistent with the link bouncing post vport creation. + - The vport may be created in a disabled state. + - This is consistent with a model where: the vport equates to a + FC adapter. The vport_create is synonymous with driver attachment + to the adapter, which is independent of link state. + + Note: special error codes have been defined to delineate infrastructure + failure cases for quicker resolution. + + The expected behavior for the LLDD's vport_create() function is: + - Validate Infrastructure: + - If the driver or adapter cannot support another vport, whether + due to improper firmware, (a lie about) max_npiv, or a lack of + some other resource - return VPCERR_UNSUPPORTED. + - If the driver validates the WWN's against those already active on + the adapter and detects an overlap - return VPCERR_BAD_WWN. + - If the driver detects the topology is loop, non-fabric, or the + FLOGI did not support NPIV - return VPCERR_NO_FABRIC_SUPP. + - Allocate data structures. If errors are encountered, such as out + of memory conditions, return the respective negative Exxx error code. + - If the role is FCP Initiator, the LLDD is to : + - Call scsi_host_alloc() to allocate a scsi_host for the vport. + - Call scsi_add_host(new_shost, &vport->dev) to start the scsi_host + and bind it as a child of the vport device. + - Initializes the fc_host attribute values. + - Kick of further vport state transitions based on the disable flag and + link state - and return success (zero). + + LLDD Implementers Notes: + - It is suggested that there be a different fc_function_templates for + the physical port and the virtual port. The physical port's template + would have the vport_create, vport_delete, and vport_disable functions, + while the vports would not. + - It is suggested that there be different scsi_host_templates + for the physical port and virtual port. Likely, there are driver + attributes, embedded into the scsi_host_template, that are applicable + for the physical port only (link speed, topology setting, etc). This + ensures that the attributes are applicable to the respective scsi_host. + + +Vport Disable/Enable: + + The LLDD vport_disable() syntax is: + + int vport_disable(struct fc_vport *vport, bool disable) + + where: + vport: Is vport to to be enabled or disabled + disable: If "true", the vport is to be disabled. + If "false", the vport is to be enabled. + + When a request is made to change the disabled state on a vport, the + transport will validate the request against the existing vport state. + If the request is to disable and the vport is already disabled, the + request will fail. Similarly, if the request is to enable, and the + vport is not in a disabled state, the request will fail. If the request + is valid for the vport state, the transport will call the LLDD to + change the vport's state. + + Within the LLDD, if a vport is disabled, it remains instantiated with + the kernel and LLDD, but it is not active or visible on the FC link in + any way. (see Vport Creation and the 2 part instantiation discussion). + The vport will remain in this state until it is deleted or re-enabled. + When enabling a vport, the LLDD reinstantiates the vport on the FC + link - essentially restarting the LLDD statemachine (see Vport States + above). + + +Vport Deletion: + + The LLDD vport_delete() syntax is: + + int vport_delete(struct fc_vport *vport) + + where: + vport: Is vport to delete + + When a request is made to delete a vport (via sgio/netlink, or via the + fc_host or fc_vport vport_delete attributes), the transport will call + the LLDD to terminate the vport on the FC link, and teardown all other + datastructures and references. If the LLDD completes successfully, + the transport will teardown the vport objects and complete the vport + removal. If the LLDD delete request fails, the vport object will remain, + but will be in an indeterminate state. + + Within the LLDD, the normal code paths for a scsi_host teardown should + be followed. E.g. If the vport has a FCP Initiator role, the LLDD + will call fc_remove_host() for the vports scsi_host, followed by + scsi_remove_host() and scsi_host_put() for the vports scsi_host. + + +Other: + fc_host port_type attribute: + There is a new fc_host port_type value - FC_PORTTYPE_NPIV. This value + must be set on all vport-based fc_hosts. Normally, on a physical port, + the port_type attribute would be set to NPORT, NLPORT, etc based on the + topology type and existence of the fabric. As this is not applicable to + a vport, it makes more sense to report the FC mechanism used to create + the vport. + + Driver unload: + FC drivers are required to call fc_remove_host() prior to calling + scsi_remove_host(). This allows the fc_host to tear down all remote + ports prior the scsi_host being torn down. The fc_remove_host() call + was updated to remove all vports for the fc_host as well. + + +Credits +======= +The following people have contributed to this document: + + + + + + +James Smart +james.smart@emulex.com + diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c index b4d1ece46f7..21765146811 100644 --- a/drivers/scsi/scsi_transport_fc.c +++ b/drivers/scsi/scsi_transport_fc.c @@ -19,7 +19,7 @@ * * ======== * - * Copyright (C) 2004-2005 James Smart, Emulex Corporation + * Copyright (C) 2004-2007 James Smart, Emulex Corporation * Rewrite for host, target, device, and remote port attributes, * statistics, and service functions... * @@ -39,6 +39,33 @@ static int fc_queue_work(struct Scsi_Host *, struct work_struct *); /* + * This is a temporary carrier for creating a vport. It will eventually + * be replaced by a real message definition for sgio or netlink. + * + * fc_vport_identifiers: This set of data contains all elements + * to uniquely identify and instantiate a FC virtual port. + * + * Notes: + * symbolic_name: The driver is to append the symbolic_name string data + * to the symbolic_node_name data that it generates by default. + * the resulting combination should then be registered with the switch. + * It is expected that things like Xen may stuff a VM title into + * this field. + */ +struct fc_vport_identifiers { + u64 node_name; + u64 port_name; + u32 roles; + bool disable; + enum fc_port_type vport_type; /* only FC_PORTTYPE_NPIV allowed */ + char symbolic_name[FC_VPORT_SYMBOLIC_NAMELEN]; +}; + +static int fc_vport_create(struct Scsi_Host *shost, int channel, + struct device *pdev, struct fc_vport_identifiers *ids, + struct fc_vport **vport); + +/* * Redefine so that we can have same named attributes in the * sdev/starget/host objects. */ @@ -90,10 +117,14 @@ static struct { { FC_PORTTYPE_NLPORT, "NLPort (fabric via loop)" }, { FC_PORTTYPE_LPORT, "LPort (private loop)" }, { FC_PORTTYPE_PTP, "Point-To-Point (direct nport connection" }, + { FC_PORTTYPE_NPIV, "NPIV VPORT" }, }; fc_enum_name_search(port_type, fc_port_type, fc_port_type_names) #define FC_PORTTYPE_MAX_NAMELEN 50 +/* Reuse fc_port_type enum function for vport_type */ +#define get_fc_vport_type_name get_fc_port_type_name + /* Convert fc_host_event_code values to ascii string name */ static const struct { @@ -139,6 +170,29 @@ fc_enum_name_search(port_state, fc_port_state, fc_port_state_names) #define FC_PORTSTATE_MAX_NAMELEN 20 +/* Convert fc_vport_state values to ascii string name */ +static struct { + enum fc_vport_state value; + char *name; +} fc_vport_state_names[] = { + { FC_VPORT_UNKNOWN, "Unknown" }, + { FC_VPORT_ACTIVE, "Active" }, + { FC_VPORT_DISABLED, "Disabled" }, + { FC_VPORT_LINKDOWN, "Linkdown" }, + { FC_VPORT_INITIALIZING, "Initializing" }, + { FC_VPORT_NO_FABRIC_SUPP, "No Fabric Support" }, + { FC_VPORT_NO_FABRIC_RSCS, "No Fabric Resources" }, + { FC_VPORT_FABRIC_LOGOUT, "Fabric Logout" }, + { FC_VPORT_FABRIC_REJ_WWN, "Fabric Rejected WWN" }, + { FC_VPORT_FAILED, "VPort Failed" }, +}; +fc_enum_name_search(vport_state, fc_vport_state, fc_vport_state_names) +#define FC_VPORTSTATE_MAX_NAMELEN 24 + +/* Reuse fc_vport_state enum function for vport_last_state */ +#define get_fc_vport_last_state_name get_fc_vport_state_name + + /* Convert fc_tgtid_binding_type values to ascii string name */ static const struct { enum fc_tgtid_binding_type value; @@ -219,16 +273,16 @@ show_fc_fc4s (char *buf, u8 *fc4_list) } -/* Convert FC_RPORT_ROLE bit values to ascii string name */ +/* Convert FC_PORT_ROLE bit values to ascii string name */ static const struct { u32 value; char *name; -} fc_remote_port_role_names[] = { - { FC_RPORT_ROLE_FCP_TARGET, "FCP Target" }, - { FC_RPORT_ROLE_FCP_INITIATOR, "FCP Initiator" }, - { FC_RPORT_ROLE_IP_PORT, "IP Port" }, +} fc_port_role_names[] = { + { FC_PORT_ROLE_FCP_TARGET, "FCP Target" }, + { FC_PORT_ROLE_FCP_INITIATOR, "FCP Initiator" }, + { FC_PORT_ROLE_IP_PORT, "IP Port" }, }; -fc_bitfield_name_search(remote_port_roles, fc_remote_port_role_names) +fc_bitfield_name_search(port_roles, fc_port_role_names) /* * Define roles that are specific to port_id. Values are relative to ROLE_MASK. @@ -252,7 +306,8 @@ static void fc_scsi_scan_rport(struct work_struct *work); */ #define FC_STARGET_NUM_ATTRS 3 #define FC_RPORT_NUM_ATTRS 10 -#define FC_HOST_NUM_ATTRS 17 +#define FC_VPORT_NUM_ATTRS 9 +#define FC_HOST_NUM_ATTRS 21 struct fc_internal { struct scsi_transport_template t; @@ -278,6 +333,10 @@ struct fc_internal { struct transport_container rport_attr_cont; struct class_device_attribute private_rport_attrs[FC_RPORT_NUM_ATTRS]; struct class_device_attribute *rport_attrs[FC_RPORT_NUM_ATTRS + 1]; + + struct transport_container vport_attr_cont; + struct class_device_attribute private_vport_attrs[FC_VPORT_NUM_ATTRS]; + struct class_device_attribute *vport_attrs[FC_VPORT_NUM_ATTRS + 1]; }; #define to_fc_internal(tmpl) container_of(tmpl, struct fc_internal, t) @@ -331,6 +390,7 @@ static int fc_host_setup(struct transport_container *tc, struct device *dev, sizeof(fc_host->supported_fc4s)); fc_host->supported_speeds = FC_PORTSPEED_UNKNOWN; fc_host->maxframe_size = -1; + fc_host->max_npiv_vports = 0; memset(fc_host->serial_number, 0, sizeof(fc_host->serial_number)); @@ -348,8 +408,11 @@ static int fc_host_setup(struct transport_container *tc, struct device *dev, INIT_LIST_HEAD(&fc_host->rports); INIT_LIST_HEAD(&fc_host->rport_bindings); + INIT_LIST_HEAD(&fc_host->vports); fc_host->next_rport_number = 0; fc_host->next_target_id = 0; + fc_host->next_vport_number = 0; + fc_host->npiv_vports_inuse = 0; snprintf(fc_host->work_q_name, KOBJ_NAME_LEN, "fc_wq_%d", shost->host_no); @@ -388,6 +451,16 @@ static DECLARE_TRANSPORT_CLASS(fc_rport_class, NULL); /* + * Setup and Remove actions for virtual ports are handled + * in the service functions below. + */ +static DECLARE_TRANSPORT_CLASS(fc_vport_class, + "fc_vports", + NULL, + NULL, + NULL); + +/* * Module Parameters */ @@ -585,6 +658,9 @@ static __init int fc_transport_init(void) error = transport_class_register(&fc_host_class); if (error) return error; + error = transport_class_register(&fc_vport_class); + if (error) + return error; error = transport_class_register(&fc_rport_class); if (error) return error; @@ -596,6 +672,7 @@ static void __exit fc_transport_exit(void) transport_class_unregister(&fc_transport_class); transport_class_unregister(&fc_rport_class); transport_class_unregister(&fc_host_class); + transport_class_unregister(&fc_vport_class); } /* @@ -800,9 +877,9 @@ show_fc_rport_roles (struct class_device *cdev, char *buf) return snprintf(buf, 30, "Unknown Fabric Entity\n"); } } else { - if (rport->roles == FC_RPORT_ROLE_UNKNOWN) + if (rport->roles == FC_PORT_ROLE_UNKNOWN) return snprintf(buf, 20, "unknown\n"); - return get_fc_remote_port_roles_names(rport->roles, buf); + return get_fc_port_roles_names(rport->roles, buf); } } static FC_CLASS_DEVICE_ATTR(rport, roles, S_IRUGO, @@ -857,7 +934,7 @@ static FC_CLASS_DEVICE_ATTR(rport, fast_io_fail_tmo, S_IRUGO | S_IWUSR, /* * Note: in the target show function we recognize when the remote - * port is in the hierarchy and do not allow the driver to get + * port is in the heirarchy and do not allow the driver to get * involved in sysfs functions. The driver only gets involved if * it's the "old" style that doesn't use rports. */ @@ -912,6 +989,260 @@ fc_starget_rd_attr(port_id, "0x%06x\n", 20); /* + * FC Virtual Port Attribute Management + */ + +#define fc_vport_show_function(field, format_string, sz, cast) \ +static ssize_t \ +show_fc_vport_##field (struct class_device *cdev, char *buf) \ +{ \ + struct fc_vport *vport = transport_class_to_vport(cdev); \ + struct Scsi_Host *shost = vport_to_shost(vport); \ + struct fc_internal *i = to_fc_internal(shost->transportt); \ + if ((i->f->get_vport_##field) && \ + !(vport->flags & (FC_VPORT_DEL | FC_VPORT_CREATING))) \ + i->f->get_vport_##field(vport); \ + return snprintf(buf, sz, format_string, cast vport->field); \ +} + +#define fc_vport_store_function(field) \ +static ssize_t \ +store_fc_vport_##field(struct class_device *cdev, const char *buf, \ + size_t count) \ +{ \ + int val; \ + struct fc_vport *vport = transport_class_to_vport(cdev); \ + struct Scsi_Host *shost = vport_to_shost(vport); \ + struct fc_internal *i = to_fc_internal(shost->transportt); \ + char *cp; \ + if (vport->flags & (FC_VPORT_DEL | FC_VPORT_CREATING)) \ + return -EBUSY; \ + val = simple_strtoul(buf, &cp, 0); \ + if (*cp && (*cp != '\n')) \ + return -EINVAL; \ + i->f->set_vport_##field(vport, val); \ + return count; \ +} + +#define fc_vport_store_str_function(field, slen) \ +static ssize_t \ +store_fc_vport_##field(struct class_device *cdev, const char *buf, \ + size_t count) \ +{ \ + struct fc_vport *vport = transport_class_to_vport(cdev); \ + struct Scsi_Host *shost = vport_to_shost(vport); \ + struct fc_internal *i = to_fc_internal(shost->transportt); \ + unsigned int cnt=count; \ + \ + /* count may include a LF at end of string */ \ + if (buf[cnt-1] == '\n') \ + cnt--; \ + if (cnt > ((slen) - 1)) \ + return -EINVAL; \ + memcpy(vport->field, buf, cnt); \ + i->f->set_vport_##field(vport); \ + return count; \ +} + +#define fc_vport_rd_attr(field, format_string, sz) \ + fc_vport_show_function(field, format_string, sz, ) \ +static FC_CLASS_DEVICE_ATTR(vport, field, S_IRUGO, \ + show_fc_vport_##field, NULL) + +#define fc_vport_rd_attr_cast(field, format_string, sz, cast) \ + fc_vport_show_function(field, format_string, sz, (cast)) \ +static FC_CLASS_DEVICE_ATTR(vport, field, S_IRUGO, \ + show_fc_vport_##field, NULL) + +#define fc_vport_rw_attr(field, format_string, sz) \ + fc_vport_show_function(field, format_string, sz, ) \ + fc_vport_store_function(field) \ +static FC_CLASS_DEVICE_ATTR(vport, field, S_IRUGO | S_IWUSR, \ + show_fc_vport_##field, \ + store_fc_vport_##field) + +#define fc_private_vport_show_function(field, format_string, sz, cast) \ +static ssize_t \ +show_fc_vport_##field (struct class_device *cdev, char *buf) \ +{ \ + struct fc_vport *vport = transport_class_to_vport(cdev); \ + return snprintf(buf, sz, format_string, cast vport->field); \ +} + +#define fc_private_vport_store_u32_function(field) \ +static ssize_t \ +store_fc_vport_##field(struct class_device *cdev, const char *buf, \ + size_t count) \ +{ \ + u32 val; \ + struct fc_vport *vport = transport_class_to_vport(cdev); \ + char *cp; \ + if (vport->flags & (FC_VPORT_DEL | FC_VPORT_CREATING)) \ + return -EBUSY; \ + val = simple_strtoul(buf, &cp, 0); \ + if (*cp && (*cp != '\n')) \ + return -EINVAL; \ + vport->field = val; \ + return count; \ +} + + +#define fc_private_vport_rd_attr(field, format_string, sz) \ + fc_private_vport_show_function(field, format_string, sz, ) \ +static FC_CLASS_DEVICE_ATTR(vport, field, S_IRUGO, \ + show_fc_vport_##field, NULL) + +#define fc_private_vport_rd_attr_cast(field, format_string, sz, cast) \ + fc_private_vport_show_function(field, format_string, sz, (cast)) \ +static FC_CLASS_DEVICE_ATTR(vport, field, S_IRUGO, \ + show_fc_vport_##field, NULL) + +#define fc_private_vport_rw_u32_attr(field, format_string, sz) \ + fc_private_vport_show_function(field, format_string, sz, ) \ + fc_private_vport_store_u32_function(field) \ +static FC_CLASS_DEVICE_ATTR(vport, field, S_IRUGO | S_IWUSR, \ + show_fc_vport_##field, \ + store_fc_vport_##field) + + +#define fc_private_vport_rd_enum_attr(title, maxlen) \ +static ssize_t \ +show_fc_vport_##title (struct class_device *cdev, char *buf) \ +{ \ + struct fc_vport *vport = transport_class_to_vport(cdev); \ + const char *name; \ + name = get_fc_##title##_name(vport->title); \ + if (!name) \ + return -EINVAL; \ + return snprintf(buf, maxlen, "%s\n", name); \ +} \ +static FC_CLASS_DEVICE_ATTR(vport, title, S_IRUGO, \ + show_fc_vport_##title, NULL) + + +#define SETUP_VPORT_ATTRIBUTE_RD(field) \ + i->private_vport_attrs[count] = class_device_attr_vport_##field; \ + i->private_vport_attrs[count].attr.mode = S_IRUGO; \ + i->private_vport_attrs[count].store = NULL; \ + i->vport_attrs[count] = &i->private_vport_attrs[count]; \ + if (i->f->get_##field) \ + count++ + /* NOTE: Above MACRO differs: checks function not show bit */ + +#define SETUP_PRIVATE_VPORT_ATTRIBUTE_RD(field) \ + i->private_vport_attrs[count] = class_device_attr_vport_##field; \ + i->private_vport_attrs[count].attr.mode = S_IRUGO; \ + i->private_vport_attrs[count].store = NULL; \ + i->vport_attrs[count] = &i->private_vport_attrs[count]; \ + count++ + +#define SETUP_VPORT_ATTRIBUTE_WR(field) \ + i->private_vport_attrs[count] = class_device_attr_vport_##field; \ + i->vport_attrs[count] = &i->private_vport_attrs[count]; \ + if (i->f->field) \ + count++ + /* NOTE: Above MACRO differs: checks function */ + +#define SETUP_VPORT_ATTRIBUTE_RW(field) \ + i->private_vport_attrs[count] = class_device_attr_vport_##field; \ + if (!i->f->set_vport_##field) { \ + i->private_vport_attrs[count].attr.mode = S_IRUGO; \ + i->private_vport_attrs[count].store = NULL; \ + } \ + i->vport_attrs[count] = &i->private_vport_attrs[count]; \ + count++ + /* NOTE: Above MACRO differs: does not check show bit */ + +#define SETUP_PRIVATE_VPORT_ATTRIBUTE_RW(field) \ +{ \ + i->private_vport_attrs[count] = class_device_attr_vport_##field; \ + i->vport_attrs[count] = &i->private_vport_attrs[count]; \ + count++; \ +} + + +/* The FC Transport Virtual Port Attributes: */ + +/* Fixed Virtual Port Attributes */ + +/* Dynamic Virtual Port Attributes */ + +/* Private Virtual Port Attributes */ + +fc_private_vport_rd_enum_attr(vport_state, FC_VPORTSTATE_MAX_NAMELEN); +fc_private_vport_rd_enum_attr(vport_last_state, FC_VPORTSTATE_MAX_NAMELEN); +fc_private_vport_rd_attr_cast(node_name, "0x%llx\n", 20, unsigned long long); +fc_private_vport_rd_attr_cast(port_name, "0x%llx\n", 20, unsigned long long); + +static ssize_t +show_fc_vport_roles (struct class_device *cdev, char *buf) +{ + struct fc_vport *vport = transport_class_to_vport(cdev); + + if (vport->roles == FC_PORT_ROLE_UNKNOWN) + return snprintf(buf, 20, "unknown\n"); + return get_fc_port_roles_names(vport->roles, buf); +} +static FC_CLASS_DEVICE_ATTR(vport, roles, S_IRUGO, show_fc_vport_roles, NULL); + +fc_private_vport_rd_enum_attr(vport_type, FC_PORTTYPE_MAX_NAMELEN); + +fc_private_vport_show_function(symbolic_name, "%s\n", + FC_VPORT_SYMBOLIC_NAMELEN + 1, ) +fc_vport_store_str_function(symbolic_name, FC_VPORT_SYMBOLIC_NAMELEN) +static FC_CLASS_DEVICE_ATTR(vport, symbolic_name, S_IRUGO | S_IWUSR, + show_fc_vport_symbolic_name, store_fc_vport_symbolic_name); + +static ssize_t +store_fc_vport_delete(struct class_device *cdev, const char *buf, + size_t count) +{ + struct fc_vport *vport = transport_class_to_vport(cdev); + int stat; + + stat = fc_vport_terminate(vport); + if (stat) + return stat; + + return count; +} +static FC_CLASS_DEVICE_ATTR(vport, vport_delete, S_IWUSR, + NULL, store_fc_vport_delete); + + +/* + * Enable/Disable vport + * Write "1" to disable, write "0" to enable + */ +static ssize_t +store_fc_vport_disable(struct class_device *cdev, const char *buf, + size_t count) +{ + struct fc_vport *vport = transport_class_to_vport(cdev); + struct Scsi_Host *shost = vport_to_shost(vport); + struct fc_internal *i = to_fc_internal(shost->transportt); + int stat; + + if (vport->flags & (FC_VPORT_DEL | FC_VPORT_CREATING)) + return -EBUSY; + + if (*buf == '0') { + if (vport->vport_state != FC_VPORT_DISABLED) + return -EALREADY; + } else if (*buf == '1') { + if (vport->vport_state == FC_VPORT_DISABLED) + return -EALREADY; + } else + return -EINVAL; + + stat = i->f->vport_disable(vport, ((*buf == '0') ? false : true)); + return stat ? stat : count; +} +static FC_CLASS_DEVICE_ATTR(vport, vport_disable, S_IWUSR, + NULL, store_fc_vport_disable); + + +/* * Host Attribute Management */ @@ -1003,6 +1334,13 @@ static FC_CLASS_DEVICE_ATTR(host, title, S_IRUGO, show_fc_host_##title, NULL) if (i->f->show_host_##field) \ count++ +#define SETUP_HOST_ATTRIBUTE_RD_NS(field) \ + i->private_host_attrs[count] = class_device_attr_host_##field; \ + i->private_host_attrs[count].attr.mode = S_IRUGO; \ + i->private_host_attrs[count].store = NULL; \ + i->host_attrs[count] = &i->private_host_attrs[count]; \ + count++ + #define SETUP_HOST_ATTRIBUTE_RW(field) \ i->private_host_attrs[count] = class_device_attr_host_##field; \ if (!i->f->set_host_##field) { \ @@ -1090,6 +1428,7 @@ fc_private_host_rd_attr_cast(port_name, "0x%llx\n", 20, unsigned long long); fc_private_host_rd_attr_cast(permanent_port_name, "0x%llx\n", 20, unsigned long long); fc_private_host_rd_attr(maxframe_size, "%u bytes\n", 20); +fc_private_host_rd_attr(max_npiv_vports, "%u\n", 20); fc_private_host_rd_attr(serial_number, "%s\n", (FC_SERIAL_NUMBER_SIZE +1)); @@ -1210,6 +1549,9 @@ store_fc_private_host_issue_lip(struct class_device *cdev, static FC_CLASS_DEVICE_ATTR(host, issue_lip, S_IWUSR, NULL, store_fc_private_host_issue_lip); +fc_private_host_rd_attr(npiv_vports_inuse, "%u\n", 20); + + /* * Host Statistics Management */ @@ -1285,7 +1627,6 @@ fc_reset_statistics(struct class_device *cdev, const char *buf, static FC_CLASS_DEVICE_ATTR(host, reset_statistics, S_IWUSR, NULL, fc_reset_statistics); - static struct attribute *fc_statistics_attrs[] = { &class_device_attr_host_seconds_since_last_reset.attr, &class_device_attr_host_tx_frames.attr, @@ -1316,6 +1657,142 @@ static s |