xref: /illumos-gate/usr/src/uts/common/io/pciex/pciev.c (revision 20a7641f9918de8574b8b3b47dbe35c4bfc78df1)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*
27  * Copyright (c) 2017, Joyent, Inc.
28  */
29 
30 #include <sys/types.h>
31 #include <sys/ddi.h>
32 #include <sys/dditypes.h>
33 #include <sys/ddifm.h>
34 #include <sys/sunndi.h>
35 #include <sys/devops.h>
36 #include <sys/pcie.h>
37 #include <sys/pci_cap.h>
38 #include <sys/pcie_impl.h>
39 #include <sys/pathname.h>
40 
41 /*
42  * The below 2 global variables are for PCIe IOV Error Handling.  They must only
43  * be accessed during error handling under the protection of a error mutex.
44  */
45 static pcie_domains_t *pcie_faulty_domains = NULL;
46 static boolean_t pcie_faulty_all = B_FALSE;
47 
48 static void pcie_domain_list_destroy(pcie_domains_t *domain_ids);
49 static void pcie_bdf_list_add(pcie_req_id_t bdf,
50     pcie_req_id_list_t **rlist_p);
51 static void pcie_bdf_list_remove(pcie_req_id_t bdf,
52     pcie_req_id_list_t **rlist_p);
53 static void pcie_cache_domain_info(pcie_bus_t *bus_p);
54 static void pcie_uncache_domain_info(pcie_bus_t *bus_p);
55 
56 static void pcie_faulty_list_clear();
57 static void pcie_faulty_list_update(pcie_domains_t *pd,
58     pcie_domains_t **headp);
59 
60 dev_info_t *
61 pcie_find_dip_by_bdf(dev_info_t *rootp, pcie_req_id_t bdf)
62 {
63 	dev_info_t *dip;
64 	pcie_bus_t *bus_p;
65 	int bus_num;
66 
67 	dip = ddi_get_child(rootp);
68 	while (dip) {
69 		bus_p = PCIE_DIP2BUS(dip);
70 		if (bus_p && (bus_p->bus_bdf == bdf))
71 			return (dip);
72 		if (bus_p) {
73 			bus_num = (bdf >> 8) & 0xff;
74 			if ((bus_num >= bus_p->bus_bus_range.lo &&
75 			    bus_num <= bus_p->bus_bus_range.hi) ||
76 			    bus_p->bus_bus_range.hi == 0)
77 				return (pcie_find_dip_by_bdf(dip, bdf));
78 		}
79 		dip = ddi_get_next_sibling(dip);
80 	}
81 	return (NULL);
82 }
83 
84 /*
85  * Add a device bdf to the bdf list.
86  */
87 static void
88 pcie_bdf_list_add(pcie_req_id_t bdf, pcie_req_id_list_t **rlist_p)
89 {
90 	pcie_req_id_list_t *rl = PCIE_ZALLOC(pcie_req_id_list_t);
91 
92 	rl->bdf = bdf;
93 	rl->next = *rlist_p;
94 	*rlist_p = rl;
95 }
96 
97 /*
98  * Remove a bdf from the bdf list.
99  */
100 static void
101 pcie_bdf_list_remove(pcie_req_id_t bdf, pcie_req_id_list_t **rlist_p)
102 {
103 	pcie_req_id_list_t *rl_pre, *rl_next;
104 
105 	rl_pre = *rlist_p;
106 	if (rl_pre->bdf == bdf) {
107 		*rlist_p = rl_pre->next;
108 		kmem_free(rl_pre, sizeof (pcie_req_id_list_t));
109 		return;
110 	}
111 
112 	while (rl_pre->next) {
113 		rl_next = rl_pre->next;
114 		if (rl_next->bdf == bdf) {
115 			rl_pre->next = rl_next->next;
116 			kmem_free(rl_next, sizeof (pcie_req_id_list_t));
117 			break;
118 		} else
119 			rl_pre = rl_next;
120 	}
121 }
122 
123 /*
124  * Cache IOV domain info in all it's parent's pcie_domain_t
125  *
126  * The leaf devices's domain info must be set before calling this function.
127  */
128 void
129 pcie_cache_domain_info(pcie_bus_t *bus_p)
130 {
131 	boolean_t	assigned = PCIE_IS_ASSIGNED(bus_p);
132 	boolean_t	fma_dom = PCIE_ASSIGNED_TO_FMA_DOM(bus_p);
133 	uint_t		domain_id = PCIE_DOMAIN_ID_GET(bus_p);
134 	pcie_req_id_t	bdf = bus_p->bus_bdf;
135 	dev_info_t	*pdip;
136 	pcie_bus_t	*pbus_p;
137 	pcie_domain_t	*pdom_p;
138 
139 	ASSERT(!PCIE_IS_BDG(bus_p));
140 
141 	for (pdip = ddi_get_parent(PCIE_BUS2DIP(bus_p)); PCIE_DIP2BUS(pdip);
142 	    pdip = ddi_get_parent(pdip)) {
143 		pbus_p = PCIE_DIP2BUS(pdip);
144 		pdom_p = PCIE_BUS2DOM(pbus_p);
145 
146 		if (assigned) {
147 			if (domain_id)
148 				PCIE_DOMAIN_LIST_ADD(pbus_p, domain_id);
149 
150 			if (fma_dom)
151 				pdom_p->fmadom_count++;
152 			else {
153 				PCIE_BDF_LIST_ADD(pbus_p, bdf);
154 				pdom_p->nfmadom_count++;
155 			}
156 		} else
157 			pdom_p->rootdom_count++;
158 	}
159 }
160 
161 /*
162  * Clear the leaf device's domain info and uncache IOV domain info in all it's
163  * parent's pcie_domain_t
164  *
165  * The leaf devices's domain info is also cleared by calling this function.
166  */
167 void
168 pcie_uncache_domain_info(pcie_bus_t *bus_p)
169 {
170 	boolean_t	assigned = PCIE_IS_ASSIGNED(bus_p);
171 	boolean_t	fma_dom = PCIE_ASSIGNED_TO_FMA_DOM(bus_p);
172 	uint_t		domain_id = PCIE_DOMAIN_ID_GET(bus_p);
173 	pcie_domain_t	*dom_p = PCIE_BUS2DOM(bus_p), *pdom_p;
174 	pcie_bus_t	*pbus_p;
175 	dev_info_t	*pdip;
176 
177 	ASSERT(!PCIE_IS_BDG(bus_p));
178 	ASSERT((dom_p->fmadom_count + dom_p->nfmadom_count +
179 	    dom_p->rootdom_count) == 1);
180 
181 	/* Clear the domain information */
182 	if (domain_id) {
183 		PCIE_DOMAIN_ID_SET(bus_p, 0);
184 		PCIE_DOMAIN_ID_DECR_REF_COUNT(bus_p);
185 	}
186 
187 	dom_p->fmadom_count = 0;
188 	dom_p->nfmadom_count = 0;
189 	dom_p->rootdom_count = 0;
190 
191 	for (pdip = ddi_get_parent(PCIE_BUS2DIP(bus_p)); PCIE_DIP2BUS(pdip);
192 	    pdip = ddi_get_parent(pdip)) {
193 		pbus_p = PCIE_DIP2BUS(pdip);
194 		pdom_p = PCIE_BUS2DOM(pbus_p);
195 
196 		if (assigned) {
197 			if (domain_id)
198 				PCIE_DOMAIN_LIST_REMOVE(pbus_p, domain_id);
199 
200 			if (fma_dom)
201 				pdom_p->fmadom_count--;
202 			else {
203 				pdom_p->nfmadom_count--;
204 				PCIE_BDF_LIST_REMOVE(pbus_p, bus_p->bus_bdf);
205 			}
206 		} else
207 			pdom_p->rootdom_count--;
208 	}
209 }
210 
211 
212 /*
213  * Initialize private data structure for IOV environments.
214  * o Allocate memory for iov data
215  * o Cache Domain ids.
216  */
217 void
218 pcie_init_dom(dev_info_t *dip)
219 {
220 	pcie_domain_t	*dom_p = PCIE_ZALLOC(pcie_domain_t);
221 	pcie_bus_t	*bus_p = PCIE_DIP2BUS(dip);
222 
223 	PCIE_BUS2DOM(bus_p) = dom_p;
224 
225 	/* Only leaf devices are assignable to IO Domains */
226 	if (PCIE_IS_BDG(bus_p))
227 		return;
228 
229 	/*
230 	 * At the time of init_dom in the root domain a device may or may not
231 	 * have been assigned to an IO Domain.
232 	 *
233 	 * LDOMS: the property "ddi-assigned" will be set for devices that is
234 	 * assignable to an IO domain and unusable in the root domain.  If the
235 	 * property exist assume it has been assigned to a non-fma domain until
236 	 * otherwise notified.  The domain id is unknown on LDOMS.
237 	 *
238 	 * Xen: the "ddi-assigned" property won't be set until Xen store calls
239 	 * pcie_loan_device is called.  In this function this will always look
240 	 * like the device is assigned to the root domain.  Domain ID caching
241 	 * will occur in pcie_loan_device function.
242 	 */
243 	if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
244 	    "ddi-assigned", -1) != -1) {
245 		dom_p->nfmadom_count = 1;
246 
247 		/* Prevent "assigned" device from detaching */
248 		ndi_hold_devi(dip);
249 	} else
250 		dom_p->rootdom_count = 1;
251 	(void) ddi_prop_remove(DDI_DEV_T_NONE, dip, "ddi-assigned");
252 
253 	pcie_cache_domain_info(bus_p);
254 }
255 
256 void
257 pcie_fini_dom(dev_info_t *dip)
258 {
259 	pcie_domain_t	*dom_p = PCIE_DIP2DOM(dip);
260 	pcie_bus_t	*bus_p = PCIE_DIP2BUS(dip);
261 
262 	if (PCIE_IS_BDG(bus_p))
263 		pcie_domain_list_destroy(PCIE_DOMAIN_LIST_GET(bus_p));
264 	else
265 		pcie_uncache_domain_info(bus_p);
266 
267 	kmem_free(dom_p, sizeof (pcie_domain_t));
268 }
269 
270 /*
271  * PCIe Severity:
272  *
273  * PF_ERR_NO_ERROR	: no IOV Action
274  * PF_ERR_CE		: no IOV Action
275  * PF_ERR_NO_PANIC	: contains error telemetry, log domain info
276  * PF_ERR_MATCHED_DEVICE: contains error telemetry, log domain info
277  * PF_ERR_MATCHED_RC	: Error already taken care of, no further IOV Action
278  * PF_ERR_MATCHED_PARENT: Error already taken care of, no further IOV Action
279  * PF_ERR_PANIC		: contains error telemetry, log domain info
280  *
281  * For NO_PANIC, MATCHED_DEVICE and PANIC, IOV wants to look at the affected
282  * devices and find the domains involved.
283  *
284  * If root domain does not own an affected device, IOV EH should change
285  * PF_ERR_PANIC to PF_ERR_MATCH_DOM.
286  */
287 int
288 pciev_eh(pf_data_t *pfd_p, pf_impl_t *impl)
289 {
290 	int severity = pfd_p->pe_severity_flags;
291 	int iov_severity = severity;
292 	pcie_bus_t *a_bus_p;	/* Affected device's pcie_bus_t */
293 	pf_data_t *root_pfd_p = impl->pf_dq_head_p;
294 	pcie_bus_t *root_bus_p;
295 
296 	/*
297 	 * check if all devices under the root device are unassigned.
298 	 * this function should quickly return in non-IOV environment.
299 	 */
300 	ASSERT(root_pfd_p != NULL);
301 	root_bus_p = PCIE_PFD2BUS(root_pfd_p);
302 	if (PCIE_BDG_IS_UNASSIGNED(root_bus_p))
303 		return (severity);
304 
305 	if (severity & PF_ERR_PANIC_DEADLOCK) {
306 		pcie_faulty_all = B_TRUE;
307 
308 	} else if (severity & (PF_ERR_NO_PANIC | PF_ERR_MATCHED_DEVICE |
309 	    PF_ERR_PANIC | PF_ERR_BAD_RESPONSE)) {
310 
311 		uint16_t affected_flag, dev_affected_flags;
312 		uint_t is_panic = 0, is_aff_dev_found = 0;
313 
314 		dev_affected_flags = PFD_AFFECTED_DEV(pfd_p)->pe_affected_flags;
315 		/* adjust affected flags to leverage cached domain ids */
316 		if (dev_affected_flags & PF_AFFECTED_CHILDREN) {
317 			dev_affected_flags |= PF_AFFECTED_SELF;
318 			dev_affected_flags &= ~PF_AFFECTED_CHILDREN;
319 		}
320 
321 		for (affected_flag = 1;
322 		    affected_flag <= PF_MAX_AFFECTED_FLAG;
323 		    affected_flag <<= 1) {
324 			a_bus_p = pciev_get_affected_dev(impl, pfd_p,
325 			    affected_flag, dev_affected_flags);
326 
327 			if (a_bus_p == NULL)
328 				continue;
329 
330 			is_aff_dev_found++;
331 			PFD_AFFECTED_DEV(pfd_p)->pe_affected_bdf =
332 			    a_bus_p->bus_bdf;
333 
334 			/*
335 			 * If a leaf device is assigned to the root domain or if
336 			 * a bridge has children assigned to a root domain
337 			 * panic.
338 			 *
339 			 * If a leaf device or a child of a bridge is assigned
340 			 * to NFMA domain mark it for panic.  If assigned to FMA
341 			 * domain save the domain id.
342 			 */
343 			if (!PCIE_IS_BDG(a_bus_p) &&
344 			    !PCIE_IS_ASSIGNED(a_bus_p)) {
345 				if (severity & PF_ERR_FATAL_FLAGS)
346 					is_panic++;
347 				continue;
348 			}
349 
350 			if (PCIE_BDG_HAS_CHILDREN_ROOT_DOM(a_bus_p)) {
351 				if (severity & PF_ERR_FATAL_FLAGS)
352 					is_panic++;
353 			}
354 
355 			if ((PCIE_ASSIGNED_TO_NFMA_DOM(a_bus_p) ||
356 			    PCIE_BDG_HAS_CHILDREN_NFMA_DOM(a_bus_p)) &&
357 			    (severity & PF_ERR_FATAL_FLAGS)) {
358 				PCIE_BUS2DOM(a_bus_p)->nfma_panic = B_TRUE;
359 				iov_severity |= PF_ERR_MATCH_DOM;
360 			}
361 
362 			if (PCIE_ASSIGNED_TO_FMA_DOM(a_bus_p)) {
363 				pcie_save_domain_id(
364 				    &PCIE_BUS2DOM(a_bus_p)->domain.id);
365 				iov_severity |= PF_ERR_MATCH_DOM;
366 			}
367 
368 			if (PCIE_BDG_HAS_CHILDREN_FMA_DOM(a_bus_p)) {
369 				pcie_save_domain_id(
370 				    PCIE_DOMAIN_LIST_GET(a_bus_p));
371 				iov_severity |= PF_ERR_MATCH_DOM;
372 			}
373 		}
374 
375 		/*
376 		 * Overwrite the severity only if affected device can be
377 		 * identified and root domain does not need to panic.
378 		 */
379 		if ((!is_panic) && is_aff_dev_found) {
380 			iov_severity &= ~PF_ERR_FATAL_FLAGS;
381 		}
382 	}
383 
384 	return (iov_severity);
385 }
386 
387 /* ARGSUSED */
388 void
389 pciev_eh_exit(pf_data_t *root_pfd_p, uint_t intr_type)
390 {
391 	pcie_bus_t *root_bus_p;
392 
393 	/*
394 	 * check if all devices under the root device are unassigned.
395 	 * this function should quickly return in non-IOV environment.
396 	 */
397 	root_bus_p = PCIE_PFD2BUS(root_pfd_p);
398 	if (PCIE_BDG_IS_UNASSIGNED(root_bus_p))
399 		return;
400 
401 	pcie_faulty_list_clear();
402 }
403 
404 pcie_bus_t *
405 pciev_get_affected_dev(pf_impl_t *impl, pf_data_t *pfd_p,
406     uint16_t affected_flag, uint16_t dev_affected_flags)
407 {
408 	pcie_bus_t *bus_p = PCIE_PFD2BUS(pfd_p);
409 	uint16_t flag = affected_flag & dev_affected_flags;
410 	pcie_bus_t *temp_bus_p;
411 	pcie_req_id_t a_bdf;
412 	uint64_t a_addr;
413 	uint16_t cmd;
414 
415 	if (!flag)
416 		return (NULL);
417 
418 	switch (flag) {
419 	case PF_AFFECTED_ROOT:
420 		return (PCIE_DIP2BUS(bus_p->bus_rp_dip));
421 	case PF_AFFECTED_SELF:
422 		return (bus_p);
423 	case PF_AFFECTED_PARENT:
424 		return (PCIE_DIP2BUS(ddi_get_parent(PCIE_BUS2DIP(bus_p))));
425 	case PF_AFFECTED_BDF: /* may only be used for RC */
426 		a_bdf = PFD_AFFECTED_DEV(pfd_p)->pe_affected_bdf;
427 		if (!PCIE_CHECK_VALID_BDF(a_bdf))
428 			return (NULL);
429 
430 		temp_bus_p = pf_find_busp_by_bdf(impl, a_bdf);
431 		return (temp_bus_p);
432 	case PF_AFFECTED_AER:
433 		if (pf_tlp_decode(bus_p, PCIE_ADV_REG(pfd_p)) == DDI_SUCCESS) {
434 			temp_bus_p = pf_find_busp_by_aer(impl, pfd_p);
435 			return (temp_bus_p);
436 		}
437 		break;
438 	case PF_AFFECTED_SAER:
439 		if (pf_pci_decode(pfd_p, &cmd) == DDI_SUCCESS) {
440 			temp_bus_p = pf_find_busp_by_saer(impl, pfd_p);
441 			return (temp_bus_p);
442 		}
443 		break;
444 	case PF_AFFECTED_ADDR: /* ROOT only */
445 		a_addr = PCIE_ROOT_FAULT(pfd_p)->scan_addr;
446 		temp_bus_p = pf_find_busp_by_addr(impl, a_addr);
447 		return (temp_bus_p);
448 	}
449 
450 	return (NULL);
451 }
452 
453 /* type used for pcie_domain_list_find() function */
454 typedef enum {
455 	PCIE_DOM_LIST_TYPE_CACHE = 1,
456 	PCIE_DOM_LIST_TYPE_FAULT = 2
457 } pcie_dom_list_type_t;
458 
459 /*
460  * Check if a domain id is already in the linked list
461  */
462 static pcie_domains_t *
463 pcie_domain_list_find(uint_t domain_id, pcie_domains_t *pd_list_p,
464     pcie_dom_list_type_t type)
465 {
466 	while (pd_list_p) {
467 		if (pd_list_p->domain_id == domain_id)
468 			return (pd_list_p);
469 
470 		if (type == PCIE_DOM_LIST_TYPE_CACHE) {
471 			pd_list_p = pd_list_p->cached_next;
472 		} else if (type == PCIE_DOM_LIST_TYPE_FAULT) {
473 			pd_list_p = pd_list_p->faulty_next;
474 		} else {
475 			return (NULL);
476 		}
477 	}
478 
479 	return (NULL);
480 }
481 
482 /*
483  * Return true if a leaf device is assigned to a domain or a bridge device
484  * has children assigned to the domain
485  */
486 boolean_t
487 pcie_in_domain(pcie_bus_t *bus_p, uint_t domain_id)
488 {
489 	if (PCIE_IS_BDG(bus_p)) {
490 		pcie_domains_t *pd;
491 		pd = pcie_domain_list_find(domain_id,
492 		    PCIE_DOMAIN_LIST_GET(bus_p), PCIE_DOM_LIST_TYPE_CACHE);
493 		if (pd && pd->cached_count)
494 			return (B_TRUE);
495 		return (B_FALSE);
496 	} else {
497 		return (PCIE_DOMAIN_ID_GET(bus_p) == domain_id);
498 	}
499 }
500 
501 /*
502  * Add a domain id to a cached domain id list.
503  * If the domain already exists in the list, increment the reference count.
504  */
505 void
506 pcie_domain_list_add(uint_t domain_id, pcie_domains_t **pd_list_p)
507 {
508 	pcie_domains_t *pd;
509 
510 	pd = pcie_domain_list_find(domain_id, *pd_list_p,
511 	    PCIE_DOM_LIST_TYPE_CACHE);
512 
513 	if (pd == NULL) {
514 		pd = PCIE_ZALLOC(pcie_domains_t);
515 		pd->domain_id = domain_id;
516 		pd->cached_count = 1;
517 		pd->cached_next = *pd_list_p;
518 		*pd_list_p = pd;
519 	} else
520 		pd->cached_count++;
521 }
522 
523 /*
524  * Remove a domain id from a cached domain id list.
525  * Decrement the reference count.
526  */
527 void
528 pcie_domain_list_remove(uint_t domain_id, pcie_domains_t *pd_list_p)
529 {
530 	pcie_domains_t *pd;
531 
532 	pd = pcie_domain_list_find(domain_id, pd_list_p,
533 	    PCIE_DOM_LIST_TYPE_CACHE);
534 
535 	if (pd) {
536 		ASSERT((pd->cached_count)--);
537 	}
538 }
539 
540 /* destroy cached domain id list */
541 static void
542 pcie_domain_list_destroy(pcie_domains_t *domain_ids)
543 {
544 	pcie_domains_t *p = domain_ids;
545 	pcie_domains_t *next;
546 
547 	while (p) {
548 		next = p->cached_next;
549 		kmem_free(p, sizeof (pcie_domains_t));
550 		p = next;
551 	}
552 }
553 
554 static void
555 pcie_faulty_list_update(pcie_domains_t *pd,
556     pcie_domains_t **headp)
557 {
558 	if (pd == NULL)
559 		return;
560 
561 	if (*headp == NULL) {
562 		*headp = pd;
563 		pd->faulty_prev = NULL;
564 		pd->faulty_next = NULL;
565 		pd->faulty_count = 1;
566 	} else {
567 		pd->faulty_next = *headp;
568 		(*headp)->faulty_prev = pd;
569 		pd->faulty_prev = NULL;
570 		pd->faulty_count = 1;
571 		*headp = pd;
572 	}
573 }
574 
575 static void
576 pcie_faulty_list_clear()
577 {
578 	pcie_domains_t *pd = pcie_faulty_domains;
579 	pcie_domains_t *next;
580 
581 	/* unlink all domain structures from the faulty list */
582 	while (pd) {
583 		next = pd->faulty_next;
584 		pd->faulty_prev = NULL;
585 		pd->faulty_next = NULL;
586 		pd->faulty_count = 0;
587 		pd = next;
588 	}
589 	pcie_faulty_domains = NULL;
590 	pcie_faulty_all = B_FALSE;
591 }
592 
593 void
594 pcie_save_domain_id(pcie_domains_t *domain_ids)
595 {
596 	pcie_domains_t *old_list_p, *new_list_p, *pd;
597 
598 	if (pcie_faulty_all)
599 		return;
600 
601 	if (domain_ids == NULL)
602 		return;
603 
604 	old_list_p = pcie_faulty_domains;
605 	for (new_list_p = domain_ids; new_list_p;
606 	    new_list_p = new_list_p->cached_next) {
607 		if (!new_list_p->cached_count)
608 			continue;
609 
610 		/* search domain id in the faulty domain list */
611 		pd = pcie_domain_list_find(new_list_p->domain_id,
612 		    old_list_p, PCIE_DOM_LIST_TYPE_FAULT);
613 		if (pd)
614 			pd->faulty_count++;
615 		else
616 			pcie_faulty_list_update(new_list_p,
617 			    &pcie_faulty_domains);
618 	}
619 }
620