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