linux/drivers/scsi/libsas/sas_port.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Serial Attached SCSI (SAS) Port class
   4 *
   5 * Copyright (C) 2005 Adaptec, Inc.  All rights reserved.
   6 * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
   7 */
   8
   9#include "sas_internal.h"
  10
  11#include <scsi/scsi_transport.h>
  12#include <scsi/scsi_transport_sas.h>
  13#include "scsi_sas_internal.h"
  14
  15static bool phy_is_wideport_member(struct asd_sas_port *port, struct asd_sas_phy *phy)
  16{
  17        struct sas_ha_struct *sas_ha = phy->ha;
  18
  19        if (memcmp(port->attached_sas_addr, phy->attached_sas_addr,
  20                   SAS_ADDR_SIZE) != 0 || (sas_ha->strict_wide_ports &&
  21             memcmp(port->sas_addr, phy->sas_addr, SAS_ADDR_SIZE) != 0))
  22                return false;
  23        return true;
  24}
  25
  26static void sas_resume_port(struct asd_sas_phy *phy)
  27{
  28        struct domain_device *dev, *n;
  29        struct asd_sas_port *port = phy->port;
  30        struct sas_ha_struct *sas_ha = phy->ha;
  31        struct sas_internal *si = to_sas_internal(sas_ha->core.shost->transportt);
  32
  33        if (si->dft->lldd_port_formed)
  34                si->dft->lldd_port_formed(phy);
  35
  36        if (port->suspended)
  37                port->suspended = 0;
  38        else {
  39                /* we only need to handle "link returned" actions once */
  40                return;
  41        }
  42
  43        /* if the port came back:
  44         * 1/ presume every device came back
  45         * 2/ force the next revalidation to check all expander phys
  46         */
  47        list_for_each_entry_safe(dev, n, &port->dev_list, dev_list_node) {
  48                int i, rc;
  49
  50                rc = sas_notify_lldd_dev_found(dev);
  51                if (rc) {
  52                        sas_unregister_dev(port, dev);
  53                        sas_destruct_devices(port);
  54                        continue;
  55                }
  56
  57                if (dev_is_expander(dev->dev_type)) {
  58                        dev->ex_dev.ex_change_count = -1;
  59                        for (i = 0; i < dev->ex_dev.num_phys; i++) {
  60                                struct ex_phy *phy = &dev->ex_dev.ex_phy[i];
  61
  62                                phy->phy_change_count = -1;
  63                        }
  64                }
  65        }
  66
  67        sas_discover_event(port, DISCE_RESUME);
  68}
  69
  70/**
  71 * sas_form_port - add this phy to a port
  72 * @phy: the phy of interest
  73 *
  74 * This function adds this phy to an existing port, thus creating a wide
  75 * port, or it creates a port and adds the phy to the port.
  76 */
  77static void sas_form_port(struct asd_sas_phy *phy)
  78{
  79        int i;
  80        struct sas_ha_struct *sas_ha = phy->ha;
  81        struct asd_sas_port *port = phy->port;
  82        struct domain_device *port_dev;
  83        struct sas_internal *si =
  84                to_sas_internal(sas_ha->core.shost->transportt);
  85        unsigned long flags;
  86
  87        if (port) {
  88                if (!phy_is_wideport_member(port, phy))
  89                        sas_deform_port(phy, 0);
  90                else if (phy->suspended) {
  91                        phy->suspended = 0;
  92                        sas_resume_port(phy);
  93
  94                        /* phy came back, try to cancel the timeout */
  95                        wake_up(&sas_ha->eh_wait_q);
  96                        return;
  97                } else {
  98                        pr_info("%s: phy%d belongs to port%d already(%d)!\n",
  99                                __func__, phy->id, phy->port->id,
 100                                phy->port->num_phys);
 101                        return;
 102                }
 103        }
 104
 105        /* see if the phy should be part of a wide port */
 106        spin_lock_irqsave(&sas_ha->phy_port_lock, flags);
 107        for (i = 0; i < sas_ha->num_phys; i++) {
 108                port = sas_ha->sas_port[i];
 109                spin_lock(&port->phy_list_lock);
 110                if (*(u64 *) port->sas_addr &&
 111                    phy_is_wideport_member(port, phy) && port->num_phys > 0) {
 112                        /* wide port */
 113                        pr_debug("phy%d matched wide port%d\n", phy->id,
 114                                 port->id);
 115                        break;
 116                }
 117                spin_unlock(&port->phy_list_lock);
 118        }
 119        /* The phy does not match any existing port, create a new one */
 120        if (i == sas_ha->num_phys) {
 121                for (i = 0; i < sas_ha->num_phys; i++) {
 122                        port = sas_ha->sas_port[i];
 123                        spin_lock(&port->phy_list_lock);
 124                        if (*(u64 *)port->sas_addr == 0
 125                                && port->num_phys == 0) {
 126                                memcpy(port->sas_addr, phy->sas_addr,
 127                                        SAS_ADDR_SIZE);
 128                                break;
 129                        }
 130                        spin_unlock(&port->phy_list_lock);
 131                }
 132        }
 133
 134        if (i >= sas_ha->num_phys) {
 135                pr_err("%s: couldn't find a free port, bug?\n", __func__);
 136                spin_unlock_irqrestore(&sas_ha->phy_port_lock, flags);
 137                return;
 138        }
 139
 140        /* add the phy to the port */
 141        port_dev = port->port_dev;
 142        list_add_tail(&phy->port_phy_el, &port->phy_list);
 143        sas_phy_set_target(phy, port_dev);
 144        phy->port = port;
 145        port->num_phys++;
 146        port->phy_mask |= (1U << phy->id);
 147
 148        if (*(u64 *)port->attached_sas_addr == 0) {
 149                port->class = phy->class;
 150                memcpy(port->attached_sas_addr, phy->attached_sas_addr,
 151                       SAS_ADDR_SIZE);
 152                port->iproto = phy->iproto;
 153                port->tproto = phy->tproto;
 154                port->oob_mode = phy->oob_mode;
 155                port->linkrate = phy->linkrate;
 156        } else
 157                port->linkrate = max(port->linkrate, phy->linkrate);
 158        spin_unlock(&port->phy_list_lock);
 159        spin_unlock_irqrestore(&sas_ha->phy_port_lock, flags);
 160
 161        if (!port->port) {
 162                port->port = sas_port_alloc(phy->phy->dev.parent, port->id);
 163                BUG_ON(!port->port);
 164                sas_port_add(port->port);
 165        }
 166        sas_port_add_phy(port->port, phy->phy);
 167
 168        pr_debug("%s added to %s, phy_mask:0x%x (%016llx)\n",
 169                 dev_name(&phy->phy->dev), dev_name(&port->port->dev),
 170                 port->phy_mask,
 171                 SAS_ADDR(port->attached_sas_addr));
 172
 173        if (port_dev)
 174                port_dev->pathways = port->num_phys;
 175
 176        /* Tell the LLDD about this port formation. */
 177        if (si->dft->lldd_port_formed)
 178                si->dft->lldd_port_formed(phy);
 179
 180        sas_discover_event(phy->port, DISCE_DISCOVER_DOMAIN);
 181        /* Only insert a revalidate event after initial discovery */
 182        if (port_dev && dev_is_expander(port_dev->dev_type)) {
 183                struct expander_device *ex_dev = &port_dev->ex_dev;
 184
 185                ex_dev->ex_change_count = -1;
 186                sas_discover_event(port, DISCE_REVALIDATE_DOMAIN);
 187        }
 188        flush_workqueue(sas_ha->disco_q);
 189}
 190
 191/**
 192 * sas_deform_port - remove this phy from the port it belongs to
 193 * @phy: the phy of interest
 194 * @gone: whether or not the PHY is gone
 195 *
 196 * This is called when the physical link to the other phy has been
 197 * lost (on this phy), in Event thread context. We cannot delay here.
 198 */
 199void sas_deform_port(struct asd_sas_phy *phy, int gone)
 200{
 201        struct sas_ha_struct *sas_ha = phy->ha;
 202        struct asd_sas_port *port = phy->port;
 203        struct sas_internal *si =
 204                to_sas_internal(sas_ha->core.shost->transportt);
 205        struct domain_device *dev;
 206        unsigned long flags;
 207
 208        if (!port)
 209                return;           /* done by a phy event */
 210
 211        dev = port->port_dev;
 212        if (dev)
 213                dev->pathways--;
 214
 215        if (port->num_phys == 1) {
 216                sas_unregister_domain_devices(port, gone);
 217                sas_destruct_devices(port);
 218                sas_port_delete(port->port);
 219                port->port = NULL;
 220        } else {
 221                sas_port_delete_phy(port->port, phy->phy);
 222                sas_device_set_phy(dev, port->port);
 223        }
 224
 225        if (si->dft->lldd_port_deformed)
 226                si->dft->lldd_port_deformed(phy);
 227
 228        spin_lock_irqsave(&sas_ha->phy_port_lock, flags);
 229        spin_lock(&port->phy_list_lock);
 230
 231        list_del_init(&phy->port_phy_el);
 232        sas_phy_set_target(phy, NULL);
 233        phy->port = NULL;
 234        port->num_phys--;
 235        port->phy_mask &= ~(1U << phy->id);
 236
 237        if (port->num_phys == 0) {
 238                INIT_LIST_HEAD(&port->phy_list);
 239                memset(port->sas_addr, 0, SAS_ADDR_SIZE);
 240                memset(port->attached_sas_addr, 0, SAS_ADDR_SIZE);
 241                port->class = 0;
 242                port->iproto = 0;
 243                port->tproto = 0;
 244                port->oob_mode = 0;
 245                port->phy_mask = 0;
 246        }
 247        spin_unlock(&port->phy_list_lock);
 248        spin_unlock_irqrestore(&sas_ha->phy_port_lock, flags);
 249
 250        /* Only insert revalidate event if the port still has members */
 251        if (port->port && dev && dev_is_expander(dev->dev_type)) {
 252                struct expander_device *ex_dev = &dev->ex_dev;
 253
 254                ex_dev->ex_change_count = -1;
 255                sas_discover_event(port, DISCE_REVALIDATE_DOMAIN);
 256        }
 257        flush_workqueue(sas_ha->disco_q);
 258
 259        return;
 260}
 261
 262/* ---------- SAS port events ---------- */
 263
 264void sas_porte_bytes_dmaed(struct work_struct *work)
 265{
 266        struct asd_sas_event *ev = to_asd_sas_event(work);
 267        struct asd_sas_phy *phy = ev->phy;
 268
 269        sas_form_port(phy);
 270}
 271
 272void sas_porte_broadcast_rcvd(struct work_struct *work)
 273{
 274        struct asd_sas_event *ev = to_asd_sas_event(work);
 275        struct asd_sas_phy *phy = ev->phy;
 276        unsigned long flags;
 277        u32 prim;
 278
 279        spin_lock_irqsave(&phy->sas_prim_lock, flags);
 280        prim = phy->sas_prim;
 281        spin_unlock_irqrestore(&phy->sas_prim_lock, flags);
 282
 283        pr_debug("broadcast received: %d\n", prim);
 284        sas_discover_event(phy->port, DISCE_REVALIDATE_DOMAIN);
 285
 286        if (phy->port)
 287                flush_workqueue(phy->port->ha->disco_q);
 288}
 289
 290void sas_porte_link_reset_err(struct work_struct *work)
 291{
 292        struct asd_sas_event *ev = to_asd_sas_event(work);
 293        struct asd_sas_phy *phy = ev->phy;
 294
 295        sas_deform_port(phy, 1);
 296}
 297
 298void sas_porte_timer_event(struct work_struct *work)
 299{
 300        struct asd_sas_event *ev = to_asd_sas_event(work);
 301        struct asd_sas_phy *phy = ev->phy;
 302
 303        sas_deform_port(phy, 1);
 304}
 305
 306void sas_porte_hard_reset(struct work_struct *work)
 307{
 308        struct asd_sas_event *ev = to_asd_sas_event(work);
 309        struct asd_sas_phy *phy = ev->phy;
 310
 311        sas_deform_port(phy, 1);
 312}
 313
 314/* ---------- SAS port registration ---------- */
 315
 316static void sas_init_port(struct asd_sas_port *port,
 317                          struct sas_ha_struct *sas_ha, int i)
 318{
 319        memset(port, 0, sizeof(*port));
 320        port->id = i;
 321        INIT_LIST_HEAD(&port->dev_list);
 322        INIT_LIST_HEAD(&port->disco_list);
 323        INIT_LIST_HEAD(&port->destroy_list);
 324        INIT_LIST_HEAD(&port->sas_port_del_list);
 325        spin_lock_init(&port->phy_list_lock);
 326        INIT_LIST_HEAD(&port->phy_list);
 327        port->ha = sas_ha;
 328
 329        spin_lock_init(&port->dev_list_lock);
 330}
 331
 332int sas_register_ports(struct sas_ha_struct *sas_ha)
 333{
 334        int i;
 335
 336        /* initialize the ports and discovery */
 337        for (i = 0; i < sas_ha->num_phys; i++) {
 338                struct asd_sas_port *port = sas_ha->sas_port[i];
 339
 340                sas_init_port(port, sas_ha, i);
 341                sas_init_disc(&port->disc, port);
 342        }
 343        return 0;
 344}
 345
 346void sas_unregister_ports(struct sas_ha_struct *sas_ha)
 347{
 348        int i;
 349
 350        for (i = 0; i < sas_ha->num_phys; i++)
 351                if (sas_ha->sas_phy[i]->port)
 352                        sas_deform_port(sas_ha->sas_phy[i], 0);
 353
 354}
 355
 356const work_func_t sas_port_event_fns[PORT_NUM_EVENTS] = {
 357        [PORTE_BYTES_DMAED] = sas_porte_bytes_dmaed,
 358        [PORTE_BROADCAST_RCVD] = sas_porte_broadcast_rcvd,
 359        [PORTE_LINK_RESET_ERR] = sas_porte_link_reset_err,
 360        [PORTE_TIMER_EVENT] = sas_porte_timer_event,
 361        [PORTE_HARD_RESET] = sas_porte_hard_reset,
 362};
 363