Commit a88dc3ec authored by Mauro Carvalho Chehab's avatar Mauro Carvalho Chehab Committed by Martin K. Petersen
parent ac69461b
...@@ -23,5 +23,6 @@ Linux SCSI Subsystem ...@@ -23,5 +23,6 @@ Linux SCSI Subsystem
g_NCR5380 g_NCR5380
hpsa hpsa
hptiop hptiop
libsas
scsi_transport_srp/figures scsi_transport_srp/figures
.. SPDX-License-Identifier: GPL-2.0
=========
SAS Layer SAS Layer
--------- =========
The SAS Layer is a management infrastructure which manages The SAS Layer is a management infrastructure which manages
SAS LLDDs. It sits between SCSI Core and SAS LLDDs. The SAS LLDDs. It sits between SCSI Core and SAS LLDDs. The
...@@ -37,16 +40,21 @@ It will then return. Then you enable your phys to actually ...@@ -37,16 +40,21 @@ It will then return. Then you enable your phys to actually
start OOB (at which point your driver will start calling the start OOB (at which point your driver will start calling the
notify_* event callbacks). notify_* event callbacks).
Structure descriptions: Structure descriptions
======================
``struct sas_phy``
------------------
struct sas_phy --------------------
Normally this is statically embedded to your driver's Normally this is statically embedded to your driver's
phy structure: phy structure::
struct my_phy { struct my_phy {
blah; blah;
struct sas_phy sas_phy; struct sas_phy sas_phy;
bleh; bleh;
}; };
And then all the phys are an array of my_phy in your HA And then all the phys are an array of my_phy in your HA
struct (shown below). struct (shown below).
...@@ -63,94 +71,122 @@ There is a scheme where the LLDD can RW certain fields, ...@@ -63,94 +71,122 @@ There is a scheme where the LLDD can RW certain fields,
and the SAS layer can only read such ones, and vice versa. and the SAS layer can only read such ones, and vice versa.
The idea is to avoid unnecessary locking. The idea is to avoid unnecessary locking.
enabled -- must be set (0/1) enabled
id -- must be set [0,MAX_PHYS) - must be set (0/1)
class, proto, type, role, oob_mode, linkrate -- must be set
oob_mode -- you set this when OOB has finished and then notify id
the SAS Layer. - must be set [0,MAX_PHYS)]
sas_addr -- this normally points to an array holding the sas class, proto, type, role, oob_mode, linkrate
address of the phy, possibly somewhere in your my_phy - must be set
struct.
oob_mode
attached_sas_addr -- set this when you (LLDD) receive an - you set this when OOB has finished and then notify
IDENTIFY frame or a FIS frame, _before_ notifying the SAS the SAS Layer.
layer. The idea is that sometimes the LLDD may want to fake
or provide a different SAS address on that phy/port and this sas_addr
allows it to do this. At best you should copy the sas - this normally points to an array holding the sas
address from the IDENTIFY frame or maybe generate a SAS address of the phy, possibly somewhere in your my_phy
address for SATA directly attached devices. The Discover struct.
process may later change this.
attached_sas_addr
frame_rcvd -- this is where you copy the IDENTIFY/FIS frame - set this when you (LLDD) receive an
when you get it; you lock, copy, set frame_rcvd_size and IDENTIFY frame or a FIS frame, _before_ notifying the SAS
unlock the lock, and then call the event. It is a pointer layer. The idea is that sometimes the LLDD may want to fake
since there's no way to know your hw frame size _exactly_, or provide a different SAS address on that phy/port and this
so you define the actual array in your phy struct and let allows it to do this. At best you should copy the sas
this pointer point to it. You copy the frame from your address from the IDENTIFY frame or maybe generate a SAS
DMAable memory to that area holding the lock. address for SATA directly attached devices. The Discover
process may later change this.
sas_prim -- this is where primitives go when they're
received. See sas.h. Grab the lock, set the primitive, frame_rcvd
release the lock, notify. - this is where you copy the IDENTIFY/FIS frame
when you get it; you lock, copy, set frame_rcvd_size and
port -- this points to the sas_port if the phy belongs unlock the lock, and then call the event. It is a pointer
to a port -- the LLDD only reads this. It points to the since there's no way to know your hw frame size _exactly_,
sas_port this phy is part of. Set by the SAS Layer. so you define the actual array in your phy struct and let
this pointer point to it. You copy the frame from your
ha -- may be set; the SAS layer sets it anyway. DMAable memory to that area holding the lock.
lldd_phy -- you should set this to point to your phy so you sas_prim
can find your way around faster when the SAS layer calls one - this is where primitives go when they're
of your callbacks and passes you a phy. If the sas_phy is received. See sas.h. Grab the lock, set the primitive,
embedded you can also use container_of -- whatever you release the lock, notify.
prefer.
port
- this points to the sas_port if the phy belongs
struct sas_port -------------------- to a port -- the LLDD only reads this. It points to the
sas_port this phy is part of. Set by the SAS Layer.
ha
- may be set; the SAS layer sets it anyway.
lldd_phy
- you should set this to point to your phy so you
can find your way around faster when the SAS layer calls one
of your callbacks and passes you a phy. If the sas_phy is
embedded you can also use container_of -- whatever you
prefer.
``struct sas_port``
-------------------
The LLDD doesn't set any fields of this struct -- it only The LLDD doesn't set any fields of this struct -- it only
reads them. They should be self explanatory. reads them. They should be self explanatory.
phy_mask is 32 bit, this should be enough for now, as I phy_mask is 32 bit, this should be enough for now, as I
haven't heard of a HA having more than 8 phys. haven't heard of a HA having more than 8 phys.
lldd_port -- I haven't found use for that -- maybe other lldd_port
LLDD who wish to have internal port representation can make - I haven't found use for that -- maybe other
use of this. LLDD who wish to have internal port representation can make
use of this.
``struct sas_ha_struct``
------------------------
struct sas_ha_struct --------------------
It normally is statically declared in your own LLDD It normally is statically declared in your own LLDD
structure describing your adapter: structure describing your adapter::
struct my_sas_ha {
struct my_sas_ha {
blah; blah;
struct sas_ha_struct sas_ha; struct sas_ha_struct sas_ha;
struct my_phy phys[MAX_PHYS]; struct my_phy phys[MAX_PHYS];
struct sas_port sas_ports[MAX_PHYS]; /* (1) */ struct sas_port sas_ports[MAX_PHYS]; /* (1) */
bleh; bleh;
}; };
(1) If your LLDD doesn't have its own port representation. (1) If your LLDD doesn't have its own port representation.
What needs to be initialized (sample function given below). What needs to be initialized (sample function given below).
pcidev pcidev
sas_addr -- since the SAS layer doesn't want to mess with ^^^^^^
sas_addr
- since the SAS layer doesn't want to mess with
memory allocation, etc, this points to statically memory allocation, etc, this points to statically
allocated array somewhere (say in your host adapter allocated array somewhere (say in your host adapter
structure) and holds the SAS address of the host structure) and holds the SAS address of the host
adapter as given by you or the manufacturer, etc. adapter as given by you or the manufacturer, etc.
sas_port sas_port
sas_phy -- an array of pointers to structures. (see ^^^^^^^^
sas_phy
- an array of pointers to structures. (see
note above on sas_addr). note above on sas_addr).
These must be set. See more notes below. These must be set. See more notes below.
num_phys -- the number of phys present in the sas_phy array,
num_phys
- the number of phys present in the sas_phy array,
and the number of ports present in the sas_port and the number of ports present in the sas_port
array. There can be a maximum num_phys ports (one per array. There can be a maximum num_phys ports (one per
port) so we drop the num_ports, and only use port) so we drop the num_ports, and only use
num_phys. num_phys.
The event interface: The event interface::
/* LLDD calls these to notify the class of an event. */ /* LLDD calls these to notify the class of an event. */
void (*notify_ha_event)(struct sas_ha_struct *, enum ha_event); void (*notify_ha_event)(struct sas_ha_struct *, enum ha_event);
...@@ -161,7 +197,7 @@ When sas_register_ha() returns, those are set and can be ...@@ -161,7 +197,7 @@ When sas_register_ha() returns, those are set and can be
called by the LLDD to notify the SAS layer of such events called by the LLDD to notify the SAS layer of such events
the SAS layer. the SAS layer.
The port notification: The port notification::
/* The class calls these to notify the LLDD of an event. */ /* The class calls these to notify the LLDD of an event. */
void (*lldd_port_formed)(struct sas_phy *); void (*lldd_port_formed)(struct sas_phy *);
...@@ -171,7 +207,7 @@ If the LLDD wants notification when a port has been formed ...@@ -171,7 +207,7 @@ If the LLDD wants notification when a port has been formed
or deformed it sets those to a function satisfying the type. or deformed it sets those to a function satisfying the type.
A SAS LLDD should also implement at least one of the Task A SAS LLDD should also implement at least one of the Task
Management Functions (TMFs) described in SAM: Management Functions (TMFs) described in SAM::
/* Task Management Functions. Must be called from process context. */ /* Task Management Functions. Must be called from process context. */
int (*lldd_abort_task)(struct sas_task *); int (*lldd_abort_task)(struct sas_task *);
...@@ -184,7 +220,7 @@ Management Functions (TMFs) described in SAM: ...@@ -184,7 +220,7 @@ Management Functions (TMFs) described in SAM:
For more information please read SAM from T10.org. For more information please read SAM from T10.org.
Port and Adapter management: Port and Adapter management::
/* Port and Adapter management */ /* Port and Adapter management */
int (*lldd_clear_nexus_port)(struct sas_port *); int (*lldd_clear_nexus_port)(struct sas_port *);
...@@ -192,20 +228,21 @@ Port and Adapter management: ...@@ -192,20 +228,21 @@ Port and Adapter management:
A SAS LLDD should implement at least one of those. A SAS LLDD should implement at least one of those.
Phy management: Phy management::
/* Phy management */ /* Phy management */
int (*lldd_control_phy)(struct sas_phy *, enum phy_func); int (*lldd_control_phy)(struct sas_phy *, enum phy_func);
lldd_ha -- set this to point to your HA struct. You can also lldd_ha
use container_of if you embedded it as shown above. - set this to point to your HA struct. You can also
use container_of if you embedded it as shown above.
A sample initialization and registration function A sample initialization and registration function
can look like this (called last thing from probe()) can look like this (called last thing from probe())
*but* before you enable the phys to do OOB: *but* before you enable the phys to do OOB::
static int register_sas_ha(struct my_sas_ha *my_ha) static int register_sas_ha(struct my_sas_ha *my_ha)
{ {
int i; int i;
static struct sas_phy *sas_phys[MAX_PHYS]; static struct sas_phy *sas_phys[MAX_PHYS];
static struct sas_port *sas_ports[MAX_PHYS]; static struct sas_port *sas_ports[MAX_PHYS];
...@@ -242,25 +279,27 @@ static int register_sas_ha(struct my_sas_ha *my_ha) ...@@ -242,25 +279,27 @@ static int register_sas_ha(struct my_sas_ha *my_ha)
my_ha->sas_ha.lldd_control_phy = my_control_phy; my_ha->sas_ha.lldd_control_phy = my_control_phy;
return sas_register_ha(&my_ha->sas_ha); return sas_register_ha(&my_ha->sas_ha);
} }
(2) SAS 1.1 does not define I_T Nexus Reset TMF. (2) SAS 1.1 does not define I_T Nexus Reset TMF.
Events Events
------ ======
Events are _the only way_ a SAS LLDD notifies the SAS layer Events are **the only way** a SAS LLDD notifies the SAS layer
of anything. There is no other method or way a LLDD to tell of anything. There is no other method or way a LLDD to tell
the SAS layer of anything happening internally or in the SAS the SAS layer of anything happening internally or in the SAS
domain. domain.
Phy events: Phy events::
PHYE_LOSS_OF_SIGNAL, (C) PHYE_LOSS_OF_SIGNAL, (C)
PHYE_OOB_DONE, PHYE_OOB_DONE,
PHYE_OOB_ERROR, (C) PHYE_OOB_ERROR, (C)
PHYE_SPINUP_HOLD. PHYE_SPINUP_HOLD.
Port events, passed on a _phy_: Port events, passed on a _phy_::
PORTE_BYTES_DMAED, (M) PORTE_BYTES_DMAED, (M)
PORTE_BROADCAST_RCVD, (E) PORTE_BROADCAST_RCVD, (E)
PORTE_LINK_RESET_ERR, (C) PORTE_LINK_RESET_ERR, (C)
...@@ -271,6 +310,7 @@ Host Adapter event: ...@@ -271,6 +310,7 @@ Host Adapter event:
HAE_RESET HAE_RESET
A SAS LLDD should be able to generate A SAS LLDD should be able to generate
- at least one event from group C (choice), - at least one event from group C (choice),
- events marked M (mandatory) are mandatory (only one), - events marked M (mandatory) are mandatory (only one),
- events marked E (expander) if it wants the SAS layer - events marked E (expander) if it wants the SAS layer
...@@ -279,26 +319,42 @@ A SAS LLDD should be able to generate ...@@ -279,26 +319,42 @@ A SAS LLDD should be able to generate
Meaning: Meaning:
HAE_RESET -- when your HA got internal error and was reset. HAE_RESET
- when your HA got internal error and was reset.
PORTE_BYTES_DMAED
- on receiving an IDENTIFY/FIS frame
PORTE_BROADCAST_RCVD
- on receiving a primitive
PORTE_LINK_RESET_ERR
- timer expired, loss of signal, loss of DWS, etc. [1]_
PORTE_BYTES_DMAED -- on receiving an IDENTIFY/FIS frame PORTE_TIMER_EVENT
PORTE_BROADCAST_RCVD -- on receiving a primitive - DWS reset timeout timer expired [1]_
PORTE_LINK_RESET_ERR -- timer expired, loss of signal, loss
of DWS, etc. (*)
PORTE_TIMER_EVENT -- DWS reset timeout timer expired (*)
PORTE_HARD_RESET -- Hard Reset primitive received.
PHYE_LOSS_OF_SIGNAL -- the device is gone (*) PORTE_HARD_RESET
PHYE_OOB_DONE -- OOB went fine and oob_mode is valid - Hard Reset primitive received.
PHYE_OOB_ERROR -- Error while doing OOB, the device probably
got disconnected. (*)
PHYE_SPINUP_HOLD -- SATA is present, COMWAKE not sent.
(*) should set/clear the appropriate fields in the phy, PHYE_LOSS_OF_SIGNAL
- the device is gone [1]_
PHYE_OOB_DONE
- OOB went fine and oob_mode is valid
PHYE_OOB_ERROR
- Error while doing OOB, the device probably
got disconnected. [1]_
PHYE_SPINUP_HOLD
- SATA is present, COMWAKE not sent.
.. [1] should set/clear the appropriate fields in the phy,
or alternatively call the inlined sas_phy_disconnected() or alternatively call the inlined sas_phy_disconnected()
which is just a helper, from their tasklet. which is just a helper, from their tasklet.
The Execute Command SCSI RPC: The Execute Command SCSI RPC::
int (*lldd_execute_task)(struct sas_task *, gfp_t gfp_flags); int (*lldd_execute_task)(struct sas_task *, gfp_t gfp_flags);
...@@ -311,10 +367,14 @@ That is, when lldd_execute_task() is called, the command ...@@ -311,10 +367,14 @@ That is, when lldd_execute_task() is called, the command
go out on the transport *immediately*. There is *no* go out on the transport *immediately*. There is *no*
queuing of any sort and at any level in a SAS LLDD. queuing of any sort and at any level in a SAS LLDD.
Returns: -SAS_QUEUE_FULL, -ENOMEM, nothing was queued; Returns:
0, the task(s) were queued.
* -SAS_QUEUE_FULL, -ENOMEM, nothing was queued;
* 0, the task(s) were queued.
struct sas_task { ::
struct sas_task {
dev -- the device this task is destined to dev -- the device this task is destined to
task_proto -- _one_ of enum sas_proto task_proto -- _one_ of enum sas_proto
scatter -- pointer to scatter gather list array scatter -- pointer to scatter gather list array
...@@ -322,12 +382,13 @@ struct sas_task { ...@@ -322,12 +382,13 @@ struct sas_task {
total_xfer_len -- total number of bytes expected to be transferred total_xfer_len -- total number of bytes expected to be transferred
data_dir -- PCI_DMA_... data_dir -- PCI_DMA_...
task_done -- callback when the task has finished execution task_done -- callback when the task has finished execution
}; };
DISCOVERY Discovery
--------- =========
The sysfs tree has the following purposes: The sysfs tree has the following purposes:
a) It shows you the physical layout of the SAS domain at a) It shows you the physical layout of the SAS domain at
the current time, i.e. how the domain looks in the the current time, i.e. how the domain looks in the
physical world right now. physical world right now.
...@@ -336,6 +397,7 @@ The sysfs tree has the following purposes: ...@@ -336,6 +397,7 @@ The sysfs tree has the following purposes:
This is a link to the tree(1) program, very useful in This is a link to the tree(1) program, very useful in
viewing the SAS domain: viewing the SAS domain:
ftp://mama.indstate.edu/linux/tree/ ftp://mama.indstate.edu/linux/tree/
I expect user space applications to actually create a I expect user space applications to actually create a
graphical interface of this. graphical interface of this.
...@@ -359,7 +421,7 @@ contents of the domain_device structure, but it never creates ...@@ -359,7 +421,7 @@ contents of the domain_device structure, but it never creates
or destroys one. or destroys one.
Expander management from User Space Expander management from User Space
----------------------------------- ===================================
In each expander directory in sysfs, there is a file called In each expander directory in sysfs, there is a file called
"smp_portal". It is a binary sysfs attribute file, which "smp_portal". It is a binary sysfs attribute file, which
...@@ -371,15 +433,23 @@ Functionality is deceptively simple: ...@@ -371,15 +433,23 @@ Functionality is deceptively simple:
1. Build the SMP frame you want to send. The format and layout 1. Build the SMP frame you want to send. The format and layout
is described in the SAS spec. Leave the CRC field equal 0. is described in the SAS spec. Leave the CRC field equal 0.
open(2) open(2)
2. Open the expander's SMP portal sysfs file in RW mode. 2. Open the expander's SMP portal sysfs file in RW mode.
write(2) write(2)
3. Write the frame you built in 1. 3. Write the frame you built in 1.
read(2) read(2)
4. Read the amount of data you expect to receive for the frame you built. 4. Read the amount of data you expect to receive for the frame you built.
If you receive different amount of data you expected to receive, If you receive different amount of data you expected to receive,
then there was some kind of error. then there was some kind of error.
close(2) close(2)
All this process is shown in detail in the function do_smp_func() All this process is shown in detail in the function do_smp_func()
and its callers, in the file "expander_conf.c". and its callers, in the file "expander_conf.c".
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment