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 
615