// SPDX-License-Identifier: GPL-2.0-only /* * SPDX-FileCopyrightText: Copyright (c) 2022-2024, NVIDIA CORPORATION & AFFILIATES. * All rights reserved. */ #define pr_fmt(fmt) "nvscic2c-pcie: pci-client: " fmt #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "common.h" #include "iova-mngr.h" #include "pci-client.h" /* Anticipate as many users as endpoints in worst-case. */ #define MAX_LINK_EVENT_USERS (MAX_ENDPOINTS) /* * The size of x86/peer edma rx descriptor */ #define EDMA_CH_DESC_SZ (60 * SZ_1K) /* Internal private data-structure as PCI client. */ struct pci_client_t { struct device *dev; struct device *cdev; struct iommu_domain *domain; /* Recv area. Peer's write reflect here. */ struct dma_buff_t *self_mem; /* Send area. PCIe aperture area. Self's Write reach Peer via this.*/ struct pci_aper_t *peer_mem; /* PCI link status memory. mmap() to user-space.*/ atomic_t link_status; struct cpu_buff_t link_status_mem; /* * Lock to guard users getting un/registered and link status change * invocation at the same time. Also, to protect table from concurrent * access. */ struct mutex event_tbl_lock; /* Table of users registered for change in PCI link status. */ struct event_t { /* is taken.*/ atomic_t in_use; /* callback to invoke when change in status is seen.*/ struct callback_ops cb_ops; } event_tbl[MAX_LINK_EVENT_USERS]; /* * Skip reserved iova for use. This area in BAR0 aperture is reserved for * GIC SPI interrupt mechanism. As the allocation, fragmentration * of iova must be identical on both @DRV_MODE_EPF and @DRV_MODE_EPC * skip this area for use in @DRV_MODE_EPC also. We skip by reserving * the iova region and thereby marking it as unusable. */ dma_addr_t edma_ch_desc_iova; bool edma_ch_desc_iova_mapped; void *edma_ch_desc_pva; void *skip_iova; void *skip_meta; void *edma_ch_desc_iova_h; /* * iova-mngr instance for managing the reserved iova region. * application allocated objs and endpoints allocated physical memory * are pinned to this address. */ void *mem_mngr_h; /* eDMA error memory for each endpoint. */ struct cpu_buff_t ep_edma_err_mem[MAX_LINK_EVENT_USERS]; /* * the context of DRV_MODE_EPC/DRV_MODE_EPF */ struct driver_ctx_t *drv_ctx; }; static void free_ep_edma_err_mem(struct pci_client_t *ctx) { uint32_t i = 0U; struct cpu_buff_t *edma_err_mem = NULL; if (ctx != NULL) for (i = 0U; i < MAX_LINK_EVENT_USERS; i++) { edma_err_mem = &ctx->ep_edma_err_mem[i]; kfree(edma_err_mem->pva); edma_err_mem->pva = NULL; } } static int allocate_ep_edma_err_mem(struct pci_client_t *ctx) { int ret = 0; uint32_t i = 0U; struct cpu_buff_t *edma_err_mem = NULL; for (i = 0U; i < MAX_LINK_EVENT_USERS; i++) { edma_err_mem = &ctx->ep_edma_err_mem[i]; edma_err_mem->size = PAGE_ALIGN(sizeof(u32)); edma_err_mem->pva = kzalloc(edma_err_mem->size, GFP_KERNEL); if (WARN_ON(!edma_err_mem->pva)) { ret = -ENOMEM; goto err; } /* physical address to be mmap() in user-space.*/ edma_err_mem->phys_addr = virt_to_phys(edma_err_mem->pva); *(u32 *)edma_err_mem->pva = NVSCIC2C_PCIE_NO_ERROR; } return ret; err: free_ep_edma_err_mem(ctx); return ret; } static void free_link_status_mem(struct pci_client_t *ctx) { if (!ctx || !ctx->link_status_mem.pva) return; kfree(ctx->link_status_mem.pva); ctx->link_status_mem.pva = NULL; } static int allocate_link_status_mem(struct pci_client_t *ctx) { int ret = 0; struct nvscic2c_pcie_link_mem *link_mem = NULL; struct cpu_buff_t *mem = &ctx->link_status_mem; mem->size = PAGE_ALIGN(sizeof(enum nvscic2c_pcie_link)); mem->pva = kzalloc(mem->size, GFP_KERNEL); if (WARN_ON(!mem->pva)) return -ENOMEM; atomic_set(&ctx->link_status, (int)NVSCIC2C_PCIE_LINK_DOWN); link_mem = ((struct nvscic2c_pcie_link_mem *)mem->pva); link_mem->link_status = NVSCIC2C_PCIE_LINK_DOWN; link_mem->aer_err = NVSCIC2C_PCIE_NO_ERROR; /* physical address to be mmap() in user-space.*/ mem->phys_addr = virt_to_phys(mem->pva); return ret; } /* Free edma rx desc_iova that originally mapped from bar0 */ static void free_edma_rx_desc_iova(struct pci_client_t *ctx) { if (!ctx) return; if (ctx->skip_iova) { iova_mngr_block_release(ctx->mem_mngr_h, &ctx->skip_iova); ctx->skip_iova = NULL; } if (ctx->edma_ch_desc_iova_mapped) { iommu_unmap(ctx->domain, ctx->edma_ch_desc_iova, EDMA_CH_DESC_SZ); ctx->edma_ch_desc_iova_mapped = false; } if (ctx->edma_ch_desc_iova) { iova_mngr_block_release(ctx->mem_mngr_h, &ctx->edma_ch_desc_iova_h); ctx->edma_ch_desc_iova_h = NULL; } if (ctx->edma_ch_desc_pva) { free_pages_exact(ctx->edma_ch_desc_pva, EDMA_CH_DESC_SZ); ctx->edma_ch_desc_pva = NULL; } if (ctx->skip_meta) { iova_mngr_block_release(ctx->mem_mngr_h, &ctx->skip_meta); ctx->skip_meta = NULL; } } /* Allocate desc_iova and mapping to bar0 for remote edma, x86-orin c2c only */ static int allocate_edma_rx_desc_iova(struct pci_client_t *ctx) { int ret = 0; int prot = 0; u64 phys_addr = 0; if (WARN_ON(!ctx)) return -EINVAL; /* *bar0 first 128K [-------128k-------] * [-4k-][-60k-][-64k-] *first 4K reserved for meta data communication *next 60k for x86/peer edma rx descriptor *next 64K resered for sys-sw */ ret = iova_mngr_block_reserve(ctx->mem_mngr_h, SZ_4K, NULL, NULL, &ctx->skip_meta); if (ret) { pr_err("Failed to skip the 4K reserved iova region\n"); goto err; } ctx->edma_ch_desc_pva = alloc_pages_exact(EDMA_CH_DESC_SZ, (GFP_KERNEL | __GFP_ZERO)); if (!ctx->edma_ch_desc_pva) { pr_err("Failed to allocate a page with size of 60K\n"); ret = -ENOMEM; goto err; } phys_addr = page_to_phys(virt_to_page(ctx->edma_ch_desc_pva)); if (!phys_addr) { pr_err("Failed to retrieve physical address\n"); ret = -ENOMEM; goto err; } ret = iova_mngr_block_reserve(ctx->mem_mngr_h, EDMA_CH_DESC_SZ, &ctx->edma_ch_desc_iova, NULL, &ctx->edma_ch_desc_iova_h); if (ret) { pr_err("Failed to reserve 60K iova space for remote edma desc\n"); goto err; } prot = (IOMMU_CACHE | IOMMU_READ | IOMMU_WRITE); #if defined(NV_IOMMU_MAP_HAS_GFP_ARG) /* Linux v6.3 */ ret = iommu_map(ctx->domain, ctx->edma_ch_desc_iova, phys_addr, EDMA_CH_DESC_SZ, prot, GFP_KERNEL); #else ret = iommu_map(ctx->domain, ctx->edma_ch_desc_iova, phys_addr, EDMA_CH_DESC_SZ, prot); #endif if (ret) { pr_err("pci client failed to map iova to 60K physical backing\n"); goto err; } ctx->edma_ch_desc_iova_mapped = true; /* bar0+64K - bar0+126K reserved for sys-sw */ ret = iova_mngr_block_reserve(ctx->mem_mngr_h, SZ_64K, NULL, NULL, &ctx->skip_iova); if (ret) { pr_err("Failed to skip the 64K reserved iova region\n"); goto err; } return ret; err: free_edma_rx_desc_iova(ctx); return ret; } int pci_client_init(struct pci_client_params *params, void **pci_client_h) { u32 i = 0; int ret = 0; struct pci_client_t *ctx = NULL; /* should not be an already instantiated pci client context. */ if (WARN_ON(!pci_client_h || *pci_client_h || !params || !params->self_mem || !params->peer_mem || !params->dev)) return -EINVAL; ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); if (WARN_ON(!ctx)) return -ENOMEM; ctx->dev = params->dev; ctx->cdev = params->cdev; ctx->self_mem = params->self_mem; ctx->peer_mem = params->peer_mem; mutex_init(&ctx->event_tbl_lock); /* for link event notifications. */ for (i = 0; i < MAX_LINK_EVENT_USERS; i++) atomic_set(&ctx->event_tbl[i].in_use, 0); ret = allocate_link_status_mem(ctx); if (ret) goto err; ret = allocate_ep_edma_err_mem(ctx); if (ret) goto err; /* * for mapping application objs and endpoint physical memory to remote * visible area. */ ctx->domain = iommu_get_domain_for_dev(ctx->dev); if (WARN_ON(!ctx->domain)) { ret = -ENODEV; pr_err("iommu not available for the pci device\n"); goto err; } /* assumption : PCIe to be fully IO Coherent. Validate the assumption.*/ if (WARN_ON(!dev_is_dma_coherent(ctx->dev))) { ret = -ENODEV; pr_err("io-coherency not enabled for the pci device\n"); goto err; } /* * configure iova manager for inbound/self_mem. Application * supplied objs shall be pinned to this area. */ ret = iova_mngr_init("self_mem", ctx->self_mem->dma_handle, ctx->self_mem->size, &ctx->mem_mngr_h); if (ret) { pr_err("Failed to initialize iova memory manager\n"); goto err; } /* * Skip reserved iova for any use. This area in BAR0 is reserved for * GIC SPI interrupt mechanism. As the allocation, fragmentration * of iova must be identical on both @DRV_MODE_EPF and @DRV_MODE_EPC * skip this area for use in @DRV_MODE_EPC also. We skip by reserving * the iova region and thereby marking it as unusable for others. */ /* remote edma on x86 */ ret = allocate_edma_rx_desc_iova(ctx); if (ret) { pr_err("Failed to skip the reserved iova region\n"); goto err; } *pci_client_h = ctx; return ret; err: pci_client_deinit((void **)&ctx); return ret; } void pci_client_deinit(void **pci_client_h) { struct pci_client_t *ctx = (struct pci_client_t *)(*pci_client_h); if (!ctx) return; free_edma_rx_desc_iova(ctx); if (ctx->mem_mngr_h) { iova_mngr_deinit(&ctx->mem_mngr_h); ctx->mem_mngr_h = NULL; } free_ep_edma_err_mem(ctx); free_link_status_mem(ctx); mutex_destroy(&ctx->event_tbl_lock); kfree(ctx); *pci_client_h = NULL; } int pci_client_alloc_iova(void *pci_client_h, size_t size, u64 *iova, size_t *offset, void **block_h) { struct pci_client_t *ctx = (struct pci_client_t *)pci_client_h; if (WARN_ON(!ctx)) return -EINVAL; return iova_mngr_block_reserve(ctx->mem_mngr_h, size, iova, offset, block_h); } int pci_client_free_iova(void *pci_client_h, void **block_h) { struct pci_client_t *ctx = (struct pci_client_t *)pci_client_h; if (!ctx || !block_h) return -EINVAL; return iova_mngr_block_release(ctx->mem_mngr_h, block_h); } int pci_client_map_addr(void *pci_client_h, u64 to_iova, phys_addr_t paddr, size_t size, int prot) { struct pci_client_t *ctx = (struct pci_client_t *)pci_client_h; if (WARN_ON(!ctx || !to_iova || !paddr || !size)) return -EINVAL; #if defined(NV_IOMMU_MAP_HAS_GFP_ARG) /* Linux v6.3 */ return iommu_map(ctx->domain, to_iova, paddr, size, prot, GFP_KERNEL); #else return iommu_map(ctx->domain, to_iova, paddr, size, prot); #endif } size_t pci_client_unmap_addr(void *pci_client_h, u64 from_iova, size_t size) { struct pci_client_t *ctx = (struct pci_client_t *)pci_client_h; if (!ctx || !ctx->domain) return 0; return iommu_unmap(ctx->domain, from_iova, size); } int pci_client_get_peer_aper(void *pci_client_h, size_t offsetof, size_t size, phys_addr_t *phys_addr) { struct pci_client_t *ctx = (struct pci_client_t *)pci_client_h; if (WARN_ON(!ctx || !size || !phys_addr)) return -EINVAL; if (ctx->peer_mem->size < (offsetof + size)) return -ENOMEM; *phys_addr = ctx->peer_mem->aper + offsetof; return 0; } struct dma_buf_attachment * pci_client_dmabuf_attach(void *pci_client_h, struct dma_buf *dmabuff) { struct pci_client_t *ctx = (struct pci_client_t *)pci_client_h; if (WARN_ON(!ctx || !dmabuff)) return ERR_PTR(-EINVAL); return dma_buf_attach(dmabuff, ctx->cdev); } void pci_client_dmabuf_detach(void *pci_client_h, struct dma_buf *dmabuff, struct dma_buf_attachment *attach) { struct pci_client_t *ctx = (struct pci_client_t *)pci_client_h; if (!ctx || !dmabuff || !attach) return; return dma_buf_detach(dmabuff, attach); } /* Helper function to mmap the PCI link status memory to user-space.*/ int pci_client_mmap_link_mem(void *pci_client_h, struct vm_area_struct *vma) { int ret = 0; struct pci_client_t *ctx = (struct pci_client_t *)pci_client_h; if (WARN_ON(!vma)) return -EINVAL; if (WARN_ON(!ctx || !ctx->link_status_mem.pva)) return -EINVAL; if ((vma->vm_end - vma->vm_start) != ctx->link_status_mem.size) return -EINVAL; vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); ret = remap_pfn_range(vma, vma->vm_start, PFN_DOWN(ctx->link_status_mem.phys_addr), ctx->link_status_mem.size, vma->vm_page_prot); if (ret) pr_err("remap_pfn_range returns error: (%d) for Link mem\n", ret); return ret; } /* Helper function to mmap eDMA error memory to user-space.*/ int pci_client_mmap_edma_err_mem(void *pci_client_h, u32 ep_id, struct vm_area_struct *vma) { int ret = 0; struct cpu_buff_t *edma_err_mem = NULL; struct pci_client_t *ctx = (struct pci_client_t *)pci_client_h; if (WARN_ON(!vma || !ctx)) return -EINVAL; if (WARN_ON(ep_id >= MAX_LINK_EVENT_USERS)) return -EINVAL; edma_err_mem = &ctx->ep_edma_err_mem[ep_id]; if (WARN_ON(!edma_err_mem->pva)) return -EINVAL; if ((vma->vm_end - vma->vm_start) != edma_err_mem->size) return -EINVAL; vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); ret = remap_pfn_range(vma, vma->vm_start, PFN_DOWN(edma_err_mem->phys_addr), edma_err_mem->size, vma->vm_page_prot); if (ret) pr_err("remap_pfn_range returns error: (%d) for eDMA err mem\n", ret); return ret; } /* Update eDMA xfer error code.*/ int pci_client_set_edma_error(void *pci_client_h, u32 ep_id, u32 err) { int ret = 0; struct event_t *event = NULL; struct callback_ops *ops = NULL; struct cpu_buff_t *edma_err_mem = NULL; struct pci_client_t *ctx = (struct pci_client_t *)pci_client_h; if (WARN_ON(!ctx)) return -EINVAL; if (WARN_ON(ep_id >= MAX_LINK_EVENT_USERS || err != NVSCIC2C_PCIE_EDMA_XFER_ERROR)) return -EINVAL; edma_err_mem = &ctx->ep_edma_err_mem[ep_id]; *(u32 *)edma_err_mem->pva = err; arch_invalidate_pmem(edma_err_mem->pva, edma_err_mem->size); mutex_lock(&ctx->event_tbl_lock); /* notify user. */ event = &ctx->event_tbl[ep_id]; if (atomic_read(&event->in_use)) { ops = &event->cb_ops; ops->callback(NULL, ops->ctx); } mutex_unlock(&ctx->event_tbl_lock); return ret; } /* Query PCI link status. */ enum nvscic2c_pcie_link pci_client_query_link_status(void *pci_client_h) { struct pci_client_t *ctx = (struct pci_client_t *)pci_client_h; if (WARN_ON(!ctx)) return NVSCIC2C_PCIE_LINK_DOWN; return atomic_read(&ctx->link_status); } /* * Users/Units can register for PCI link events as received by * @@DRV_MODE_EPF or @DRV_MODE_EPC module sbstraction. */ int pci_client_register_for_link_event(void *pci_client_h, struct callback_ops *ops, u32 id) { int ret = 0; struct event_t *event = NULL; struct pci_client_t *ctx = (struct pci_client_t *)pci_client_h; if (WARN_ON(!ctx)) return -EINVAL; if (WARN_ON((id >= MAX_LINK_EVENT_USERS) || !ops || !ops->callback)) return -EINVAL; mutex_lock(&ctx->event_tbl_lock); event = &ctx->event_tbl[id]; if (atomic_read(&event->in_use)) { ret = -ENOMEM; pr_err("PCIe link event already registered for endpoint: %d\n", id); } else { event->cb_ops.callback = ops->callback; event->cb_ops.ctx = ops->ctx; atomic_set(&event->in_use, 1); } mutex_unlock(&ctx->event_tbl_lock); return ret; } /* Unregister for PCI link events. - teardown only. */ int pci_client_unregister_for_link_event(void *pci_client_h, u32 id) { int ret = 0; struct event_t *event = NULL; struct pci_client_t *ctx = (struct pci_client_t *)pci_client_h; if (WARN_ON(!ctx)) return -EINVAL; if (WARN_ON(id >= MAX_LINK_EVENT_USERS)) return -EINVAL; mutex_lock(&ctx->event_tbl_lock); event = &ctx->event_tbl[id]; if (atomic_read(&event->in_use)) { atomic_set(&event->in_use, 0); event->cb_ops.callback = NULL; event->cb_ops.ctx = NULL; } mutex_unlock(&ctx->event_tbl_lock); return ret; } /* * Update the PCI link status as received in @DRV_MODE_EPF or @DRV_MODE_EPC * module abstraction. Propagate the link status to registered users. */ int pci_client_change_link_status(void *pci_client_h, enum nvscic2c_pcie_link status) { u32 i = 0; int ret = 0; struct event_t *event = NULL; struct callback_ops *ops = NULL; struct nvscic2c_pcie_link_mem *link_mem = NULL; struct pci_client_t *ctx = (struct pci_client_t *)pci_client_h; if (WARN_ON(!ctx)) return -EINVAL; if (WARN_ON(status < NVSCIC2C_PCIE_LINK_DOWN || status > NVSCIC2C_PCIE_LINK_UP)) return -EINVAL; /* * Reflect the status for user-space to see. * For consistent view of same phys_addr by user-space, flush the update * Call is arm64 specific. */ atomic_set(&ctx->link_status, (__s32)status); link_mem = ((struct nvscic2c_pcie_link_mem *)ctx->link_status_mem.pva); link_mem->link_status = status; arch_invalidate_pmem(ctx->link_status_mem.pva, ctx->link_status_mem.size); /* interrupt registered users. */ mutex_lock(&ctx->event_tbl_lock); for (i = 0U; i < MAX_LINK_EVENT_USERS; i++) { event = &ctx->event_tbl[i]; if (atomic_read(&event->in_use)) { ops = &event->cb_ops; ops->callback(NULL, ops->ctx); } } mutex_unlock(&ctx->event_tbl_lock); return ret; } /* Update PCIe error offset with error. */ int pci_client_set_link_aer_error(void *pci_client_h, u32 err) { int ret = 0; struct nvscic2c_pcie_link_mem *link_mem = NULL; struct pci_client_t *ctx = (struct pci_client_t *)pci_client_h; if (WARN_ON(!ctx)) return -EINVAL; if (WARN_ON((err != NVSCIC2C_PCIE_AER_UNCORRECTABLE_FATAL) && (err != NVSCIC2C_PCIE_AER_UNCORRECTABLE_NONFATAL))) return -EINVAL; link_mem = ((struct nvscic2c_pcie_link_mem *)ctx->link_status_mem.pva); /* * There can be more than one type of AER raised before system recovery is done. * Hence update the offset with masked error codes. */ link_mem->aer_err |= err; arch_invalidate_pmem(ctx->link_status_mem.pva, ctx->link_status_mem.size); return ret; } /* * Helper functions to set and get driver context from pci_client t * */ /*Set driver context of DRV_MODE_EPF or DRV_MODE_EPC */ int pci_client_save_driver_ctx(void *pci_client_h, struct driver_ctx_t *drv_ctx) { int ret = 0; struct pci_client_t *pci_client_ctx = (struct pci_client_t *)pci_client_h; if (WARN_ON(!pci_client_ctx)) return -EINVAL; if (WARN_ON(!drv_ctx)) return -EINVAL; pci_client_ctx->drv_ctx = drv_ctx; return ret; } /*Get the driver context of DRV_MODE_EPF or DRV_MODE_EPC */ struct driver_ctx_t * pci_client_get_driver_ctx(void *pci_client_h) { struct pci_client_t *pci_client_ctx = (struct pci_client_t *)pci_client_h; struct driver_ctx_t *drv_ctx = NULL; if (WARN_ON(!pci_client_ctx)) return NULL; drv_ctx = pci_client_ctx->drv_ctx; if (WARN_ON(!drv_ctx)) return NULL; return drv_ctx; } /* get drv mode */ enum drv_mode_t pci_client_get_drv_mode(void *pci_client_h) { struct pci_client_t *pci_client_ctx = (struct pci_client_t *)pci_client_h; struct driver_ctx_t *drv_ctx = NULL; if (WARN_ON(!pci_client_ctx)) return DRV_MODE_INVALID; drv_ctx = pci_client_ctx->drv_ctx; if (WARN_ON(!drv_ctx)) return DRV_MODE_MAXIMUM; return drv_ctx->drv_mode; } /* save the peer cup orin/x86_64 */ int pci_client_save_peer_cpu(void *pci_client_h, enum peer_cpu_t peer_cpu) { int ret = 0; struct pci_client_t *pci_client_ctx = (struct pci_client_t *)pci_client_h; struct driver_ctx_t *drv_ctx = NULL; if (WARN_ON(!pci_client_ctx)) return -EINVAL; drv_ctx = pci_client_ctx->drv_ctx; if (WARN_ON(!drv_ctx)) return -EINVAL; drv_ctx->peer_cpu = peer_cpu; return ret; } /* get the peer cup orin/x86_64 */ enum peer_cpu_t pci_client_get_peer_cpu(void *pci_client_h) { struct pci_client_t *pci_client_ctx = (struct pci_client_t *)pci_client_h; struct driver_ctx_t *drv_ctx = NULL; if (WARN_ON(!pci_client_ctx)) return NVCPU_MAXIMUM; drv_ctx = pci_client_ctx->drv_ctx; if (WARN_ON(!drv_ctx)) return NVCPU_MAXIMUM; return drv_ctx->peer_cpu; } /* get the iova allocated for x86 peer tegra-pcie-emda rx descriptor */ dma_addr_t pci_client_get_edma_rx_desc_iova(void *pci_client_h) { struct pci_client_t *ctx = (struct pci_client_t *)pci_client_h; if (ctx) return ctx->edma_ch_desc_iova; else return 0; } int #if defined(PCI_EPC_IRQ_TYPE_ENUM_PRESENT) /* Dropped from Linux 6.8 */ pci_client_raise_irq(void *pci_client_h, enum pci_epc_irq_type type, u16 num) #else pci_client_raise_irq(void *pci_client_h, int type, u16 num) #endif { int ret = 0; struct pci_client_t *pci_client_ctx = (struct pci_client_t *)pci_client_h; struct driver_ctx_t *drv_ctx = NULL; struct epf_context_t *epf_ctx = NULL; if (WARN_ON(!pci_client_ctx)) return -EINVAL; drv_ctx = pci_client_ctx->drv_ctx; if (WARN_ON(!drv_ctx)) return -EINVAL; epf_ctx = drv_ctx->epf_ctx; if (WARN_ON(!epf_ctx)) return -EINVAL; if (WARN_ON(!epf_ctx->epf)) return -EINVAL; if (WARN_ON(drv_ctx->drv_mode != DRV_MODE_EPF)) return -EINVAL; ret = pci_epc_raise_irq(epf_ctx->epf->epc, epf_ctx->epf->func_no, PCIE_VFNO, type, num); return ret; }