1 // SPDX-License-Identifier: GPL-2.0
3 * Serial Attached SCSI (SAS) Port class
5 * Copyright (C) 2005 Adaptec, Inc. All rights reserved.
6 * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
9 #include "sas_internal.h"
11 #include <scsi/scsi_transport.h>
12 #include <scsi/scsi_transport_sas.h>
13 #include "../scsi_sas_internal.h"
15 static bool phy_is_wideport_member(struct asd_sas_port
*port
, struct asd_sas_phy
*phy
)
17 struct sas_ha_struct
*sas_ha
= phy
->ha
;
19 if (memcmp(port
->attached_sas_addr
, phy
->attached_sas_addr
,
20 SAS_ADDR_SIZE
) != 0 || (sas_ha
->strict_wide_ports
&&
21 memcmp(port
->sas_addr
, phy
->sas_addr
, SAS_ADDR_SIZE
) != 0))
26 static void sas_resume_port(struct asd_sas_phy
*phy
)
28 struct domain_device
*dev
;
29 struct asd_sas_port
*port
= phy
->port
;
30 struct sas_ha_struct
*sas_ha
= phy
->ha
;
31 struct sas_internal
*si
= to_sas_internal(sas_ha
->core
.shost
->transportt
);
33 if (si
->dft
->lldd_port_formed
)
34 si
->dft
->lldd_port_formed(phy
);
39 /* we only need to handle "link returned" actions once */
43 /* if the port came back:
44 * 1/ presume every device came back
45 * 2/ force the next revalidation to check all expander phys
47 list_for_each_entry(dev
, &port
->dev_list
, dev_list_node
) {
50 rc
= sas_notify_lldd_dev_found(dev
);
52 sas_unregister_dev(port
, dev
);
53 sas_destruct_devices(port
);
57 if (dev_is_expander(dev
->dev_type
)) {
58 dev
->ex_dev
.ex_change_count
= -1;
59 for (i
= 0; i
< dev
->ex_dev
.num_phys
; i
++) {
60 struct ex_phy
*phy
= &dev
->ex_dev
.ex_phy
[i
];
62 phy
->phy_change_count
= -1;
67 sas_discover_event(port
, DISCE_RESUME
);
71 * sas_form_port - add this phy to a port
72 * @phy: the phy of interest
74 * This function adds this phy to an existing port, thus creating a wide
75 * port, or it creates a port and adds the phy to the port.
77 static void sas_form_port(struct asd_sas_phy
*phy
)
80 struct sas_ha_struct
*sas_ha
= phy
->ha
;
81 struct asd_sas_port
*port
= phy
->port
;
82 struct domain_device
*port_dev
;
83 struct sas_internal
*si
=
84 to_sas_internal(sas_ha
->core
.shost
->transportt
);
88 if (!phy_is_wideport_member(port
, phy
))
89 sas_deform_port(phy
, 0);
90 else if (phy
->suspended
) {
94 /* phy came back, try to cancel the timeout */
95 wake_up(&sas_ha
->eh_wait_q
);
98 pr_info("%s: phy%d belongs to port%d already(%d)!\n",
99 __func__
, phy
->id
, phy
->port
->id
,
100 phy
->port
->num_phys
);
105 /* see if the phy should be part of a wide port */
106 spin_lock_irqsave(&sas_ha
->phy_port_lock
, flags
);
107 for (i
= 0; i
< sas_ha
->num_phys
; i
++) {
108 port
= sas_ha
->sas_port
[i
];
109 spin_lock(&port
->phy_list_lock
);
110 if (*(u64
*) port
->sas_addr
&&
111 phy_is_wideport_member(port
, phy
) && port
->num_phys
> 0) {
113 pr_debug("phy%d matched wide port%d\n", phy
->id
,
117 spin_unlock(&port
->phy_list_lock
);
119 /* The phy does not match any existing port, create a new one */
120 if (i
== sas_ha
->num_phys
) {
121 for (i
= 0; i
< sas_ha
->num_phys
; i
++) {
122 port
= sas_ha
->sas_port
[i
];
123 spin_lock(&port
->phy_list_lock
);
124 if (*(u64
*)port
->sas_addr
== 0
125 && port
->num_phys
== 0) {
126 memcpy(port
->sas_addr
, phy
->sas_addr
,
130 spin_unlock(&port
->phy_list_lock
);
134 if (i
>= sas_ha
->num_phys
) {
135 pr_err("%s: couldn't find a free port, bug?\n", __func__
);
136 spin_unlock_irqrestore(&sas_ha
->phy_port_lock
, flags
);
140 /* add the phy to the port */
141 port_dev
= port
->port_dev
;
142 list_add_tail(&phy
->port_phy_el
, &port
->phy_list
);
143 sas_phy_set_target(phy
, port_dev
);
146 port
->phy_mask
|= (1U << phy
->id
);
148 if (*(u64
*)port
->attached_sas_addr
== 0) {
149 port
->class = phy
->class;
150 memcpy(port
->attached_sas_addr
, phy
->attached_sas_addr
,
152 port
->iproto
= phy
->iproto
;
153 port
->tproto
= phy
->tproto
;
154 port
->oob_mode
= phy
->oob_mode
;
155 port
->linkrate
= phy
->linkrate
;
157 port
->linkrate
= max(port
->linkrate
, phy
->linkrate
);
158 spin_unlock(&port
->phy_list_lock
);
159 spin_unlock_irqrestore(&sas_ha
->phy_port_lock
, flags
);
162 port
->port
= sas_port_alloc(phy
->phy
->dev
.parent
, port
->id
);
164 sas_port_add(port
->port
);
166 sas_port_add_phy(port
->port
, phy
->phy
);
168 pr_debug("%s added to %s, phy_mask:0x%x (%016llx)\n",
169 dev_name(&phy
->phy
->dev
), dev_name(&port
->port
->dev
),
171 SAS_ADDR(port
->attached_sas_addr
));
174 port_dev
->pathways
= port
->num_phys
;
176 /* Tell the LLDD about this port formation. */
177 if (si
->dft
->lldd_port_formed
)
178 si
->dft
->lldd_port_formed(phy
);
180 sas_discover_event(phy
->port
, DISCE_DISCOVER_DOMAIN
);
181 /* Only insert a revalidate event after initial discovery */
182 if (port_dev
&& dev_is_expander(port_dev
->dev_type
)) {
183 struct expander_device
*ex_dev
= &port_dev
->ex_dev
;
185 ex_dev
->ex_change_count
= -1;
186 sas_discover_event(port
, DISCE_REVALIDATE_DOMAIN
);
188 flush_workqueue(sas_ha
->disco_q
);
192 * sas_deform_port - remove this phy from the port it belongs to
193 * @phy: the phy of interest
194 * @gone: whether or not the PHY is gone
196 * This is called when the physical link to the other phy has been
197 * lost (on this phy), in Event thread context. We cannot delay here.
199 void sas_deform_port(struct asd_sas_phy
*phy
, int gone
)
201 struct sas_ha_struct
*sas_ha
= phy
->ha
;
202 struct asd_sas_port
*port
= phy
->port
;
203 struct sas_internal
*si
=
204 to_sas_internal(sas_ha
->core
.shost
->transportt
);
205 struct domain_device
*dev
;
209 return; /* done by a phy event */
211 dev
= port
->port_dev
;
215 if (port
->num_phys
== 1) {
216 sas_unregister_domain_devices(port
, gone
);
217 sas_destruct_devices(port
);
218 sas_port_delete(port
->port
);
221 sas_port_delete_phy(port
->port
, phy
->phy
);
222 sas_device_set_phy(dev
, port
->port
);
225 if (si
->dft
->lldd_port_deformed
)
226 si
->dft
->lldd_port_deformed(phy
);
228 spin_lock_irqsave(&sas_ha
->phy_port_lock
, flags
);
229 spin_lock(&port
->phy_list_lock
);
231 list_del_init(&phy
->port_phy_el
);
232 sas_phy_set_target(phy
, NULL
);
235 port
->phy_mask
&= ~(1U << phy
->id
);
237 if (port
->num_phys
== 0) {
238 INIT_LIST_HEAD(&port
->phy_list
);
239 memset(port
->sas_addr
, 0, SAS_ADDR_SIZE
);
240 memset(port
->attached_sas_addr
, 0, SAS_ADDR_SIZE
);
247 spin_unlock(&port
->phy_list_lock
);
248 spin_unlock_irqrestore(&sas_ha
->phy_port_lock
, flags
);
250 /* Only insert revalidate event if the port still has members */
251 if (port
->port
&& dev
&& dev_is_expander(dev
->dev_type
)) {
252 struct expander_device
*ex_dev
= &dev
->ex_dev
;
254 ex_dev
->ex_change_count
= -1;
255 sas_discover_event(port
, DISCE_REVALIDATE_DOMAIN
);
257 flush_workqueue(sas_ha
->disco_q
);
262 /* ---------- SAS port events ---------- */
264 void sas_porte_bytes_dmaed(struct work_struct
*work
)
266 struct asd_sas_event
*ev
= to_asd_sas_event(work
);
267 struct asd_sas_phy
*phy
= ev
->phy
;
272 void sas_porte_broadcast_rcvd(struct work_struct
*work
)
274 struct asd_sas_event
*ev
= to_asd_sas_event(work
);
275 struct asd_sas_phy
*phy
= ev
->phy
;
279 spin_lock_irqsave(&phy
->sas_prim_lock
, flags
);
280 prim
= phy
->sas_prim
;
281 spin_unlock_irqrestore(&phy
->sas_prim_lock
, flags
);
283 pr_debug("broadcast received: %d\n", prim
);
284 sas_discover_event(phy
->port
, DISCE_REVALIDATE_DOMAIN
);
287 flush_workqueue(phy
->port
->ha
->disco_q
);
290 void sas_porte_link_reset_err(struct work_struct
*work
)
292 struct asd_sas_event
*ev
= to_asd_sas_event(work
);
293 struct asd_sas_phy
*phy
= ev
->phy
;
295 sas_deform_port(phy
, 1);
298 void sas_porte_timer_event(struct work_struct
*work
)
300 struct asd_sas_event
*ev
= to_asd_sas_event(work
);
301 struct asd_sas_phy
*phy
= ev
->phy
;
303 sas_deform_port(phy
, 1);
306 void sas_porte_hard_reset(struct work_struct
*work
)
308 struct asd_sas_event
*ev
= to_asd_sas_event(work
);
309 struct asd_sas_phy
*phy
= ev
->phy
;
311 sas_deform_port(phy
, 1);
314 /* ---------- SAS port registration ---------- */
316 static void sas_init_port(struct asd_sas_port
*port
,
317 struct sas_ha_struct
*sas_ha
, int i
)
319 memset(port
, 0, sizeof(*port
));
321 INIT_LIST_HEAD(&port
->dev_list
);
322 INIT_LIST_HEAD(&port
->disco_list
);
323 INIT_LIST_HEAD(&port
->destroy_list
);
324 INIT_LIST_HEAD(&port
->sas_port_del_list
);
325 spin_lock_init(&port
->phy_list_lock
);
326 INIT_LIST_HEAD(&port
->phy_list
);
329 spin_lock_init(&port
->dev_list_lock
);
332 int sas_register_ports(struct sas_ha_struct
*sas_ha
)
336 /* initialize the ports and discovery */
337 for (i
= 0; i
< sas_ha
->num_phys
; i
++) {
338 struct asd_sas_port
*port
= sas_ha
->sas_port
[i
];
340 sas_init_port(port
, sas_ha
, i
);
341 sas_init_disc(&port
->disc
, port
);
346 void sas_unregister_ports(struct sas_ha_struct
*sas_ha
)
350 for (i
= 0; i
< sas_ha
->num_phys
; i
++)
351 if (sas_ha
->sas_phy
[i
]->port
)
352 sas_deform_port(sas_ha
->sas_phy
[i
], 0);
356 const work_func_t sas_port_event_fns
[PORT_NUM_EVENTS
] = {
357 [PORTE_BYTES_DMAED
] = sas_porte_bytes_dmaed
,
358 [PORTE_BROADCAST_RCVD
] = sas_porte_broadcast_rcvd
,
359 [PORTE_LINK_RESET_ERR
] = sas_porte_link_reset_err
,
360 [PORTE_TIMER_EVENT
] = sas_porte_timer_event
,
361 [PORTE_HARD_RESET
] = sas_porte_hard_reset
,