brintos

brintos / linux-shallow public Read only

0
0
Text · 9.5 KiB · e3f2ed9 Raw
372 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Serial Attached SCSI (SAS) Port class4 *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->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 back45	 * 2/ force the next revalidation to check all expander phys46	 */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 70static void sas_form_port_add_phy(struct asd_sas_port *port,71				  struct asd_sas_phy *phy, bool wideport)72{73	list_add_tail(&phy->port_phy_el, &port->phy_list);74	sas_phy_set_target(phy, port->port_dev);75	phy->port = port;76	port->num_phys++;77	port->phy_mask |= (1U << phy->id);78 79	if (wideport)80		pr_debug("phy%d matched wide port%d\n", phy->id,81			 port->id);82	else83		memcpy(port->sas_addr, phy->sas_addr, SAS_ADDR_SIZE);84 85	if (*(u64 *)port->attached_sas_addr == 0) {86		memcpy(port->attached_sas_addr, phy->attached_sas_addr,87		       SAS_ADDR_SIZE);88		port->iproto = phy->iproto;89		port->tproto = phy->tproto;90		port->oob_mode = phy->oob_mode;91		port->linkrate = phy->linkrate;92	} else {93		port->linkrate = max(port->linkrate, phy->linkrate);94	}95}96 97/**98 * sas_form_port - add this phy to a port99 * @phy: the phy of interest100 *101 * This function adds this phy to an existing port, thus creating a wide102 * port, or it creates a port and adds the phy to the port.103 */104static void sas_form_port(struct asd_sas_phy *phy)105{106	int i;107	struct sas_ha_struct *sas_ha = phy->ha;108	struct asd_sas_port *port = phy->port;109	struct domain_device *port_dev = NULL;110	struct sas_internal *si =111		to_sas_internal(sas_ha->shost->transportt);112	unsigned long flags;113 114	if (port) {115		if (!phy_is_wideport_member(port, phy))116			sas_deform_port(phy, 0);117		else if (phy->suspended) {118			phy->suspended = 0;119			sas_resume_port(phy);120 121			/* phy came back, try to cancel the timeout */122			wake_up(&sas_ha->eh_wait_q);123			return;124		} else {125			pr_info("%s: phy%d belongs to port%d already(%d)!\n",126				__func__, phy->id, phy->port->id,127				phy->port->num_phys);128			return;129		}130	}131 132	/* see if the phy should be part of a wide port */133	spin_lock_irqsave(&sas_ha->phy_port_lock, flags);134	for (i = 0; i < sas_ha->num_phys; i++) {135		port = sas_ha->sas_port[i];136		spin_lock(&port->phy_list_lock);137		if (*(u64 *) port->sas_addr &&138		    phy_is_wideport_member(port, phy) && port->num_phys > 0) {139			/* wide port */140			port_dev = port->port_dev;141			sas_form_port_add_phy(port, phy, true);142			spin_unlock(&port->phy_list_lock);143			break;144		}145		spin_unlock(&port->phy_list_lock);146	}147	/* The phy does not match any existing port, create a new one */148	if (i == sas_ha->num_phys) {149		for (i = 0; i < sas_ha->num_phys; i++) {150			port = sas_ha->sas_port[i];151			spin_lock(&port->phy_list_lock);152			if (*(u64 *)port->sas_addr == 0153			    && port->num_phys == 0) {154				port_dev = port->port_dev;155				sas_form_port_add_phy(port, phy, false);156				spin_unlock(&port->phy_list_lock);157				break;158			}159			spin_unlock(&port->phy_list_lock);160		}161 162		if (i >= sas_ha->num_phys) {163			pr_err("%s: couldn't find a free port, bug?\n",164			       __func__);165			spin_unlock_irqrestore(&sas_ha->phy_port_lock, flags);166			return;167		}168	}169	spin_unlock_irqrestore(&sas_ha->phy_port_lock, flags);170 171	if (!port->port) {172		port->port = sas_port_alloc(phy->phy->dev.parent, port->id);173		BUG_ON(!port->port);174		sas_port_add(port->port);175	}176	sas_port_add_phy(port->port, phy->phy);177 178	pr_debug("%s added to %s, phy_mask:0x%x (%016llx)\n",179		 dev_name(&phy->phy->dev), dev_name(&port->port->dev),180		 port->phy_mask,181		 SAS_ADDR(port->attached_sas_addr));182 183	if (port_dev)184		port_dev->pathways = port->num_phys;185 186	/* Tell the LLDD about this port formation. */187	if (si->dft->lldd_port_formed)188		si->dft->lldd_port_formed(phy);189 190	sas_discover_event(phy->port, DISCE_DISCOVER_DOMAIN);191	/* Only insert a revalidate event after initial discovery */192	if (port_dev && dev_is_expander(port_dev->dev_type)) {193		struct expander_device *ex_dev = &port_dev->ex_dev;194 195		ex_dev->ex_change_count = -1;196		sas_discover_event(port, DISCE_REVALIDATE_DOMAIN);197	}198	flush_workqueue(sas_ha->disco_q);199}200 201/**202 * sas_deform_port - remove this phy from the port it belongs to203 * @phy: the phy of interest204 * @gone: whether or not the PHY is gone205 *206 * This is called when the physical link to the other phy has been207 * lost (on this phy), in Event thread context. We cannot delay here.208 */209void sas_deform_port(struct asd_sas_phy *phy, int gone)210{211	struct sas_ha_struct *sas_ha = phy->ha;212	struct asd_sas_port *port = phy->port;213	struct sas_internal *si =214		to_sas_internal(sas_ha->shost->transportt);215	struct domain_device *dev;216	unsigned long flags;217 218	if (!port)219		return;		  /* done by a phy event */220 221	dev = port->port_dev;222	if (dev)223		dev->pathways--;224 225	if (port->num_phys == 1) {226		sas_unregister_domain_devices(port, gone);227		sas_destruct_devices(port);228		sas_port_delete(port->port);229		port->port = NULL;230	} else {231		sas_port_delete_phy(port->port, phy->phy);232		sas_device_set_phy(dev, port->port);233	}234 235	if (si->dft->lldd_port_deformed)236		si->dft->lldd_port_deformed(phy);237 238	spin_lock_irqsave(&sas_ha->phy_port_lock, flags);239	spin_lock(&port->phy_list_lock);240 241	list_del_init(&phy->port_phy_el);242	sas_phy_set_target(phy, NULL);243	phy->port = NULL;244	port->num_phys--;245	port->phy_mask &= ~(1U << phy->id);246 247	if (port->num_phys == 0) {248		INIT_LIST_HEAD(&port->phy_list);249		memset(port->sas_addr, 0, SAS_ADDR_SIZE);250		memset(port->attached_sas_addr, 0, SAS_ADDR_SIZE);251		port->iproto = 0;252		port->tproto = 0;253		port->oob_mode = 0;254		port->phy_mask = 0;255	}256	spin_unlock(&port->phy_list_lock);257	spin_unlock_irqrestore(&sas_ha->phy_port_lock, flags);258 259	/* Only insert revalidate event if the port still has members */260	if (port->port && dev && dev_is_expander(dev->dev_type)) {261		struct expander_device *ex_dev = &dev->ex_dev;262 263		ex_dev->ex_change_count = -1;264		sas_discover_event(port, DISCE_REVALIDATE_DOMAIN);265	}266	flush_workqueue(sas_ha->disco_q);267 268	return;269}270 271/* ---------- SAS port events ---------- */272 273void sas_porte_bytes_dmaed(struct work_struct *work)274{275	struct asd_sas_event *ev = to_asd_sas_event(work);276	struct asd_sas_phy *phy = ev->phy;277 278	sas_form_port(phy);279}280 281void sas_porte_broadcast_rcvd(struct work_struct *work)282{283	struct asd_sas_event *ev = to_asd_sas_event(work);284	struct asd_sas_phy *phy = ev->phy;285	unsigned long flags;286	u32 prim;287 288	spin_lock_irqsave(&phy->sas_prim_lock, flags);289	prim = phy->sas_prim;290	spin_unlock_irqrestore(&phy->sas_prim_lock, flags);291 292	pr_debug("broadcast received: %d\n", prim);293	sas_discover_event(phy->port, DISCE_REVALIDATE_DOMAIN);294 295	if (phy->port)296		flush_workqueue(phy->port->ha->disco_q);297}298 299void sas_porte_link_reset_err(struct work_struct *work)300{301	struct asd_sas_event *ev = to_asd_sas_event(work);302	struct asd_sas_phy *phy = ev->phy;303 304	sas_deform_port(phy, 1);305}306 307void sas_porte_timer_event(struct work_struct *work)308{309	struct asd_sas_event *ev = to_asd_sas_event(work);310	struct asd_sas_phy *phy = ev->phy;311 312	sas_deform_port(phy, 1);313}314 315void sas_porte_hard_reset(struct work_struct *work)316{317	struct asd_sas_event *ev = to_asd_sas_event(work);318	struct asd_sas_phy *phy = ev->phy;319 320	sas_deform_port(phy, 1);321}322 323/* ---------- SAS port registration ---------- */324 325static void sas_init_port(struct asd_sas_port *port,326			  struct sas_ha_struct *sas_ha, int i)327{328	memset(port, 0, sizeof(*port));329	port->id = i;330	INIT_LIST_HEAD(&port->dev_list);331	INIT_LIST_HEAD(&port->disco_list);332	INIT_LIST_HEAD(&port->destroy_list);333	INIT_LIST_HEAD(&port->sas_port_del_list);334	spin_lock_init(&port->phy_list_lock);335	INIT_LIST_HEAD(&port->phy_list);336	port->ha = sas_ha;337 338	spin_lock_init(&port->dev_list_lock);339}340 341int sas_register_ports(struct sas_ha_struct *sas_ha)342{343	int i;344 345	/* initialize the ports and discovery */346	for (i = 0; i < sas_ha->num_phys; i++) {347		struct asd_sas_port *port = sas_ha->sas_port[i];348 349		sas_init_port(port, sas_ha, i);350		sas_init_disc(&port->disc, port);351	}352	return 0;353}354 355void sas_unregister_ports(struct sas_ha_struct *sas_ha)356{357	int i;358 359	for (i = 0; i < sas_ha->num_phys; i++)360		if (sas_ha->sas_phy[i]->port)361			sas_deform_port(sas_ha->sas_phy[i], 0);362 363}364 365const work_func_t sas_port_event_fns[PORT_NUM_EVENTS] = {366	[PORTE_BYTES_DMAED] = sas_porte_bytes_dmaed,367	[PORTE_BROADCAST_RCVD] = sas_porte_broadcast_rcvd,368	[PORTE_LINK_RESET_ERR] = sas_porte_link_reset_err,369	[PORTE_TIMER_EVENT] = sas_porte_timer_event,370	[PORTE_HARD_RESET] = sas_porte_hard_reset,371};372