Add support to add virtual function in endpoint core. Every virtual
function should be associated with a physical function. Provide APIs
to associate a virtual function with a physical function.

Signed-off-by: Kishon Vijay Abraham I <[email protected]>
---
 drivers/pci/endpoint/pci-epc-core.c |  2 +-
 drivers/pci/endpoint/pci-epf-core.c | 92 ++++++++++++++++++++++++++++-
 include/linux/pci-epf.h             | 18 +++++-
 3 files changed, 109 insertions(+), 3 deletions(-)

diff --git a/drivers/pci/endpoint/pci-epc-core.c 
b/drivers/pci/endpoint/pci-epc-core.c
index e5d8d8370686..55417504791b 100644
--- a/drivers/pci/endpoint/pci-epc-core.c
+++ b/drivers/pci/endpoint/pci-epc-core.c
@@ -473,7 +473,7 @@ int pci_epc_add_epf(struct pci_epc *epc, struct pci_epf 
*epf)
 {
        u32 func_no = 0;
 
-       if (epf->epc)
+       if (epf->epc || epf->is_vf)
                return -EBUSY;
 
        if (IS_ERR(epc))
diff --git a/drivers/pci/endpoint/pci-epf-core.c 
b/drivers/pci/endpoint/pci-epf-core.c
index 6e0648991b5c..ce9651ee552a 100644
--- a/drivers/pci/endpoint/pci-epf-core.c
+++ b/drivers/pci/endpoint/pci-epf-core.c
@@ -30,13 +30,20 @@ static const struct device_type pci_epf_type;
  */
 void pci_epf_unbind(struct pci_epf *epf)
 {
+       struct pci_epf *epf_vf;
+
        if (!epf->driver) {
                dev_WARN(&epf->dev, "epf device not bound to driver\n");
                return;
        }
 
        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);
        module_put(epf->driver->owner);
 }
@@ -51,6 +58,7 @@ EXPORT_SYMBOL_GPL(pci_epf_unbind);
  */
 int pci_epf_bind(struct pci_epf *epf)
 {
+       struct pci_epf *epf_vf;
        int ret;
 
        if (!epf->driver) {
@@ -62,13 +70,91 @@ int pci_epf_bind(struct pci_epf *epf)
                return -EAGAIN;
 
        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;
+               ret = epf_vf->driver->ops->bind(epf_vf);
+               if (ret)
+                       goto ret;
+               epf_vf->is_bound = true;
+       }
+
        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);
+       pci_epf_unbind(epf);
 
        return ret;
 }
 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;
+
+       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)
+               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
  * @addr: the virtual address of the PCI EPF register space
@@ -256,6 +342,10 @@ struct pci_epf *pci_epf_create(const char *name)
                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;
        device_initialize(dev);
        dev->bus = &pci_epf_bus_type;
diff --git a/include/linux/pci-epf.h b/include/linux/pci-epf.h
index bcdf4f07bde7..c36e474bc9c7 100644
--- a/include/linux/pci-epf.h
+++ b/include/linux/pci-epf.h
@@ -105,12 +105,20 @@ struct pci_epf_bar {
  * @header: represents standard configuration header
  * @bar: represents the BAR of EPF device
  * @msi_interrupts: number of MSI 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
+ * @epf_pf: the physical EPF device to which this virtual EPF device is bound
+ *   An EPF cannot be associated with both EPC and physical EPF device at the
+ *   same time.
  * @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
  * @nb: notifier block to notify EPF of any EPC events (like linkup)
  * @lock: mutex to protect pci_epf_ops
+ * @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 function associated with this function
  */
 struct pci_epf {
        struct device           dev;
@@ -120,13 +128,19 @@ struct pci_epf {
        u8                      msi_interrupts;
        u16                     msix_interrupts;
        u8                      func_no;
+       u8                      vfunc_no;
 
        struct pci_epc          *epc;
+       struct pci_epf          *epf_pf;
        struct pci_epf_driver   *driver;
        struct list_head        list;
        struct notifier_block   nb;
        /* mutex to protect against concurrent access of pci_epf_ops */
        struct mutex            lock;
+       unsigned int            is_bound;
+       unsigned int            is_vf;
+       unsigned long           vfunction_num_map;
+       struct list_head        pci_vepf;
 };
 
 #define to_pci_epf(epf_dev) container_of((epf_dev), struct pci_epf, dev)
@@ -156,4 +170,6 @@ void *pci_epf_alloc_space(struct pci_epf *epf, size_t size, 
enum pci_barno bar,
 void pci_epf_free_space(struct pci_epf *epf, void *addr, enum pci_barno bar);
 int pci_epf_bind(struct pci_epf *epf);
 void pci_epf_unbind(struct pci_epf *epf);
+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 */
-- 
2.17.1

Reply via email to