root/drivers/scsi/libsas/sas_port.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. phy_is_wideport_member
  2. sas_resume_port
  3. sas_form_port
  4. sas_deform_port
  5. sas_porte_bytes_dmaed
  6. sas_porte_broadcast_rcvd
  7. sas_porte_link_reset_err
  8. sas_porte_timer_event
  9. sas_porte_hard_reset
  10. sas_init_port
  11. sas_register_ports
  12. sas_unregister_ports

   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 
  15 static 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 
  26 static void sas_resume_port(struct asd_sas_phy *phy)
  27 {
  28         struct domain_device *dev;
  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(dev, &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  */
  77 static 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 (%16llx)\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  */
 199 void 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 
 264 void 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 
 272 void 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 
 290 void 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 
 298 void 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 
 306 void 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 
 316 static 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 
 332 int 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 
 346 void 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 
 356 const 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 };

/* [<][>][^][v][top][bottom][index][help] */