linux/drivers/scsi/libsas/sas_port.c
<<
>>
Prefs
   1/*
   2 * Serial Attached SCSI (SAS) Port class
   3 *
   4 * Copyright (C) 2005 Adaptec, Inc.  All rights reserved.
   5 * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
   6 *
   7 * This file is licensed under GPLv2.
   8 *
   9 * This program is free software; you can redistribute it and/or
  10 * modify it under the terms of the GNU General Public License as
  11 * published by the Free Software Foundation; either version 2 of the
  12 * License, or (at your option) any later version.
  13 *
  14 * This program is distributed in the hope that it will be useful, but
  15 * WITHOUT ANY WARRANTY; without even the implied warranty of
  16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  17 * General Public License for more details.
  18 *
  19 * You should have received a copy of the GNU General Public License
  20 * along with this program; if not, write to the Free Software
  21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  22 *
  23 */
  24
  25#include "sas_internal.h"
  26
  27#include <scsi/scsi_transport.h>
  28#include <scsi/scsi_transport_sas.h>
  29#include "../scsi_sas_internal.h"
  30
  31static bool phy_is_wideport_member(struct asd_sas_port *port, struct asd_sas_phy *phy)
  32{
  33        struct sas_ha_struct *sas_ha = phy->ha;
  34
  35        if (memcmp(port->attached_sas_addr, phy->attached_sas_addr,
  36                   SAS_ADDR_SIZE) != 0 || (sas_ha->strict_wide_ports &&
  37             memcmp(port->sas_addr, phy->sas_addr, SAS_ADDR_SIZE) != 0))
  38                return false;
  39        return true;
  40}
  41
  42/**
  43 * sas_form_port -- add this phy to a port
  44 * @phy: the phy of interest
  45 *
  46 * This function adds this phy to an existing port, thus creating a wide
  47 * port, or it creates a port and adds the phy to the port.
  48 */
  49static void sas_form_port(struct asd_sas_phy *phy)
  50{
  51        int i;
  52        struct sas_ha_struct *sas_ha = phy->ha;
  53        struct asd_sas_port *port = phy->port;
  54        struct sas_internal *si =
  55                to_sas_internal(sas_ha->core.shost->transportt);
  56        unsigned long flags;
  57
  58        if (port) {
  59                if (!phy_is_wideport_member(port, phy))
  60                        sas_deform_port(phy, 0);
  61                else {
  62                        SAS_DPRINTK("%s: phy%d belongs to port%d already(%d)!\n",
  63                                    __func__, phy->id, phy->port->id,
  64                                    phy->port->num_phys);
  65                        return;
  66                }
  67        }
  68
  69        /* see if the phy should be part of a wide port */
  70        spin_lock_irqsave(&sas_ha->phy_port_lock, flags);
  71        for (i = 0; i < sas_ha->num_phys; i++) {
  72                port = sas_ha->sas_port[i];
  73                spin_lock(&port->phy_list_lock);
  74                if (*(u64 *) port->sas_addr &&
  75                    phy_is_wideport_member(port, phy) && port->num_phys > 0) {
  76                        /* wide port */
  77                        SAS_DPRINTK("phy%d matched wide port%d\n", phy->id,
  78                                    port->id);
  79                        break;
  80                }
  81                spin_unlock(&port->phy_list_lock);
  82        }
  83        /* The phy does not match any existing port, create a new one */
  84        if (i == sas_ha->num_phys) {
  85                for (i = 0; i < sas_ha->num_phys; i++) {
  86                        port = sas_ha->sas_port[i];
  87                        spin_lock(&port->phy_list_lock);
  88                        if (*(u64 *)port->sas_addr == 0
  89                                && port->num_phys == 0) {
  90                                memcpy(port->sas_addr, phy->sas_addr,
  91                                        SAS_ADDR_SIZE);
  92                                break;
  93                        }
  94                        spin_unlock(&port->phy_list_lock);
  95                }
  96        }
  97
  98        if (i >= sas_ha->num_phys) {
  99                printk(KERN_NOTICE "%s: couldn't find a free port, bug?\n",
 100                       __func__);
 101                spin_unlock_irqrestore(&sas_ha->phy_port_lock, flags);
 102                return;
 103        }
 104
 105        /* add the phy to the port */
 106        list_add_tail(&phy->port_phy_el, &port->phy_list);
 107        phy->port = port;
 108        port->num_phys++;
 109        port->phy_mask |= (1U << phy->id);
 110
 111        if (!port->phy)
 112                port->phy = phy->phy;
 113
 114        if (*(u64 *)port->attached_sas_addr == 0) {
 115                port->class = phy->class;
 116                memcpy(port->attached_sas_addr, phy->attached_sas_addr,
 117                       SAS_ADDR_SIZE);
 118                port->iproto = phy->iproto;
 119                port->tproto = phy->tproto;
 120                port->oob_mode = phy->oob_mode;
 121                port->linkrate = phy->linkrate;
 122        } else
 123                port->linkrate = max(port->linkrate, phy->linkrate);
 124        spin_unlock(&port->phy_list_lock);
 125        spin_unlock_irqrestore(&sas_ha->phy_port_lock, flags);
 126
 127        if (!port->port) {
 128                port->port = sas_port_alloc(phy->phy->dev.parent, port->id);
 129                BUG_ON(!port->port);
 130                sas_port_add(port->port);
 131        }
 132        sas_port_add_phy(port->port, phy->phy);
 133
 134        SAS_DPRINTK("%s added to %s, phy_mask:0x%x (%16llx)\n",
 135                    dev_name(&phy->phy->dev), dev_name(&port->port->dev),
 136                    port->phy_mask,
 137                    SAS_ADDR(port->attached_sas_addr));
 138
 139        if (port->port_dev)
 140                port->port_dev->pathways = port->num_phys;
 141
 142        /* Tell the LLDD about this port formation. */
 143        if (si->dft->lldd_port_formed)
 144                si->dft->lldd_port_formed(phy);
 145
 146        sas_discover_event(phy->port, DISCE_DISCOVER_DOMAIN);
 147}
 148
 149/**
 150 * sas_deform_port -- remove this phy from the port it belongs to
 151 * @phy: the phy of interest
 152 *
 153 * This is called when the physical link to the other phy has been
 154 * lost (on this phy), in Event thread context. We cannot delay here.
 155 */
 156void sas_deform_port(struct asd_sas_phy *phy, int gone)
 157{
 158        struct sas_ha_struct *sas_ha = phy->ha;
 159        struct asd_sas_port *port = phy->port;
 160        struct sas_internal *si =
 161                to_sas_internal(sas_ha->core.shost->transportt);
 162        struct domain_device *dev;
 163        unsigned long flags;
 164
 165        if (!port)
 166                return;           /* done by a phy event */
 167
 168        dev = port->port_dev;
 169        if (dev)
 170                dev->pathways--;
 171
 172        if (port->num_phys == 1) {
 173                if (dev && gone)
 174                        dev->gone = 1;
 175                sas_unregister_domain_devices(port);
 176                sas_port_delete(port->port);
 177                port->port = NULL;
 178        } else
 179                sas_port_delete_phy(port->port, phy->phy);
 180
 181        if (si->dft->lldd_port_deformed)
 182                si->dft->lldd_port_deformed(phy);
 183
 184        spin_lock_irqsave(&sas_ha->phy_port_lock, flags);
 185        spin_lock(&port->phy_list_lock);
 186
 187        list_del_init(&phy->port_phy_el);
 188        phy->port = NULL;
 189        port->num_phys--;
 190        port->phy_mask &= ~(1U << phy->id);
 191
 192        if (port->num_phys == 0) {
 193                INIT_LIST_HEAD(&port->phy_list);
 194                memset(port->sas_addr, 0, SAS_ADDR_SIZE);
 195                memset(port->attached_sas_addr, 0, SAS_ADDR_SIZE);
 196                port->class = 0;
 197                port->iproto = 0;
 198                port->tproto = 0;
 199                port->oob_mode = 0;
 200                port->phy_mask = 0;
 201        }
 202        spin_unlock(&port->phy_list_lock);
 203        spin_unlock_irqrestore(&sas_ha->phy_port_lock, flags);
 204
 205        return;
 206}
 207
 208/* ---------- SAS port events ---------- */
 209
 210void sas_porte_bytes_dmaed(struct work_struct *work)
 211{
 212        struct asd_sas_event *ev =
 213                container_of(work, struct asd_sas_event, work);
 214        struct asd_sas_phy *phy = ev->phy;
 215
 216        sas_begin_event(PORTE_BYTES_DMAED, &phy->ha->event_lock,
 217                        &phy->port_events_pending);
 218
 219        sas_form_port(phy);
 220}
 221
 222void sas_porte_broadcast_rcvd(struct work_struct *work)
 223{
 224        struct asd_sas_event *ev =
 225                container_of(work, struct asd_sas_event, work);
 226        struct asd_sas_phy *phy = ev->phy;
 227        unsigned long flags;
 228        u32 prim;
 229
 230        sas_begin_event(PORTE_BROADCAST_RCVD, &phy->ha->event_lock,
 231                        &phy->port_events_pending);
 232
 233        spin_lock_irqsave(&phy->sas_prim_lock, flags);
 234        prim = phy->sas_prim;
 235        spin_unlock_irqrestore(&phy->sas_prim_lock, flags);
 236
 237        SAS_DPRINTK("broadcast received: %d\n", prim);
 238        sas_discover_event(phy->port, DISCE_REVALIDATE_DOMAIN);
 239}
 240
 241void sas_porte_link_reset_err(struct work_struct *work)
 242{
 243        struct asd_sas_event *ev =
 244                container_of(work, struct asd_sas_event, work);
 245        struct asd_sas_phy *phy = ev->phy;
 246
 247        sas_begin_event(PORTE_LINK_RESET_ERR, &phy->ha->event_lock,
 248                        &phy->port_events_pending);
 249
 250        sas_deform_port(phy, 1);
 251}
 252
 253void sas_porte_timer_event(struct work_struct *work)
 254{
 255        struct asd_sas_event *ev =
 256                container_of(work, struct asd_sas_event, work);
 257        struct asd_sas_phy *phy = ev->phy;
 258
 259        sas_begin_event(PORTE_TIMER_EVENT, &phy->ha->event_lock,
 260                        &phy->port_events_pending);
 261
 262        sas_deform_port(phy, 1);
 263}
 264
 265void sas_porte_hard_reset(struct work_struct *work)
 266{
 267        struct asd_sas_event *ev =
 268                container_of(work, struct asd_sas_event, work);
 269        struct asd_sas_phy *phy = ev->phy;
 270
 271        sas_begin_event(PORTE_HARD_RESET, &phy->ha->event_lock,
 272                        &phy->port_events_pending);
 273
 274        sas_deform_port(phy, 1);
 275}
 276
 277/* ---------- SAS port registration ---------- */
 278
 279static void sas_init_port(struct asd_sas_port *port,
 280                          struct sas_ha_struct *sas_ha, int i)
 281{
 282        memset(port, 0, sizeof(*port));
 283        port->id = i;
 284        INIT_LIST_HEAD(&port->dev_list);
 285        spin_lock_init(&port->phy_list_lock);
 286        INIT_LIST_HEAD(&port->phy_list);
 287        port->ha = sas_ha;
 288
 289        spin_lock_init(&port->dev_list_lock);
 290}
 291
 292int sas_register_ports(struct sas_ha_struct *sas_ha)
 293{
 294        int i;
 295
 296        /* initialize the ports and discovery */
 297        for (i = 0; i < sas_ha->num_phys; i++) {
 298                struct asd_sas_port *port = sas_ha->sas_port[i];
 299
 300                sas_init_port(port, sas_ha, i);
 301                sas_init_disc(&port->disc, port);
 302        }
 303        return 0;
 304}
 305
 306void sas_unregister_ports(struct sas_ha_struct *sas_ha)
 307{
 308        int i;
 309
 310        for (i = 0; i < sas_ha->num_phys; i++)
 311                if (sas_ha->sas_phy[i]->port)
 312                        sas_deform_port(sas_ha->sas_phy[i], 0);
 313
 314}
 315