608 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Serial Attached SCSI (SAS) Discover process4 *5 * Copyright (C) 2005 Adaptec, Inc. All rights reserved.6 * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>7 */8 9#include <linux/scatterlist.h>10#include <linux/slab.h>11#include <scsi/scsi_host.h>12#include <scsi/scsi_eh.h>13#include "sas_internal.h"14 15#include <scsi/scsi_transport.h>16#include <scsi/scsi_transport_sas.h>17#include <scsi/sas_ata.h>18#include "scsi_sas_internal.h"19 20/* ---------- Basic task processing for discovery purposes ---------- */21 22void sas_init_dev(struct domain_device *dev)23{24 switch (dev->dev_type) {25 case SAS_END_DEVICE:26 INIT_LIST_HEAD(&dev->ssp_dev.eh_list_node);27 break;28 case SAS_EDGE_EXPANDER_DEVICE:29 case SAS_FANOUT_EXPANDER_DEVICE:30 INIT_LIST_HEAD(&dev->ex_dev.children);31 mutex_init(&dev->ex_dev.cmd_mutex);32 break;33 default:34 break;35 }36}37 38/* ---------- Domain device discovery ---------- */39 40/**41 * sas_get_port_device - Discover devices which caused port creation42 * @port: pointer to struct sas_port of interest43 *44 * Devices directly attached to a HA port, have no parent. This is45 * how we know they are (domain) "root" devices. All other devices46 * do, and should have their "parent" pointer set appropriately as47 * soon as a child device is discovered.48 */49static int sas_get_port_device(struct asd_sas_port *port)50{51 struct asd_sas_phy *phy;52 struct sas_rphy *rphy;53 struct domain_device *dev;54 int rc = -ENODEV;55 56 dev = sas_alloc_device();57 if (!dev)58 return -ENOMEM;59 60 spin_lock_irq(&port->phy_list_lock);61 if (list_empty(&port->phy_list)) {62 spin_unlock_irq(&port->phy_list_lock);63 sas_put_device(dev);64 return -ENODEV;65 }66 phy = container_of(port->phy_list.next, struct asd_sas_phy, port_phy_el);67 spin_lock(&phy->frame_rcvd_lock);68 memcpy(dev->frame_rcvd, phy->frame_rcvd, min(sizeof(dev->frame_rcvd),69 (size_t)phy->frame_rcvd_size));70 spin_unlock(&phy->frame_rcvd_lock);71 spin_unlock_irq(&port->phy_list_lock);72 73 if (dev->frame_rcvd[0] == 0x34 && port->oob_mode == SATA_OOB_MODE) {74 struct dev_to_host_fis *fis =75 (struct dev_to_host_fis *) dev->frame_rcvd;76 if (fis->interrupt_reason == 1 && fis->lbal == 1 &&77 fis->byte_count_low == 0x69 && fis->byte_count_high == 0x9678 && (fis->device & ~0x10) == 0)79 dev->dev_type = SAS_SATA_PM;80 else81 dev->dev_type = SAS_SATA_DEV;82 dev->tproto = SAS_PROTOCOL_SATA;83 } else if (port->oob_mode == SAS_OOB_MODE) {84 struct sas_identify_frame *id =85 (struct sas_identify_frame *) dev->frame_rcvd;86 dev->dev_type = id->dev_type;87 dev->iproto = id->initiator_bits;88 dev->tproto = id->target_bits;89 } else {90 /* If the oob mode is OOB_NOT_CONNECTED, the port is91 * disconnected due to race with PHY down. We cannot92 * continue to discover this port93 */94 sas_put_device(dev);95 pr_warn("Port %016llx is disconnected when discovering\n",96 SAS_ADDR(port->attached_sas_addr));97 return -ENODEV;98 }99 100 sas_init_dev(dev);101 102 dev->port = port;103 switch (dev->dev_type) {104 case SAS_SATA_DEV:105 rc = sas_ata_init(dev);106 if (rc) {107 rphy = NULL;108 break;109 }110 fallthrough;111 case SAS_END_DEVICE:112 rphy = sas_end_device_alloc(port->port);113 break;114 case SAS_EDGE_EXPANDER_DEVICE:115 rphy = sas_expander_alloc(port->port,116 SAS_EDGE_EXPANDER_DEVICE);117 break;118 case SAS_FANOUT_EXPANDER_DEVICE:119 rphy = sas_expander_alloc(port->port,120 SAS_FANOUT_EXPANDER_DEVICE);121 break;122 default:123 pr_warn("ERROR: Unidentified device type %d\n", dev->dev_type);124 rphy = NULL;125 break;126 }127 128 if (!rphy) {129 sas_put_device(dev);130 return rc;131 }132 133 rphy->identify.phy_identifier = phy->phy->identify.phy_identifier;134 memcpy(dev->sas_addr, port->attached_sas_addr, SAS_ADDR_SIZE);135 sas_fill_in_rphy(dev, rphy);136 sas_hash_addr(dev->hashed_sas_addr, dev->sas_addr);137 port->port_dev = dev;138 dev->linkrate = port->linkrate;139 dev->min_linkrate = port->linkrate;140 dev->max_linkrate = port->linkrate;141 dev->pathways = port->num_phys;142 memset(port->disc.fanout_sas_addr, 0, SAS_ADDR_SIZE);143 memset(port->disc.eeds_a, 0, SAS_ADDR_SIZE);144 memset(port->disc.eeds_b, 0, SAS_ADDR_SIZE);145 port->disc.max_level = 0;146 sas_device_set_phy(dev, port->port);147 148 dev->rphy = rphy;149 get_device(&dev->rphy->dev);150 151 if (dev_is_sata(dev) || dev->dev_type == SAS_END_DEVICE)152 list_add_tail(&dev->disco_list_node, &port->disco_list);153 else {154 spin_lock_irq(&port->dev_list_lock);155 list_add_tail(&dev->dev_list_node, &port->dev_list);156 spin_unlock_irq(&port->dev_list_lock);157 }158 159 spin_lock_irq(&port->phy_list_lock);160 list_for_each_entry(phy, &port->phy_list, port_phy_el)161 sas_phy_set_target(phy, dev);162 spin_unlock_irq(&port->phy_list_lock);163 164 return 0;165}166 167/* ---------- Discover and Revalidate ---------- */168 169int sas_notify_lldd_dev_found(struct domain_device *dev)170{171 int res = 0;172 struct sas_ha_struct *sas_ha = dev->port->ha;173 struct Scsi_Host *shost = sas_ha->shost;174 struct sas_internal *i = to_sas_internal(shost->transportt);175 176 if (!i->dft->lldd_dev_found)177 return 0;178 179 res = i->dft->lldd_dev_found(dev);180 if (res) {181 pr_warn("driver on host %s cannot handle device %016llx, error:%d\n",182 dev_name(sas_ha->dev),183 SAS_ADDR(dev->sas_addr), res);184 return res;185 }186 set_bit(SAS_DEV_FOUND, &dev->state);187 kref_get(&dev->kref);188 return 0;189}190 191 192void sas_notify_lldd_dev_gone(struct domain_device *dev)193{194 struct sas_ha_struct *sas_ha = dev->port->ha;195 struct Scsi_Host *shost = sas_ha->shost;196 struct sas_internal *i = to_sas_internal(shost->transportt);197 198 if (!i->dft->lldd_dev_gone)199 return;200 201 if (test_and_clear_bit(SAS_DEV_FOUND, &dev->state)) {202 i->dft->lldd_dev_gone(dev);203 sas_put_device(dev);204 }205}206 207static void sas_probe_devices(struct asd_sas_port *port)208{209 struct domain_device *dev, *n;210 211 /* devices must be domain members before link recovery and probe */212 list_for_each_entry(dev, &port->disco_list, disco_list_node) {213 spin_lock_irq(&port->dev_list_lock);214 list_add_tail(&dev->dev_list_node, &port->dev_list);215 spin_unlock_irq(&port->dev_list_lock);216 }217 218 sas_probe_sata(port);219 220 list_for_each_entry_safe(dev, n, &port->disco_list, disco_list_node) {221 int err;222 223 err = sas_rphy_add(dev->rphy);224 if (err)225 sas_fail_probe(dev, __func__, err);226 else227 list_del_init(&dev->disco_list_node);228 }229}230 231static void sas_suspend_devices(struct work_struct *work)232{233 struct asd_sas_phy *phy;234 struct domain_device *dev;235 struct sas_discovery_event *ev = to_sas_discovery_event(work);236 struct asd_sas_port *port = ev->port;237 struct Scsi_Host *shost = port->ha->shost;238 struct sas_internal *si = to_sas_internal(shost->transportt);239 240 clear_bit(DISCE_SUSPEND, &port->disc.pending);241 242 sas_suspend_sata(port);243 244 /* lldd is free to forget the domain_device across the245 * suspension, we force the issue here to keep the reference246 * counts aligned247 */248 list_for_each_entry(dev, &port->dev_list, dev_list_node)249 sas_notify_lldd_dev_gone(dev);250 251 /* we are suspending, so we know events are disabled and252 * phy_list is not being mutated253 */254 list_for_each_entry(phy, &port->phy_list, port_phy_el) {255 if (si->dft->lldd_port_deformed)256 si->dft->lldd_port_deformed(phy);257 phy->suspended = 1;258 port->suspended = 1;259 }260}261 262static void sas_resume_devices(struct work_struct *work)263{264 struct sas_discovery_event *ev = to_sas_discovery_event(work);265 struct asd_sas_port *port = ev->port;266 267 clear_bit(DISCE_RESUME, &port->disc.pending);268 269 sas_resume_sata(port);270}271 272/**273 * sas_discover_end_dev - discover an end device (SSP, etc)274 * @dev: pointer to domain device of interest275 *276 * See comment in sas_discover_sata().277 */278static int sas_discover_end_dev(struct domain_device *dev)279{280 return sas_notify_lldd_dev_found(dev);281}282 283/* ---------- Device registration and unregistration ---------- */284 285void sas_free_device(struct kref *kref)286{287 struct domain_device *dev = container_of(kref, typeof(*dev), kref);288 289 put_device(&dev->rphy->dev);290 dev->rphy = NULL;291 292 if (dev->parent)293 sas_put_device(dev->parent);294 295 sas_port_put_phy(dev->phy);296 dev->phy = NULL;297 298 /* remove the phys and ports, everything else should be gone */299 if (dev_is_expander(dev->dev_type))300 kfree(dev->ex_dev.ex_phy);301 302 if (dev_is_sata(dev) && dev->sata_dev.ap) {303 ata_tport_delete(dev->sata_dev.ap);304 ata_port_free(dev->sata_dev.ap);305 ata_host_put(dev->sata_dev.ata_host);306 dev->sata_dev.ata_host = NULL;307 dev->sata_dev.ap = NULL;308 }309 310 kfree(dev);311}312 313static void sas_unregister_common_dev(struct asd_sas_port *port, struct domain_device *dev)314{315 struct sas_ha_struct *ha = port->ha;316 317 sas_notify_lldd_dev_gone(dev);318 if (!dev->parent)319 dev->port->port_dev = NULL;320 else321 list_del_init(&dev->siblings);322 323 spin_lock_irq(&port->dev_list_lock);324 list_del_init(&dev->dev_list_node);325 if (dev_is_sata(dev))326 sas_ata_end_eh(dev->sata_dev.ap);327 spin_unlock_irq(&port->dev_list_lock);328 329 spin_lock_irq(&ha->lock);330 if (dev->dev_type == SAS_END_DEVICE &&331 !list_empty(&dev->ssp_dev.eh_list_node)) {332 list_del_init(&dev->ssp_dev.eh_list_node);333 ha->eh_active--;334 }335 spin_unlock_irq(&ha->lock);336 337 sas_put_device(dev);338}339 340void sas_destruct_devices(struct asd_sas_port *port)341{342 struct domain_device *dev, *n;343 344 list_for_each_entry_safe(dev, n, &port->destroy_list, disco_list_node) {345 list_del_init(&dev->disco_list_node);346 347 sas_remove_children(&dev->rphy->dev);348 sas_rphy_delete(dev->rphy);349 sas_unregister_common_dev(port, dev);350 }351}352 353static void sas_destruct_ports(struct asd_sas_port *port)354{355 struct sas_port *sas_port, *p;356 357 list_for_each_entry_safe(sas_port, p, &port->sas_port_del_list, del_list) {358 list_del_init(&sas_port->del_list);359 sas_port_delete(sas_port);360 }361}362 363static bool sas_abort_cmd(struct request *req, void *data)364{365 struct scsi_cmnd *cmd = blk_mq_rq_to_pdu(req);366 struct domain_device *dev = data;367 368 if (dev == cmd_to_domain_dev(cmd))369 blk_abort_request(req);370 return true;371}372 373static void sas_abort_device_scsi_cmds(struct domain_device *dev)374{375 struct sas_ha_struct *sas_ha = dev->port->ha;376 struct Scsi_Host *shost = sas_ha->shost;377 378 if (dev_is_expander(dev->dev_type))379 return;380 381 /*382 * For removed device with active IOs, the user space applications have383 * to spend very long time waiting for the timeout. This is not384 * necessary because a removed device will not return the IOs.385 * Abort the inflight IOs here so that EH can be quickly kicked in.386 */387 blk_mq_tagset_busy_iter(&shost->tag_set, sas_abort_cmd, dev);388}389 390void sas_unregister_dev(struct asd_sas_port *port, struct domain_device *dev)391{392 if (!test_bit(SAS_DEV_DESTROY, &dev->state) &&393 !list_empty(&dev->disco_list_node)) {394 /* this rphy never saw sas_rphy_add */395 list_del_init(&dev->disco_list_node);396 sas_rphy_free(dev->rphy);397 sas_unregister_common_dev(port, dev);398 return;399 }400 401 if (!test_and_set_bit(SAS_DEV_DESTROY, &dev->state)) {402 if (test_bit(SAS_DEV_GONE, &dev->state))403 sas_abort_device_scsi_cmds(dev);404 sas_rphy_unlink(dev->rphy);405 list_move_tail(&dev->disco_list_node, &port->destroy_list);406 }407}408 409void sas_unregister_domain_devices(struct asd_sas_port *port, int gone)410{411 struct domain_device *dev, *n;412 413 list_for_each_entry_safe_reverse(dev, n, &port->dev_list, dev_list_node) {414 if (gone)415 set_bit(SAS_DEV_GONE, &dev->state);416 sas_unregister_dev(port, dev);417 }418 419 list_for_each_entry_safe(dev, n, &port->disco_list, disco_list_node)420 sas_unregister_dev(port, dev);421 422 port->port->rphy = NULL;423 424}425 426void sas_device_set_phy(struct domain_device *dev, struct sas_port *port)427{428 struct sas_ha_struct *ha;429 struct sas_phy *new_phy;430 431 if (!dev)432 return;433 434 ha = dev->port->ha;435 new_phy = sas_port_get_phy(port);436 437 /* pin and record last seen phy */438 spin_lock_irq(&ha->phy_port_lock);439 if (new_phy) {440 sas_port_put_phy(dev->phy);441 dev->phy = new_phy;442 }443 spin_unlock_irq(&ha->phy_port_lock);444}445 446/* ---------- Discovery and Revalidation ---------- */447 448/**449 * sas_discover_domain - discover the domain450 * @work: work structure embedded in port domain device.451 *452 * NOTE: this process _must_ quit (return) as soon as any connection453 * errors are encountered. Connection recovery is done elsewhere.454 * Discover process only interrogates devices in order to discover the455 * domain.456 */457static void sas_discover_domain(struct work_struct *work)458{459 struct domain_device *dev;460 int error = 0;461 struct sas_discovery_event *ev = to_sas_discovery_event(work);462 struct asd_sas_port *port = ev->port;463 464 clear_bit(DISCE_DISCOVER_DOMAIN, &port->disc.pending);465 466 if (port->port_dev)467 return;468 469 error = sas_get_port_device(port);470 if (error)471 return;472 dev = port->port_dev;473 474 pr_debug("DOING DISCOVERY on port %d, pid:%d\n", port->id,475 task_pid_nr(current));476 477 switch (dev->dev_type) {478 case SAS_END_DEVICE:479 error = sas_discover_end_dev(dev);480 break;481 case SAS_EDGE_EXPANDER_DEVICE:482 case SAS_FANOUT_EXPANDER_DEVICE:483 error = sas_discover_root_expander(dev);484 break;485 case SAS_SATA_DEV:486 case SAS_SATA_PM:487 error = sas_discover_sata(dev);488 break;489 default:490 error = -ENXIO;491 pr_err("unhandled device %d\n", dev->dev_type);492 break;493 }494 495 if (error) {496 sas_rphy_free(dev->rphy);497 list_del_init(&dev->disco_list_node);498 spin_lock_irq(&port->dev_list_lock);499 list_del_init(&dev->dev_list_node);500 spin_unlock_irq(&port->dev_list_lock);501 502 sas_put_device(dev);503 port->port_dev = NULL;504 }505 506 sas_probe_devices(port);507 508 pr_debug("DONE DISCOVERY on port %d, pid:%d, result:%d\n", port->id,509 task_pid_nr(current), error);510}511 512static void sas_revalidate_domain(struct work_struct *work)513{514 int res = 0;515 struct sas_discovery_event *ev = to_sas_discovery_event(work);516 struct asd_sas_port *port = ev->port;517 struct sas_ha_struct *ha = port->ha;518 struct domain_device *ddev = port->port_dev;519 520 /* prevent revalidation from finding sata links in recovery */521 mutex_lock(&ha->disco_mutex);522 if (test_bit(SAS_HA_ATA_EH_ACTIVE, &ha->state)) {523 pr_debug("REVALIDATION DEFERRED on port %d, pid:%d\n",524 port->id, task_pid_nr(current));525 goto out;526 }527 528 clear_bit(DISCE_REVALIDATE_DOMAIN, &port->disc.pending);529 530 pr_debug("REVALIDATING DOMAIN on port %d, pid:%d\n", port->id,531 task_pid_nr(current));532 533 if (ddev && dev_is_expander(ddev->dev_type))534 res = sas_ex_revalidate_domain(ddev);535 536 pr_debug("done REVALIDATING DOMAIN on port %d, pid:%d, res 0x%x\n",537 port->id, task_pid_nr(current), res);538 out:539 mutex_unlock(&ha->disco_mutex);540 541 sas_destruct_devices(port);542 sas_destruct_ports(port);543 sas_probe_devices(port);544}545 546/* ---------- Events ---------- */547 548static void sas_chain_work(struct sas_ha_struct *ha, struct sas_work *sw)549{550 /* chained work is not subject to SA_HA_DRAINING or551 * SAS_HA_REGISTERED, because it is either submitted in the552 * workqueue, or known to be submitted from a context that is553 * not racing against draining554 */555 queue_work(ha->disco_q, &sw->work);556}557 558static void sas_chain_event(int event, unsigned long *pending,559 struct sas_work *sw,560 struct sas_ha_struct *ha)561{562 if (!test_and_set_bit(event, pending)) {563 unsigned long flags;564 565 spin_lock_irqsave(&ha->lock, flags);566 sas_chain_work(ha, sw);567 spin_unlock_irqrestore(&ha->lock, flags);568 }569}570 571void sas_discover_event(struct asd_sas_port *port, enum discover_event ev)572{573 struct sas_discovery *disc;574 575 if (!port)576 return;577 disc = &port->disc;578 579 BUG_ON(ev >= DISC_NUM_EVENTS);580 581 sas_chain_event(ev, &disc->pending, &disc->disc_work[ev].work, port->ha);582}583 584/**585 * sas_init_disc - initialize the discovery struct in the port586 * @disc: port discovery structure587 * @port: pointer to struct port588 *589 * Called when the ports are being initialized.590 */591void sas_init_disc(struct sas_discovery *disc, struct asd_sas_port *port)592{593 int i;594 595 static const work_func_t sas_event_fns[DISC_NUM_EVENTS] = {596 [DISCE_DISCOVER_DOMAIN] = sas_discover_domain,597 [DISCE_REVALIDATE_DOMAIN] = sas_revalidate_domain,598 [DISCE_SUSPEND] = sas_suspend_devices,599 [DISCE_RESUME] = sas_resume_devices,600 };601 602 disc->pending = 0;603 for (i = 0; i < DISC_NUM_EVENTS; i++) {604 INIT_SAS_WORK(&disc->disc_work[i].work, sas_event_fns[i]);605 disc->disc_work[i].port = port;606 }607}608