Merge tag 'io_uring-5.15-2021-09-11' of git://git.kernel.dk/linux-block
[linux-2.6-microblaze.git] / drivers / pci / endpoint / pci-epf-core.c
index 502eb79..8aea163 100644 (file)
@@ -62,13 +62,20 @@ EXPORT_SYMBOL_GPL(pci_epf_type_add_cfs);
  */
 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);
 }
@@ -83,10 +90,14 @@ EXPORT_SYMBOL_GPL(pci_epf_unbind);
  */
 int pci_epf_bind(struct pci_epf *epf)
 {
+       struct device *dev = &epf->dev;
+       struct pci_epf *epf_vf;
+       u8 func_no, vfunc_no;
+       struct pci_epc *epc;
        int ret;
 
        if (!epf->driver) {
-               dev_WARN(&epf->dev, "epf device not bound to driver\n");
+               dev_WARN(dev, "epf device not bound to driver\n");
                return -EINVAL;
        }
 
@@ -94,13 +105,140 @@ int pci_epf_bind(struct pci_epf *epf)
                return -EAGAIN;
 
        mutex_lock(&epf->lock);
+       list_for_each_entry(epf_vf, &epf->pci_vepf, list) {
+               vfunc_no = epf_vf->vfunc_no;
+
+               if (vfunc_no < 1) {
+                       dev_err(dev, "Invalid virtual function number\n");
+                       ret = -EINVAL;
+                       goto ret;
+               }
+
+               epc = epf->epc;
+               func_no = epf->func_no;
+               if (!IS_ERR_OR_NULL(epc)) {
+                       if (!epc->max_vfs) {
+                               dev_err(dev, "No support for virt function\n");
+                               ret = -EINVAL;
+                               goto ret;
+                       }
+
+                       if (vfunc_no > epc->max_vfs[func_no]) {
+                               dev_err(dev, "PF%d: Exceeds max vfunc number\n",
+                                       func_no);
+                               ret = -EINVAL;
+                               goto ret;
+                       }
+               }
+
+               epc = epf->sec_epc;
+               func_no = epf->sec_epc_func_no;
+               if (!IS_ERR_OR_NULL(epc)) {
+                       if (!epc->max_vfs) {
+                               dev_err(dev, "No support for virt function\n");
+                               ret = -EINVAL;
+                               goto ret;
+                       }
+
+                       if (vfunc_no > epc->max_vfs[func_no]) {
+                               dev_err(dev, "PF%d: Exceeds max vfunc number\n",
+                                       func_no);
+                               ret = -EINVAL;
+                               goto ret;
+                       }
+               }
+
+               epf_vf->func_no = epf->func_no;
+               epf_vf->sec_epc_func_no = epf->sec_epc_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);
+       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;
+
+       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
  * @epf: the EPF device from whom to free the memory
@@ -317,6 +455,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;