sas_port.c 9.3 KB
Newer Older
J
James Bottomley 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
/*
 * Serial Attached SCSI (SAS) Port class
 *
 * Copyright (C) 2005 Adaptec, Inc.  All rights reserved.
 * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
 *
 * This file is licensed under GPLv2.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 */

#include "sas_internal.h"

#include <scsi/scsi_transport.h>
#include <scsi/scsi_transport_sas.h>
#include "../scsi_sas_internal.h"

31 32 33 34 35 36 37 38 39 40 41
static bool phy_is_wideport_member(struct asd_sas_port *port, struct asd_sas_phy *phy)
{
	struct sas_ha_struct *sas_ha = phy->ha;

	if (memcmp(port->attached_sas_addr, phy->attached_sas_addr,
		   SAS_ADDR_SIZE) != 0 || (sas_ha->strict_wide_ports &&
	     memcmp(port->sas_addr, phy->sas_addr, SAS_ADDR_SIZE) != 0))
		return false;
	return true;
}

42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
static void sas_resume_port(struct asd_sas_phy *phy)
{
	struct domain_device *dev;
	struct asd_sas_port *port = phy->port;
	struct sas_ha_struct *sas_ha = phy->ha;
	struct sas_internal *si = to_sas_internal(sas_ha->core.shost->transportt);

	if (si->dft->lldd_port_formed)
		si->dft->lldd_port_formed(phy);

	if (port->suspended)
		port->suspended = 0;
	else {
		/* we only need to handle "link returned" actions once */
		return;
	}

	/* if the port came back:
	 * 1/ presume every device came back
	 * 2/ force the next revalidation to check all expander phys
	 */
	list_for_each_entry(dev, &port->dev_list, dev_list_node) {
		int i, rc;

		rc = sas_notify_lldd_dev_found(dev);
		if (rc) {
			sas_unregister_dev(port, dev);
			continue;
		}

72
		if (dev->dev_type == SAS_EDGE_EXPANDER_DEVICE || dev->dev_type == SAS_FANOUT_EXPANDER_DEVICE) {
73 74 75 76 77 78 79 80 81 82 83 84
			dev->ex_dev.ex_change_count = -1;
			for (i = 0; i < dev->ex_dev.num_phys; i++) {
				struct ex_phy *phy = &dev->ex_dev.ex_phy[i];

				phy->phy_change_count = -1;
			}
		}
	}

	sas_discover_event(port, DISCE_RESUME);
}

J
James Bottomley 已提交
85 86 87 88 89 90 91 92 93 94 95 96 97 98
/**
 * sas_form_port -- add this phy to a port
 * @phy: the phy of interest
 *
 * This function adds this phy to an existing port, thus creating a wide
 * port, or it creates a port and adds the phy to the port.
 */
static void sas_form_port(struct asd_sas_phy *phy)
{
	int i;
	struct sas_ha_struct *sas_ha = phy->ha;
	struct asd_sas_port *port = phy->port;
	struct sas_internal *si =
		to_sas_internal(sas_ha->core.shost->transportt);
99
	unsigned long flags;
J
James Bottomley 已提交
100 101

	if (port) {
102
		if (!phy_is_wideport_member(port, phy))
103
			sas_deform_port(phy, 0);
104 105 106 107 108 109 110 111
		else if (phy->suspended) {
			phy->suspended = 0;
			sas_resume_port(phy);

			/* phy came back, try to cancel the timeout */
			wake_up(&sas_ha->eh_wait_q);
			return;
		} else {
J
James Bottomley 已提交
112
			SAS_DPRINTK("%s: phy%d belongs to port%d already(%d)!\n",
113
				    __func__, phy->id, phy->port->id,
J
James Bottomley 已提交
114 115 116 117 118
				    phy->port->num_phys);
			return;
		}
	}

119
	/* see if the phy should be part of a wide port */
120
	spin_lock_irqsave(&sas_ha->phy_port_lock, flags);
J
James Bottomley 已提交
121 122 123 124
	for (i = 0; i < sas_ha->num_phys; i++) {
		port = sas_ha->sas_port[i];
		spin_lock(&port->phy_list_lock);
		if (*(u64 *) port->sas_addr &&
125
		    phy_is_wideport_member(port, phy) && port->num_phys > 0) {
J
James Bottomley 已提交
126 127 128 129 130 131 132
			/* wide port */
			SAS_DPRINTK("phy%d matched wide port%d\n", phy->id,
				    port->id);
			break;
		}
		spin_unlock(&port->phy_list_lock);
	}
133 134 135 136 137 138 139 140 141 142 143 144 145 146
	/* The phy does not match any existing port, create a new one */
	if (i == sas_ha->num_phys) {
		for (i = 0; i < sas_ha->num_phys; i++) {
			port = sas_ha->sas_port[i];
			spin_lock(&port->phy_list_lock);
			if (*(u64 *)port->sas_addr == 0
				&& port->num_phys == 0) {
				memcpy(port->sas_addr, phy->sas_addr,
					SAS_ADDR_SIZE);
				break;
			}
			spin_unlock(&port->phy_list_lock);
		}
	}
J
James Bottomley 已提交
147 148 149

	if (i >= sas_ha->num_phys) {
		printk(KERN_NOTICE "%s: couldn't find a free port, bug?\n",
150
		       __func__);
151
		spin_unlock_irqrestore(&sas_ha->phy_port_lock, flags);
J
James Bottomley 已提交
152 153 154 155 156
		return;
	}

	/* add the phy to the port */
	list_add_tail(&phy->port_phy_el, &port->phy_list);
157
	sas_phy_set_target(phy, port->port_dev);
J
James Bottomley 已提交
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
	phy->port = port;
	port->num_phys++;
	port->phy_mask |= (1U << phy->id);

	if (*(u64 *)port->attached_sas_addr == 0) {
		port->class = phy->class;
		memcpy(port->attached_sas_addr, phy->attached_sas_addr,
		       SAS_ADDR_SIZE);
		port->iproto = phy->iproto;
		port->tproto = phy->tproto;
		port->oob_mode = phy->oob_mode;
		port->linkrate = phy->linkrate;
	} else
		port->linkrate = max(port->linkrate, phy->linkrate);
	spin_unlock(&port->phy_list_lock);
173
	spin_unlock_irqrestore(&sas_ha->phy_port_lock, flags);
J
James Bottomley 已提交
174 175

	if (!port->port) {
176
		port->port = sas_port_alloc(phy->phy->dev.parent, port->id);
J
James Bottomley 已提交
177 178 179 180 181
		BUG_ON(!port->port);
		sas_port_add(port->port);
	}
	sas_port_add_phy(port->port, phy->phy);

182
	SAS_DPRINTK("%s added to %s, phy_mask:0x%x (%16llx)\n",
183
		    dev_name(&phy->phy->dev), dev_name(&port->port->dev),
184 185 186
		    port->phy_mask,
		    SAS_ADDR(port->attached_sas_addr));

J
James Bottomley 已提交
187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203
	if (port->port_dev)
		port->port_dev->pathways = port->num_phys;

	/* Tell the LLDD about this port formation. */
	if (si->dft->lldd_port_formed)
		si->dft->lldd_port_formed(phy);

	sas_discover_event(phy->port, DISCE_DISCOVER_DOMAIN);
}

/**
 * sas_deform_port -- remove this phy from the port it belongs to
 * @phy: the phy of interest
 *
 * This is called when the physical link to the other phy has been
 * lost (on this phy), in Event thread context. We cannot delay here.
 */
204
void sas_deform_port(struct asd_sas_phy *phy, int gone)
J
James Bottomley 已提交
205 206 207 208 209
{
	struct sas_ha_struct *sas_ha = phy->ha;
	struct asd_sas_port *port = phy->port;
	struct sas_internal *si =
		to_sas_internal(sas_ha->core.shost->transportt);
210
	struct domain_device *dev;
211
	unsigned long flags;
J
James Bottomley 已提交
212 213 214 215

	if (!port)
		return;		  /* done by a phy event */

216 217 218
	dev = port->port_dev;
	if (dev)
		dev->pathways--;
J
James Bottomley 已提交
219 220

	if (port->num_phys == 1) {
221
		sas_unregister_domain_devices(port, gone);
J
James Bottomley 已提交
222 223
		sas_port_delete(port->port);
		port->port = NULL;
224
	} else {
J
James Bottomley 已提交
225
		sas_port_delete_phy(port->port, phy->phy);
226 227
		sas_device_set_phy(dev, port->port);
	}
J
James Bottomley 已提交
228 229 230 231

	if (si->dft->lldd_port_deformed)
		si->dft->lldd_port_deformed(phy);

232
	spin_lock_irqsave(&sas_ha->phy_port_lock, flags);
J
James Bottomley 已提交
233 234 235
	spin_lock(&port->phy_list_lock);

	list_del_init(&phy->port_phy_el);
236
	sas_phy_set_target(phy, NULL);
J
James Bottomley 已提交
237 238 239 240 241 242 243 244 245 246 247 248 249 250 251
	phy->port = NULL;
	port->num_phys--;
	port->phy_mask &= ~(1U << phy->id);

	if (port->num_phys == 0) {
		INIT_LIST_HEAD(&port->phy_list);
		memset(port->sas_addr, 0, SAS_ADDR_SIZE);
		memset(port->attached_sas_addr, 0, SAS_ADDR_SIZE);
		port->class = 0;
		port->iproto = 0;
		port->tproto = 0;
		port->oob_mode = 0;
		port->phy_mask = 0;
	}
	spin_unlock(&port->phy_list_lock);
252
	spin_unlock_irqrestore(&sas_ha->phy_port_lock, flags);
J
James Bottomley 已提交
253 254 255 256 257 258

	return;
}

/* ---------- SAS port events ---------- */

D
David Howells 已提交
259
void sas_porte_bytes_dmaed(struct work_struct *work)
J
James Bottomley 已提交
260
{
261
	struct asd_sas_event *ev = to_asd_sas_event(work);
D
David Howells 已提交
262
	struct asd_sas_phy *phy = ev->phy;
J
James Bottomley 已提交
263

264
	clear_bit(PORTE_BYTES_DMAED, &phy->port_events_pending);
J
James Bottomley 已提交
265 266 267 268

	sas_form_port(phy);
}

D
David Howells 已提交
269
void sas_porte_broadcast_rcvd(struct work_struct *work)
J
James Bottomley 已提交
270
{
271
	struct asd_sas_event *ev = to_asd_sas_event(work);
D
David Howells 已提交
272
	struct asd_sas_phy *phy = ev->phy;
J
James Bottomley 已提交
273 274 275
	unsigned long flags;
	u32 prim;

276
	clear_bit(PORTE_BROADCAST_RCVD, &phy->port_events_pending);
J
James Bottomley 已提交
277 278 279 280 281 282 283 284 285

	spin_lock_irqsave(&phy->sas_prim_lock, flags);
	prim = phy->sas_prim;
	spin_unlock_irqrestore(&phy->sas_prim_lock, flags);

	SAS_DPRINTK("broadcast received: %d\n", prim);
	sas_discover_event(phy->port, DISCE_REVALIDATE_DOMAIN);
}

D
David Howells 已提交
286
void sas_porte_link_reset_err(struct work_struct *work)
J
James Bottomley 已提交
287
{
288
	struct asd_sas_event *ev = to_asd_sas_event(work);
D
David Howells 已提交
289
	struct asd_sas_phy *phy = ev->phy;
J
James Bottomley 已提交
290

291
	clear_bit(PORTE_LINK_RESET_ERR, &phy->port_events_pending);
J
James Bottomley 已提交
292

293
	sas_deform_port(phy, 1);
J
James Bottomley 已提交
294 295
}

D
David Howells 已提交
296
void sas_porte_timer_event(struct work_struct *work)
J
James Bottomley 已提交
297
{
298
	struct asd_sas_event *ev = to_asd_sas_event(work);
D
David Howells 已提交
299
	struct asd_sas_phy *phy = ev->phy;
J
James Bottomley 已提交
300

301
	clear_bit(PORTE_TIMER_EVENT, &phy->port_events_pending);
J
James Bottomley 已提交
302

303
	sas_deform_port(phy, 1);
J
James Bottomley 已提交
304 305
}

D
David Howells 已提交
306
void sas_porte_hard_reset(struct work_struct *work)
J
James Bottomley 已提交
307
{
308
	struct asd_sas_event *ev = to_asd_sas_event(work);
D
David Howells 已提交
309
	struct asd_sas_phy *phy = ev->phy;
J
James Bottomley 已提交
310

311
	clear_bit(PORTE_HARD_RESET, &phy->port_events_pending);
J
James Bottomley 已提交
312

313
	sas_deform_port(phy, 1);
J
James Bottomley 已提交
314 315 316 317 318 319 320
}

/* ---------- SAS port registration ---------- */

static void sas_init_port(struct asd_sas_port *port,
			  struct sas_ha_struct *sas_ha, int i)
{
321
	memset(port, 0, sizeof(*port));
J
James Bottomley 已提交
322 323
	port->id = i;
	INIT_LIST_HEAD(&port->dev_list);
324 325
	INIT_LIST_HEAD(&port->disco_list);
	INIT_LIST_HEAD(&port->destroy_list);
J
James Bottomley 已提交
326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352
	spin_lock_init(&port->phy_list_lock);
	INIT_LIST_HEAD(&port->phy_list);
	port->ha = sas_ha;

	spin_lock_init(&port->dev_list_lock);
}

int sas_register_ports(struct sas_ha_struct *sas_ha)
{
	int i;

	/* initialize the ports and discovery */
	for (i = 0; i < sas_ha->num_phys; i++) {
		struct asd_sas_port *port = sas_ha->sas_port[i];

		sas_init_port(port, sas_ha, i);
		sas_init_disc(&port->disc, port);
	}
	return 0;
}

void sas_unregister_ports(struct sas_ha_struct *sas_ha)
{
	int i;

	for (i = 0; i < sas_ha->num_phys; i++)
		if (sas_ha->sas_phy[i]->port)
353
			sas_deform_port(sas_ha->sas_phy[i], 0);
J
James Bottomley 已提交
354 355

}