1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause OR GPL-2.0
3 *
4 * This file is provided under a dual BSD/GPLv2 license. When using or
5 * redistributing this file, you may do so under either license.
6 *
7 * GPL LICENSE SUMMARY
8 *
9 * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of version 2 of the GNU General Public License as
13 * published by the Free Software Foundation.
14 *
15 * This program is distributed in the hope that it will be useful, but
16 * WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
23 * The full GNU General Public License is included in this distribution
24 * in the file called LICENSE.GPL.
25 *
26 * BSD LICENSE
27 *
28 * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
29 * All rights reserved.
30 *
31 * Redistribution and use in source and binary forms, with or without
32 * modification, are permitted provided that the following conditions
33 * are met:
34 *
35 * * Redistributions of source code must retain the above copyright
36 * notice, this list of conditions and the following disclaimer.
37 * * Redistributions in binary form must reproduce the above copyright
38 * notice, this list of conditions and the following disclaimer in
39 * the documentation and/or other materials provided with the
40 * distribution.
41 *
42 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
43 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
44 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
45 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
46 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
47 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
48 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
49 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
50 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
51 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
52 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
53 */
54
55 #include <sys/cdefs.h>
56 /**
57 * @file
58 *
59 * @brief This file contains all of the entrance and exit methods for each
60 * of the domain states defined by the SCI_BASE_DOMAIN state
61 * machine.
62 */
63
64 #include <dev/isci/scil/intel_sas.h>
65 #include <dev/isci/scil/scic_port.h>
66
67 #include <dev/isci/scil/scif_sas_logger.h>
68 #include <dev/isci/scil/scif_sas_domain.h>
69 #include <dev/isci/scil/scif_sas_controller.h>
70 #include <dev/isci/scil/scic_controller.h>
71
72 //******************************************************************************
73 //* P R O T E C T E D M E T H O D S
74 //******************************************************************************
75
76 /**
77 * @brief This method will attempt to transition to the stopped state.
78 * The transition will only occur if the criteria for transition is
79 * met (i.e. all IOs are complete and all devices are stopped).
80 *
81 * @param[in] fw_domain This parameter specifies the domain in which to
82 * to attempt to perform the transition.
83 *
84 * @return none
85 */
scif_sas_domain_transition_to_stopped_state(SCIF_SAS_DOMAIN_T * fw_domain)86 void scif_sas_domain_transition_to_stopped_state(
87 SCIF_SAS_DOMAIN_T * fw_domain
88 )
89 {
90 SCIF_LOG_TRACE((
91 sci_base_object_get_logger(fw_domain),
92 SCIF_LOG_OBJECT_DOMAIN | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
93 "scif_sas_domain_transition_to_stopped_state(0x%x) enter\n",
94 fw_domain
95 ));
96
97 // If IOs are quiesced, and all remote devices are stopped,
98 // then transition directly to the STOPPED state.
99 if ( (fw_domain->request_list.element_count == 0)
100 && (fw_domain->device_start_count == 0) )
101 {
102 SCIF_LOG_INFO((
103 sci_base_object_get_logger(fw_domain),
104 SCIF_LOG_OBJECT_DOMAIN | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
105 "Domain:0x%x immediate transition to STOPPED\n",
106 fw_domain
107 ));
108
109 sci_base_state_machine_change_state(
110 &fw_domain->parent.state_machine, SCI_BASE_DOMAIN_STATE_STOPPED
111 );
112 }
113 }
114
115
116 /**
117 * @brief This method is called upon entrance to all states where the
118 * previous state may have been the DISCOVERING state.
119 * We issue the scif_cb_domain_discovery_complete() notification
120 * from this method, assuming pre-requisites are met, as opposed
121 * to in the exit handler of the DISCOVERING state, so that the
122 * appropriate state handlers are in place should the user decide
123 * to call scif_domain_discover() again.
124 *
125 * @param[in] fw_domain This parameter specifies the domain for which
126 * the state transition has occurred.
127 *
128 * @return none
129 */
130 static
scif_sas_domain_transition_from_discovering_state(SCIF_SAS_DOMAIN_T * fw_domain)131 void scif_sas_domain_transition_from_discovering_state(
132 SCIF_SAS_DOMAIN_T * fw_domain
133 )
134 {
135 SCIF_LOG_TRACE((
136 sci_base_object_get_logger(fw_domain),
137 SCIF_LOG_OBJECT_DOMAIN | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
138 "scif_sas_domain_transition_from_discovering_state(0x%x) enter\n",
139 fw_domain
140 ));
141
142 if (fw_domain->parent.state_machine.previous_state_id
143 == SCI_BASE_DOMAIN_STATE_DISCOVERING)
144 {
145 scif_sas_controller_restore_interrupt_coalescence(fw_domain->controller);
146
147 scif_cb_timer_stop(fw_domain->controller, fw_domain->operation.timer);
148
149 scif_cb_domain_discovery_complete(
150 fw_domain->controller, fw_domain, fw_domain->operation.status
151 );
152 }
153 }
154
155
156 /**
157 * @brief This method is called upon entrance to DISCOVERING state. Right before
158 * transitioning to DISCOVERING state, we temporarily change interrupt
159 * coalescence scheme.
160 *
161 * @param[in] fw_domain This parameter specifies the domain for which
162 * the state transition has occurred.
163 *
164 * @return none
165 */
scif_sas_domain_transition_to_discovering_state(SCIF_SAS_DOMAIN_T * fw_domain)166 void scif_sas_domain_transition_to_discovering_state(
167 SCIF_SAS_DOMAIN_T * fw_domain
168 )
169 {
170 scif_sas_controller_save_interrupt_coalescence(fw_domain->controller);
171
172 sci_base_state_machine_change_state(
173 &fw_domain->parent.state_machine, SCI_BASE_DOMAIN_STATE_DISCOVERING
174 );
175 }
176
177
178 /**
179 * @brief This method implements the actions taken when entering the
180 * INITIAL state.
181 *
182 * @param[in] object This parameter specifies the base object for which
183 * the state transition is occurring. This is cast into a
184 * SCIF_SAS_DOMAIN object in the method implementation.
185 *
186 * @return none
187 */
188 static
scif_sas_domain_initial_state_enter(SCI_BASE_OBJECT_T * object)189 void scif_sas_domain_initial_state_enter(
190 SCI_BASE_OBJECT_T * object
191 )
192 {
193 SCIF_SAS_DOMAIN_T * fw_domain = (SCIF_SAS_DOMAIN_T *)object;
194
195 SET_STATE_HANDLER(
196 fw_domain,
197 scif_sas_domain_state_handler_table,
198 SCI_BASE_DOMAIN_STATE_INITIAL
199 );
200
201 SCIF_LOG_TRACE((
202 sci_base_object_get_logger(fw_domain),
203 SCIF_LOG_OBJECT_DOMAIN,
204 "scif_sas_domain_initial_state_enter(0x%x) enter\n",
205 fw_domain
206 ));
207 }
208
209 /**
210 * @brief This method implements the actions taken when entering the
211 * STARTING state. This includes setting the state handlers and
212 * checking to see if the core port has already become READY.
213 *
214 * @param[in] object This parameter specifies the base object for which
215 * the state transition is occurring. This is cast into a
216 * SCIF_SAS_DOMAIN object in the method implementation.
217 *
218 * @return none
219 */
220 static
scif_sas_domain_starting_state_enter(SCI_BASE_OBJECT_T * object)221 void scif_sas_domain_starting_state_enter(
222 SCI_BASE_OBJECT_T * object
223 )
224 {
225 SCIF_SAS_DOMAIN_T * fw_domain = (SCIF_SAS_DOMAIN_T *)object;
226
227 SET_STATE_HANDLER(
228 fw_domain,
229 scif_sas_domain_state_handler_table,
230 SCI_BASE_DOMAIN_STATE_STARTING
231 );
232
233 SCIF_LOG_TRACE((
234 sci_base_object_get_logger(fw_domain),
235 SCIF_LOG_OBJECT_DOMAIN | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
236 "scif_sas_domain_starting_state_enter(0x%x) enter\n",
237 fw_domain
238 ));
239
240 scif_sas_domain_transition_from_discovering_state(fw_domain);
241
242 // If we entered the STARTING state and the core port is actually ready,
243 // then directly transition into the READY state. This can occur
244 // if we were in the middle of discovery when the port failed
245 // (causing a transition to STOPPING), then before reaching STOPPED
246 // the port becomes ready again.
247 if (fw_domain->is_port_ready == TRUE)
248 {
249 sci_base_state_machine_change_state(
250 &fw_domain->parent.state_machine, SCI_BASE_DOMAIN_STATE_READY
251 );
252 }
253 }
254
255 /**
256 * @brief This method implements the actions taken when entering the
257 * READY state. If the transition into this state came from:
258 * - the STARTING state, then alert the user via a
259 * scif_cb_domain_change_notification() that the domain
260 * has at least 1 device ready for discovery.
261 * - the DISCOVERING state, then alert the user that
262 * discovery is complete via the
263 * scif_cb_domain_discovery_complete() notification that
264 * discovery is finished.
265 *
266 * @param[in] object This parameter specifies the base object for which
267 * the state transition is occurring. This is cast into a
268 * SCIF_SAS_DOMAIN object in the method implementation.
269 *
270 * @return none
271 */
272 static
scif_sas_domain_ready_state_enter(SCI_BASE_OBJECT_T * object)273 void scif_sas_domain_ready_state_enter(
274 SCI_BASE_OBJECT_T * object
275 )
276 {
277 SCIF_SAS_DOMAIN_T * fw_domain = (SCIF_SAS_DOMAIN_T *)object;
278
279 SET_STATE_HANDLER(
280 fw_domain,
281 scif_sas_domain_state_handler_table,
282 SCI_BASE_DOMAIN_STATE_READY
283 );
284
285 SCIF_LOG_TRACE((
286 sci_base_object_get_logger(fw_domain),
287 SCIF_LOG_OBJECT_DOMAIN | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
288 "scif_sas_domain_ready_state_enter(0x%x) enter\n",
289 fw_domain
290 ));
291
292 if (fw_domain->parent.state_machine.previous_state_id
293 == SCI_BASE_DOMAIN_STATE_STARTING)
294 {
295 scif_cb_domain_ready(fw_domain->controller, fw_domain);
296
297 // Only indicate the domain change notification if the previous
298 // state was the STARTING state. We issue the notification here
299 // as opposed to exit of the STARTING state so that the appropriate
300 // state handlers are in place should the user call
301 // scif_domain_discover() from scif_cb_domain_change_notification()
302 scif_cb_domain_change_notification(fw_domain->controller, fw_domain);
303 }
304 else if (fw_domain->parent.state_machine.previous_state_id
305 == SCI_BASE_DOMAIN_STATE_DISCOVERING)
306 {
307 //if domain discovery timed out, we will NOT go back to discover even
308 //the broadcast change count is not zero. Instead we finish the discovery
309 //back to user. User can check the operation status and decide to
310 //retry discover all over again.
311 if (fw_domain->operation.status == SCI_FAILURE_TIMEOUT)
312 fw_domain->broadcast_change_count = 0;
313
314 // Check the broadcast change count to determine if discovery
315 // is indeed complete.
316 if (fw_domain->broadcast_change_count == 0)
317 {
318 scif_sas_domain_transition_from_discovering_state(fw_domain);
319 scif_cb_domain_ready(fw_domain->controller, fw_domain);
320 }
321 else
322 {
323 // The broadcast change count indicates something my have
324 // changed in the domain, while a discovery was ongoing.
325 // Thus, we should start discovery over again.
326 sci_base_state_machine_change_state(
327 &fw_domain->parent.state_machine, SCI_BASE_DOMAIN_STATE_DISCOVERING
328 );
329 }
330
331 // Enable the BCN because underneath hardware may disabled any further
332 // BCN.
333 scic_port_enable_broadcast_change_notification(fw_domain->core_object);
334 }
335 }
336
337 /**
338 * @brief This method implements the actions taken when exiting the
339 * READY state.
340 *
341 * @param[in] object This parameter specifies the base object for which
342 * the state transition is occurring. This is cast into a
343 * SCIF_SAS_DOMAIN object in the method implementation.
344 *
345 * @return none
346 */
347 static
scif_sas_domain_ready_state_exit(SCI_BASE_OBJECT_T * object)348 void scif_sas_domain_ready_state_exit(
349 SCI_BASE_OBJECT_T * object
350 )
351 {
352 SCIF_SAS_DOMAIN_T * fw_domain = (SCIF_SAS_DOMAIN_T *)object;
353
354 SCIF_LOG_TRACE((
355 sci_base_object_get_logger(fw_domain),
356 SCIF_LOG_OBJECT_DOMAIN | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
357 "scif_sas_domain_ready_state_exit(0x%x) enter\n",
358 fw_domain
359 ));
360
361 scif_cb_domain_not_ready(fw_domain->controller, fw_domain);
362 }
363
364 /**
365 * @brief This method implements the actions taken when entering the
366 * STOPPING state.
367 *
368 * @param[in] object This parameter specifies the base object for which
369 * the state transition is occurring. This is cast into a
370 * SCIF_SAS_DOMAIN object in the method implementation.
371 *
372 * @return none
373 */
374 static
scif_sas_domain_stopping_state_enter(SCI_BASE_OBJECT_T * object)375 void scif_sas_domain_stopping_state_enter(
376 SCI_BASE_OBJECT_T * object
377 )
378 {
379 SCIF_SAS_REMOTE_DEVICE_T * fw_device;
380 SCIF_SAS_DOMAIN_T * fw_domain = (SCIF_SAS_DOMAIN_T *)object;
381 SCI_ABSTRACT_ELEMENT_T * element = sci_abstract_list_get_front(
382 &fw_domain->remote_device_list
383 );
384
385 SET_STATE_HANDLER(
386 fw_domain,
387 scif_sas_domain_state_handler_table,
388 SCI_BASE_DOMAIN_STATE_STOPPING
389 );
390
391 // This must be invoked after the state handlers are set to ensure
392 // appropriate processing will occur if the user attempts to perform
393 // additional actions.
394 scif_sas_domain_transition_from_discovering_state(fw_domain);
395
396 SCIF_LOG_TRACE((
397 sci_base_object_get_logger(fw_domain),
398 SCIF_LOG_OBJECT_DOMAIN | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
399 "scif_sas_domain_stopping_state_enter(0x%x) enter\n",
400 fw_domain
401 ));
402
403 scif_sas_high_priority_request_queue_purge_domain(
404 &fw_domain->controller->hprq, fw_domain
405 );
406
407 // Search the domain's list of devices and put them all in the STOPPING
408 // state.
409 while (element != NULL)
410 {
411 fw_device = (SCIF_SAS_REMOTE_DEVICE_T*)
412 sci_abstract_list_get_object(element);
413
414 // This method will stop the core device. The core will terminate
415 // all IO requests currently outstanding.
416 fw_device->state_handlers->parent.stop_handler(&fw_device->parent);
417
418 element = sci_abstract_list_get_next(element);
419 }
420
421 // Attempt to transition to the stopped state.
422 scif_sas_domain_transition_to_stopped_state(fw_domain);
423 }
424
425 /**
426 * @brief This method implements the actions taken when entering the
427 * STOPPED state.
428 *
429 * @param[in] object This parameter specifies the base object for which
430 * the state transition is occurring. This is cast into a
431 * SCIF_SAS_DOMAIN object in the method implementation.
432 *
433 * @return none
434 */
435 static
scif_sas_domain_stopped_state_enter(SCI_BASE_OBJECT_T * object)436 void scif_sas_domain_stopped_state_enter(
437 SCI_BASE_OBJECT_T * object
438 )
439 {
440 SCIF_SAS_DOMAIN_T * fw_domain = (SCIF_SAS_DOMAIN_T *)object;
441
442 SET_STATE_HANDLER(
443 fw_domain,
444 scif_sas_domain_state_handler_table,
445 SCI_BASE_DOMAIN_STATE_STOPPED
446 );
447
448 SCIF_LOG_TRACE((
449 sci_base_object_get_logger(fw_domain),
450 SCIF_LOG_OBJECT_DOMAIN | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
451 "scif_sas_domain_stopped_state_enter(0x%x) enter\n",
452 fw_domain
453 ));
454
455 // A hot unplug of the direct attached device has occurred. Thus,
456 // notify the user. Note, if the controller is not in READY state,
457 // mostly likely the controller is in STOPPING or STOPPED state,
458 // meaning the controller is in the process of stopping, we should
459 // not call back to user in the middle of controller stopping.
460 if(fw_domain->controller->parent.state_machine.current_state_id
461 == SCI_BASE_CONTROLLER_STATE_READY)
462 scif_cb_domain_change_notification(fw_domain->controller, fw_domain);
463 }
464
465 /**
466 * @brief This method implements the actions taken when entering the
467 * DISCOVERING state. This includes determining from which
468 * state we entered. If we entered from stopping that some sort
469 * of hot-remove of the port occurred. In the hot-remove case
470 * all devices should be in the STOPPED state already and, as
471 * a result, are removed from the domain with a notification sent
472 * to the framework user.
473 *
474 * @note This method currently only handles hot-insert/hot-remove of
475 * direct attached SSP devices.
476 *
477 * @param[in] object This parameter specifies the base object for which
478 * the state transition is occurring. This is cast into a
479 * SCIF_SAS_DOMAIN object in the method implementation.
480 *
481 * @return none
482 */
483 static
scif_sas_domain_discovering_state_enter(SCI_BASE_OBJECT_T * object)484 void scif_sas_domain_discovering_state_enter(
485 SCI_BASE_OBJECT_T * object
486 )
487 {
488 SCIF_SAS_DOMAIN_T * fw_domain = (SCIF_SAS_DOMAIN_T *)object;
489
490 SET_STATE_HANDLER(
491 fw_domain,
492 scif_sas_domain_state_handler_table,
493 SCI_BASE_DOMAIN_STATE_DISCOVERING
494 );
495
496 SCIF_LOG_TRACE((
497 sci_base_object_get_logger(fw_domain),
498 SCIF_LOG_OBJECT_DOMAIN | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
499 "scif_sas_domain_discovering_state_enter(0x%x) enter\n",
500 fw_domain
501 ));
502
503 fw_domain->broadcast_change_count = 0;
504
505 // Did the domain just go through a port not ready action? If it did,
506 // then we will be entering from the STOPPED state.
507 if (fw_domain->parent.state_machine.previous_state_id
508 != SCI_BASE_DOMAIN_STATE_STOPPED)
509 {
510 SCIF_SAS_REMOTE_DEVICE_T * remote_device;
511 SCIC_PORT_PROPERTIES_T properties;
512
513 scic_port_get_properties(fw_domain->core_object, &properties);
514
515 // If the device has not yet been added to the domain, then
516 // inform the user that the device is new.
517 remote_device = (SCIF_SAS_REMOTE_DEVICE_T *)
518 scif_domain_get_device_by_sas_address(
519 fw_domain, &properties.remote.sas_address
520 );
521 if (remote_device == SCI_INVALID_HANDLE)
522 {
523 // simply notify the user of the new DA device and be done
524 // with discovery.
525 scif_cb_domain_da_device_added(
526 fw_domain->controller,
527 fw_domain,
528 &properties.remote.sas_address,
529 &properties.remote.protocols
530 );
531 }
532 else
533 {
534 if(properties.remote.protocols.u.bits.smp_target)
535 //kick off the smp discover process.
536 scif_sas_domain_start_smp_discover(fw_domain, remote_device);
537 }
538 }
539 else //entered from STOPPED state.
540 {
541 SCI_ABSTRACT_ELEMENT_T * current_element =
542 sci_abstract_list_get_front(&(fw_domain->remote_device_list) );
543
544 SCIF_SAS_REMOTE_DEVICE_T * fw_device;
545
546 while (current_element != NULL)
547 {
548 fw_device = (SCIF_SAS_REMOTE_DEVICE_T *)
549 sci_abstract_list_get_object(current_element);
550
551 ASSERT(fw_device->parent.state_machine.current_state_id
552 == SCI_BASE_REMOTE_DEVICE_STATE_STOPPED);
553
554 current_element =
555 sci_abstract_list_get_next(current_element);
556
557 SCIF_LOG_INFO((
558 sci_base_object_get_logger(fw_domain),
559 SCIF_LOG_OBJECT_DOMAIN | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
560 "Controller:0x%x Domain:0x%x Device:0x%x removed\n",
561 fw_domain->controller, fw_domain, fw_device
562 ));
563
564 // Notify the framework user of the device removal.
565 scif_cb_domain_device_removed(
566 fw_domain->controller, fw_domain, fw_device
567 );
568 }
569
570 ASSERT(fw_domain->request_list.element_count == 0);
571 ASSERT(sci_abstract_list_size(&fw_domain->remote_device_list) == 0);
572
573 sci_base_state_machine_change_state(
574 &fw_domain->parent.state_machine, SCI_BASE_DOMAIN_STATE_STARTING
575 );
576 }
577 }
578
579 SCI_BASE_STATE_T scif_sas_domain_state_table[SCI_BASE_DOMAIN_MAX_STATES] =
580 {
581 {
582 SCI_BASE_DOMAIN_STATE_INITIAL,
583 scif_sas_domain_initial_state_enter,
584 NULL,
585 },
586 {
587 SCI_BASE_DOMAIN_STATE_STARTING,
588 scif_sas_domain_starting_state_enter,
589 NULL,
590 },
591 {
592 SCI_BASE_DOMAIN_STATE_READY,
593 scif_sas_domain_ready_state_enter,
594 scif_sas_domain_ready_state_exit,
595 },
596 {
597 SCI_BASE_DOMAIN_STATE_STOPPING,
598 scif_sas_domain_stopping_state_enter,
599 NULL,
600 },
601 {
602 SCI_BASE_DOMAIN_STATE_STOPPED,
603 scif_sas_domain_stopped_state_enter,
604 NULL,
605 },
606 {
607 SCI_BASE_DOMAIN_STATE_DISCOVERING,
608 scif_sas_domain_discovering_state_enter,
609 NULL,
610 }
611 };
612
613