提交 1cf362e9 编写于 作者: K Kishon Vijay Abraham I 提交者: Lorenzo Pieralisi

PCI: endpoint: Add support to add virtual function in endpoint core

Add support to add virtual function in endpoint core. The virtual
function can only be associated with a physical function instead of a
endpoint controller. Provide APIs to associate a virtual function with
a physical function here.

[weiyongjun1@huawei.com: PCI: endpoint: Fix missing unlock on error in
 pci_epf_add_vepf() - Reported-by: Hulk Robot <hulkci@huawei.com>]

Link: https://lore.kernel.org/r/20210819123343.1951-3-kishon@ti.comSigned-off-by: NKishon Vijay Abraham I <kishon@ti.com>
Signed-off-by: NWei Yongjun <weiyongjun1@huawei.com>
Signed-off-by: NLorenzo Pieralisi <lorenzo.pieralisi@arm.com>
上级 f00bfc64
...@@ -548,7 +548,7 @@ int pci_epc_add_epf(struct pci_epc *epc, struct pci_epf *epf, ...@@ -548,7 +548,7 @@ int pci_epc_add_epf(struct pci_epc *epc, struct pci_epf *epf,
u32 func_no; u32 func_no;
int ret = 0; int ret = 0;
if (IS_ERR_OR_NULL(epc)) if (IS_ERR_OR_NULL(epc) || epf->is_vf)
return -EINVAL; return -EINVAL;
if (type == PRIMARY_INTERFACE && epf->epc) if (type == PRIMARY_INTERFACE && epf->epc)
......
...@@ -62,13 +62,20 @@ EXPORT_SYMBOL_GPL(pci_epf_type_add_cfs); ...@@ -62,13 +62,20 @@ EXPORT_SYMBOL_GPL(pci_epf_type_add_cfs);
*/ */
void pci_epf_unbind(struct pci_epf *epf) void pci_epf_unbind(struct pci_epf *epf)
{ {
struct pci_epf *epf_vf;
if (!epf->driver) { if (!epf->driver) {
dev_WARN(&epf->dev, "epf device not bound to driver\n"); dev_WARN(&epf->dev, "epf device not bound to driver\n");
return; return;
} }
mutex_lock(&epf->lock); mutex_lock(&epf->lock);
epf->driver->ops->unbind(epf); list_for_each_entry(epf_vf, &epf->pci_vepf, list) {
if (epf_vf->is_bound)
epf_vf->driver->ops->unbind(epf_vf);
}
if (epf->is_bound)
epf->driver->ops->unbind(epf);
mutex_unlock(&epf->lock); mutex_unlock(&epf->lock);
module_put(epf->driver->owner); module_put(epf->driver->owner);
} }
...@@ -83,6 +90,7 @@ EXPORT_SYMBOL_GPL(pci_epf_unbind); ...@@ -83,6 +90,7 @@ EXPORT_SYMBOL_GPL(pci_epf_unbind);
*/ */
int pci_epf_bind(struct pci_epf *epf) int pci_epf_bind(struct pci_epf *epf)
{ {
struct pci_epf *epf_vf;
int ret; int ret;
if (!epf->driver) { if (!epf->driver) {
...@@ -94,13 +102,97 @@ int pci_epf_bind(struct pci_epf *epf) ...@@ -94,13 +102,97 @@ int pci_epf_bind(struct pci_epf *epf)
return -EAGAIN; return -EAGAIN;
mutex_lock(&epf->lock); mutex_lock(&epf->lock);
list_for_each_entry(epf_vf, &epf->pci_vepf, list) {
epf_vf->func_no = epf->func_no;
epf_vf->epc = epf->epc;
epf_vf->sec_epc = epf->sec_epc;
ret = epf_vf->driver->ops->bind(epf_vf);
if (ret)
goto ret;
epf_vf->is_bound = true;
}
ret = epf->driver->ops->bind(epf); ret = epf->driver->ops->bind(epf);
if (ret)
goto ret;
epf->is_bound = true;
mutex_unlock(&epf->lock);
return 0;
ret:
mutex_unlock(&epf->lock); mutex_unlock(&epf->lock);
pci_epf_unbind(epf);
return ret; return ret;
} }
EXPORT_SYMBOL_GPL(pci_epf_bind); EXPORT_SYMBOL_GPL(pci_epf_bind);
/**
* pci_epf_add_vepf() - associate virtual EP function to physical EP function
* @epf_pf: the physical EP function to which the virtual EP function should be
* associated
* @epf_vf: the virtual EP function to be added
*
* A physical endpoint function can be associated with multiple virtual
* endpoint functions. Invoke pci_epf_add_epf() to add a virtual PCI endpoint
* function to a physical PCI endpoint function.
*/
int pci_epf_add_vepf(struct pci_epf *epf_pf, struct pci_epf *epf_vf)
{
u32 vfunc_no;
if (IS_ERR_OR_NULL(epf_pf) || IS_ERR_OR_NULL(epf_vf))
return -EINVAL;
if (epf_pf->epc || epf_vf->epc || epf_vf->epf_pf)
return -EBUSY;
if (epf_pf->sec_epc || epf_vf->sec_epc)
return -EBUSY;
mutex_lock(&epf_pf->lock);
vfunc_no = find_first_zero_bit(&epf_pf->vfunction_num_map,
BITS_PER_LONG);
if (vfunc_no >= BITS_PER_LONG) {
mutex_unlock(&epf_pf->lock);
return -EINVAL;
}
set_bit(vfunc_no, &epf_pf->vfunction_num_map);
epf_vf->vfunc_no = vfunc_no;
epf_vf->epf_pf = epf_pf;
epf_vf->is_vf = true;
list_add_tail(&epf_vf->list, &epf_pf->pci_vepf);
mutex_unlock(&epf_pf->lock);
return 0;
}
EXPORT_SYMBOL_GPL(pci_epf_add_vepf);
/**
* pci_epf_remove_vepf() - remove virtual EP function from physical EP function
* @epf_pf: the physical EP function from which the virtual EP function should
* be removed
* @epf_vf: the virtual EP function to be removed
*
* Invoke to remove a virtual endpoint function from the physcial endpoint
* function.
*/
void pci_epf_remove_vepf(struct pci_epf *epf_pf, struct pci_epf *epf_vf)
{
if (IS_ERR_OR_NULL(epf_pf) || IS_ERR_OR_NULL(epf_vf))
return;
mutex_lock(&epf_pf->lock);
clear_bit(epf_vf->vfunc_no, &epf_pf->vfunction_num_map);
list_del(&epf_vf->list);
mutex_unlock(&epf_pf->lock);
}
EXPORT_SYMBOL_GPL(pci_epf_remove_vepf);
/** /**
* pci_epf_free_space() - free the allocated PCI EPF register space * pci_epf_free_space() - free the allocated PCI EPF register space
* @epf: the EPF device from whom to free the memory * @epf: the EPF device from whom to free the memory
...@@ -317,6 +409,10 @@ struct pci_epf *pci_epf_create(const char *name) ...@@ -317,6 +409,10 @@ struct pci_epf *pci_epf_create(const char *name)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
} }
/* VFs are numbered starting with 1. So set BIT(0) by default */
epf->vfunction_num_map = 1;
INIT_LIST_HEAD(&epf->pci_vepf);
dev = &epf->dev; dev = &epf->dev;
device_initialize(dev); device_initialize(dev);
dev->bus = &pci_epf_bus_type; dev->bus = &pci_epf_bus_type;
......
...@@ -121,8 +121,10 @@ struct pci_epf_bar { ...@@ -121,8 +121,10 @@ struct pci_epf_bar {
* @bar: represents the BAR of EPF device * @bar: represents the BAR of EPF device
* @msi_interrupts: number of MSI interrupts required by this function * @msi_interrupts: number of MSI interrupts required by this function
* @msix_interrupts: number of MSI-X interrupts required by this function * @msix_interrupts: number of MSI-X interrupts required by this function
* @func_no: unique function number within this endpoint device * @func_no: unique (physical) function number within this endpoint device
* @vfunc_no: unique virtual function number within a physical function
* @epc: the EPC device to which this EPF device is bound * @epc: the EPC device to which this EPF device is bound
* @epf_pf: the physical EPF device to which this virtual EPF device is bound
* @driver: the EPF driver to which this EPF device is bound * @driver: the EPF driver to which this EPF device is bound
* @list: to add pci_epf as a list of PCI endpoint functions to pci_epc * @list: to add pci_epf as a list of PCI endpoint functions to pci_epc
* @nb: notifier block to notify EPF of any EPC events (like linkup) * @nb: notifier block to notify EPF of any EPC events (like linkup)
...@@ -133,6 +135,10 @@ struct pci_epf_bar { ...@@ -133,6 +135,10 @@ struct pci_epf_bar {
* @sec_epc_bar: represents the BAR of EPF device associated with secondary EPC * @sec_epc_bar: represents the BAR of EPF device associated with secondary EPC
* @sec_epc_func_no: unique (physical) function number within the secondary EPC * @sec_epc_func_no: unique (physical) function number within the secondary EPC
* @group: configfs group associated with the EPF device * @group: configfs group associated with the EPF device
* @is_bound: indicates if bind notification to function driver has been invoked
* @is_vf: true - virtual function, false - physical function
* @vfunction_num_map: bitmap to manage virtual function number
* @pci_vepf: list of virtual endpoint functions associated with this function
*/ */
struct pci_epf { struct pci_epf {
struct device dev; struct device dev;
...@@ -142,8 +148,10 @@ struct pci_epf { ...@@ -142,8 +148,10 @@ struct pci_epf {
u8 msi_interrupts; u8 msi_interrupts;
u16 msix_interrupts; u16 msix_interrupts;
u8 func_no; u8 func_no;
u8 vfunc_no;
struct pci_epc *epc; struct pci_epc *epc;
struct pci_epf *epf_pf;
struct pci_epf_driver *driver; struct pci_epf_driver *driver;
struct list_head list; struct list_head list;
struct notifier_block nb; struct notifier_block nb;
...@@ -156,6 +164,10 @@ struct pci_epf { ...@@ -156,6 +164,10 @@ struct pci_epf {
struct pci_epf_bar sec_epc_bar[6]; struct pci_epf_bar sec_epc_bar[6];
u8 sec_epc_func_no; u8 sec_epc_func_no;
struct config_group *group; struct config_group *group;
unsigned int is_bound;
unsigned int is_vf;
unsigned long vfunction_num_map;
struct list_head pci_vepf;
}; };
/** /**
...@@ -199,4 +211,6 @@ int pci_epf_bind(struct pci_epf *epf); ...@@ -199,4 +211,6 @@ int pci_epf_bind(struct pci_epf *epf);
void pci_epf_unbind(struct pci_epf *epf); void pci_epf_unbind(struct pci_epf *epf);
struct config_group *pci_epf_type_add_cfs(struct pci_epf *epf, struct config_group *pci_epf_type_add_cfs(struct pci_epf *epf,
struct config_group *group); struct config_group *group);
int pci_epf_add_vepf(struct pci_epf *epf_pf, struct pci_epf *epf_vf);
void pci_epf_remove_vepf(struct pci_epf *epf_pf, struct pci_epf *epf_vf);
#endif /* __LINUX_PCI_EPF_H */ #endif /* __LINUX_PCI_EPF_H */
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册