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