xref: /freebsd/sys/dev/isci/scil/scif_sas_domain_states.c (revision 0b3105a37d7adcadcb720112fed4dc4e8040be99)
1 /*-
2  * This file is provided under a dual BSD/GPLv2 license.  When using or
3  * redistributing this file, you may do so under either license.
4  *
5  * GPL LICENSE SUMMARY
6  *
7  * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of version 2 of the GNU General Public License as
11  * published by the Free Software Foundation.
12  *
13  * This program is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
21  * The full GNU General Public License is included in this distribution
22  * in the file called LICENSE.GPL.
23  *
24  * BSD LICENSE
25  *
26  * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
27  * All rights reserved.
28  *
29  * Redistribution and use in source and binary forms, with or without
30  * modification, are permitted provided that the following conditions
31  * are met:
32  *
33  *   * Redistributions of source code must retain the above copyright
34  *     notice, this list of conditions and the following disclaimer.
35  *   * Redistributions in binary form must reproduce the above copyright
36  *     notice, this list of conditions and the following disclaimer in
37  *     the documentation and/or other materials provided with the
38  *     distribution.
39  *
40  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
41  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
42  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
43  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
44  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
45  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
46  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
47  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
48  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
49  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
50  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
51  */
52 
53 #include <sys/cdefs.h>
54 __FBSDID("$FreeBSD$");
55 
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  */
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
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  */
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
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
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
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
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
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
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
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