linux/drivers/pci/endpoint/pci-ep-cfs.c

776 lines
19 KiB
C
Raw Normal View History

// SPDX-License-Identifier: GPL-2.0
/*
* configfs to configure the PCI endpoint
*
* Copyright (C) 2017 Texas Instruments
* Author: Kishon Vijay Abraham I <kishon@ti.com>
*/
#include <linux/module.h>
#include <linux/idr.h>
#include <linux/slab.h>
#include <linux/pci-epc.h>
#include <linux/pci-epf.h>
#include <linux/pci-ep-cfs.h>
static DEFINE_IDR(functions_idr);
static DEFINE_MUTEX(functions_mutex);
static struct config_group *functions_group;
static struct config_group *controllers_group;
struct pci_epf_group {
struct config_group group;
struct config_group primary_epc_group;
struct config_group secondary_epc_group;
struct delayed_work cfs_work;
struct pci_epf *epf;
int index;
};
struct pci_epc_group {
struct config_group group;
struct pci_epc *epc;
bool start;
};
static inline struct pci_epf_group *to_pci_epf_group(struct config_item *item)
{
return container_of(to_config_group(item), struct pci_epf_group, group);
}
static inline struct pci_epc_group *to_pci_epc_group(struct config_item *item)
{
return container_of(to_config_group(item), struct pci_epc_group, group);
}
static int pci_secondary_epc_epf_link(struct config_item *epf_item,
struct config_item *epc_item)
{
int ret;
struct pci_epf_group *epf_group = to_pci_epf_group(epf_item->ci_parent);
struct pci_epc_group *epc_group = to_pci_epc_group(epc_item);
struct pci_epc *epc = epc_group->epc;
struct pci_epf *epf = epf_group->epf;
ret = pci_epc_add_epf(epc, epf, SECONDARY_INTERFACE);
if (ret)
return ret;
ret = pci_epf_bind(epf);
if (ret) {
pci_epc_remove_epf(epc, epf, SECONDARY_INTERFACE);
return ret;
}
PCI: endpoint: Remove "core_init_notifier" flag "core_init_notifier" flag is set by the glue drivers requiring refclk from the host to complete the DWC core initialization. Also, those drivers will send a notification to the EPF drivers once the initialization is fully completed using the pci_epc_init_notify() API. Only then, the EPF drivers will start functioning. For the rest of the drivers generating refclk locally, EPF drivers will start functioning post binding with them. EPF drivers rely on the 'core_init_notifier' flag to differentiate between the drivers. Unfortunately, this creates two different flows for the EPF drivers. So to avoid that, let's get rid of the "core_init_notifier" flag and follow a single initialization flow for the EPF drivers. This is done by calling the dw_pcie_ep_init_notify() from all glue drivers after the completion of dw_pcie_ep_init_registers() API. This will allow all the glue drivers to send the notification to the EPF drivers once the initialization is fully completed. Only difference here is that, the drivers requiring refclk from host will send the notification once refclk is received, while others will send it during probe time itself. But this also requires the EPC core driver to deliver the notification after EPF driver bind. Because, the glue driver can send the notification before the EPF drivers bind() and in those cases the EPF drivers will miss the event. To accommodate this, EPC core is now caching the state of the EPC initialization in 'init_complete' flag and pci-ep-cfs driver sends the notification to EPF drivers based on that after each EPF driver bind. Link: https://lore.kernel.org/linux-pci/20240327-pci-dbi-rework-v12-8-082625472414@linaro.org Tested-by: Niklas Cassel <cassel@kernel.org> Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org> Signed-off-by: Krzysztof Wilczyński <kwilczynski@kernel.org> Reviewed-by: Frank Li <Frank.Li@nxp.com> Reviewed-by: Niklas Cassel <cassel@kernel.org>
2024-03-27 12:13:37 +03:00
/* Send any pending EPC initialization complete to the EPF driver */
pci_epc_notify_pending_init(epc, epf);
return 0;
}
static void pci_secondary_epc_epf_unlink(struct config_item *epc_item,
struct config_item *epf_item)
{
struct pci_epf_group *epf_group = to_pci_epf_group(epf_item->ci_parent);
struct pci_epc_group *epc_group = to_pci_epc_group(epc_item);
struct pci_epc *epc;
struct pci_epf *epf;
WARN_ON_ONCE(epc_group->start);
epc = epc_group->epc;
epf = epf_group->epf;
pci_epf_unbind(epf);
pci_epc_remove_epf(epc, epf, SECONDARY_INTERFACE);
}
static struct configfs_item_operations pci_secondary_epc_item_ops = {
.allow_link = pci_secondary_epc_epf_link,
.drop_link = pci_secondary_epc_epf_unlink,
};
static const struct config_item_type pci_secondary_epc_type = {
.ct_item_ops = &pci_secondary_epc_item_ops,
.ct_owner = THIS_MODULE,
};
static struct config_group
*pci_ep_cfs_add_secondary_group(struct pci_epf_group *epf_group)
{
struct config_group *secondary_epc_group;
secondary_epc_group = &epf_group->secondary_epc_group;
config_group_init_type_name(secondary_epc_group, "secondary",
&pci_secondary_epc_type);
configfs_register_group(&epf_group->group, secondary_epc_group);
return secondary_epc_group;
}
static int pci_primary_epc_epf_link(struct config_item *epf_item,
struct config_item *epc_item)
{
int ret;
struct pci_epf_group *epf_group = to_pci_epf_group(epf_item->ci_parent);
struct pci_epc_group *epc_group = to_pci_epc_group(epc_item);
struct pci_epc *epc = epc_group->epc;
struct pci_epf *epf = epf_group->epf;
ret = pci_epc_add_epf(epc, epf, PRIMARY_INTERFACE);
if (ret)
return ret;
ret = pci_epf_bind(epf);
if (ret) {
pci_epc_remove_epf(epc, epf, PRIMARY_INTERFACE);
return ret;
}
PCI: endpoint: Remove "core_init_notifier" flag "core_init_notifier" flag is set by the glue drivers requiring refclk from the host to complete the DWC core initialization. Also, those drivers will send a notification to the EPF drivers once the initialization is fully completed using the pci_epc_init_notify() API. Only then, the EPF drivers will start functioning. For the rest of the drivers generating refclk locally, EPF drivers will start functioning post binding with them. EPF drivers rely on the 'core_init_notifier' flag to differentiate between the drivers. Unfortunately, this creates two different flows for the EPF drivers. So to avoid that, let's get rid of the "core_init_notifier" flag and follow a single initialization flow for the EPF drivers. This is done by calling the dw_pcie_ep_init_notify() from all glue drivers after the completion of dw_pcie_ep_init_registers() API. This will allow all the glue drivers to send the notification to the EPF drivers once the initialization is fully completed. Only difference here is that, the drivers requiring refclk from host will send the notification once refclk is received, while others will send it during probe time itself. But this also requires the EPC core driver to deliver the notification after EPF driver bind. Because, the glue driver can send the notification before the EPF drivers bind() and in those cases the EPF drivers will miss the event. To accommodate this, EPC core is now caching the state of the EPC initialization in 'init_complete' flag and pci-ep-cfs driver sends the notification to EPF drivers based on that after each EPF driver bind. Link: https://lore.kernel.org/linux-pci/20240327-pci-dbi-rework-v12-8-082625472414@linaro.org Tested-by: Niklas Cassel <cassel@kernel.org> Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org> Signed-off-by: Krzysztof Wilczyński <kwilczynski@kernel.org> Reviewed-by: Frank Li <Frank.Li@nxp.com> Reviewed-by: Niklas Cassel <cassel@kernel.org>
2024-03-27 12:13:37 +03:00
/* Send any pending EPC initialization complete to the EPF driver */
pci_epc_notify_pending_init(epc, epf);
return 0;
}
static void pci_primary_epc_epf_unlink(struct config_item *epc_item,
struct config_item *epf_item)
{
struct pci_epf_group *epf_group = to_pci_epf_group(epf_item->ci_parent);
struct pci_epc_group *epc_group = to_pci_epc_group(epc_item);
struct pci_epc *epc;
struct pci_epf *epf;
WARN_ON_ONCE(epc_group->start);
epc = epc_group->epc;
epf = epf_group->epf;
pci_epf_unbind(epf);
pci_epc_remove_epf(epc, epf, PRIMARY_INTERFACE);
}
static struct configfs_item_operations pci_primary_epc_item_ops = {
.allow_link = pci_primary_epc_epf_link,
.drop_link = pci_primary_epc_epf_unlink,
};
static const struct config_item_type pci_primary_epc_type = {
.ct_item_ops = &pci_primary_epc_item_ops,
.ct_owner = THIS_MODULE,
};
static struct config_group
*pci_ep_cfs_add_primary_group(struct pci_epf_group *epf_group)
{
struct config_group *primary_epc_group = &epf_group->primary_epc_group;
config_group_init_type_name(primary_epc_group, "primary",
&pci_primary_epc_type);
configfs_register_group(&epf_group->group, primary_epc_group);
return primary_epc_group;
}
static ssize_t pci_epc_start_store(struct config_item *item, const char *page,
size_t len)
{
int ret;
bool start;
struct pci_epc *epc;
struct pci_epc_group *epc_group = to_pci_epc_group(item);
epc = epc_group->epc;
if (kstrtobool(page, &start) < 0)
return -EINVAL;
if (start == epc_group->start)
return -EALREADY;
if (!start) {
pci_epc_stop(epc);
epc_group->start = 0;
return len;
}
ret = pci_epc_start(epc);
if (ret) {
dev_err(&epc->dev, "failed to start endpoint controller\n");
return -EINVAL;
}
epc_group->start = start;
return len;
}
static ssize_t pci_epc_start_show(struct config_item *item, char *page)
{
return sysfs_emit(page, "%d\n", to_pci_epc_group(item)->start);
}
CONFIGFS_ATTR(pci_epc_, start);
static struct configfs_attribute *pci_epc_attrs[] = {
&pci_epc_attr_start,
NULL,
};
static int pci_epc_epf_link(struct config_item *epc_item,
struct config_item *epf_item)
{
int ret;
struct pci_epf_group *epf_group = to_pci_epf_group(epf_item);
struct pci_epc_group *epc_group = to_pci_epc_group(epc_item);
struct pci_epc *epc = epc_group->epc;
struct pci_epf *epf = epf_group->epf;
ret = pci_epc_add_epf(epc, epf, PRIMARY_INTERFACE);
if (ret)
return ret;
ret = pci_epf_bind(epf);
if (ret) {
pci_epc_remove_epf(epc, epf, PRIMARY_INTERFACE);
return ret;
}
PCI: endpoint: Remove "core_init_notifier" flag "core_init_notifier" flag is set by the glue drivers requiring refclk from the host to complete the DWC core initialization. Also, those drivers will send a notification to the EPF drivers once the initialization is fully completed using the pci_epc_init_notify() API. Only then, the EPF drivers will start functioning. For the rest of the drivers generating refclk locally, EPF drivers will start functioning post binding with them. EPF drivers rely on the 'core_init_notifier' flag to differentiate between the drivers. Unfortunately, this creates two different flows for the EPF drivers. So to avoid that, let's get rid of the "core_init_notifier" flag and follow a single initialization flow for the EPF drivers. This is done by calling the dw_pcie_ep_init_notify() from all glue drivers after the completion of dw_pcie_ep_init_registers() API. This will allow all the glue drivers to send the notification to the EPF drivers once the initialization is fully completed. Only difference here is that, the drivers requiring refclk from host will send the notification once refclk is received, while others will send it during probe time itself. But this also requires the EPC core driver to deliver the notification after EPF driver bind. Because, the glue driver can send the notification before the EPF drivers bind() and in those cases the EPF drivers will miss the event. To accommodate this, EPC core is now caching the state of the EPC initialization in 'init_complete' flag and pci-ep-cfs driver sends the notification to EPF drivers based on that after each EPF driver bind. Link: https://lore.kernel.org/linux-pci/20240327-pci-dbi-rework-v12-8-082625472414@linaro.org Tested-by: Niklas Cassel <cassel@kernel.org> Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org> Signed-off-by: Krzysztof Wilczyński <kwilczynski@kernel.org> Reviewed-by: Frank Li <Frank.Li@nxp.com> Reviewed-by: Niklas Cassel <cassel@kernel.org>
2024-03-27 12:13:37 +03:00
/* Send any pending EPC initialization complete to the EPF driver */
pci_epc_notify_pending_init(epc, epf);
return 0;
}
static void pci_epc_epf_unlink(struct config_item *epc_item,
struct config_item *epf_item)
{
struct pci_epc *epc;
struct pci_epf *epf;
struct pci_epf_group *epf_group = to_pci_epf_group(epf_item);
struct pci_epc_group *epc_group = to_pci_epc_group(epc_item);
WARN_ON_ONCE(epc_group->start);
epc = epc_group->epc;
epf = epf_group->epf;
pci_epf_unbind(epf);
pci_epc_remove_epf(epc, epf, PRIMARY_INTERFACE);
}
static struct configfs_item_operations pci_epc_item_ops = {
.allow_link = pci_epc_epf_link,
.drop_link = pci_epc_epf_unlink,
};
static const struct config_item_type pci_epc_type = {
.ct_item_ops = &pci_epc_item_ops,
.ct_attrs = pci_epc_attrs,
.ct_owner = THIS_MODULE,
};
struct config_group *pci_ep_cfs_add_epc_group(const char *name)
{
int ret;
struct pci_epc *epc;
struct config_group *group;
struct pci_epc_group *epc_group;
epc_group = kzalloc(sizeof(*epc_group), GFP_KERNEL);
if (!epc_group) {
ret = -ENOMEM;
goto err;
}
group = &epc_group->group;
config_group_init_type_name(group, name, &pci_epc_type);
ret = configfs_register_group(controllers_group, group);
if (ret) {
pr_err("failed to register configfs group for %s\n", name);
goto err_register_group;
}
epc = pci_epc_get(name);
if (IS_ERR(epc)) {
ret = PTR_ERR(epc);
goto err_epc_get;
}
epc_group->epc = epc;
return group;
err_epc_get:
configfs_unregister_group(group);
err_register_group:
kfree(epc_group);
err:
return ERR_PTR(ret);
}
EXPORT_SYMBOL(pci_ep_cfs_add_epc_group);
void pci_ep_cfs_remove_epc_group(struct config_group *group)
{
struct pci_epc_group *epc_group;
if (!group)
return;
epc_group = container_of(group, struct pci_epc_group, group);
pci_epc_put(epc_group->epc);
configfs_unregister_group(&epc_group->group);
kfree(epc_group);
}
EXPORT_SYMBOL(pci_ep_cfs_remove_epc_group);
#define PCI_EPF_HEADER_R(_name) \
static ssize_t pci_epf_##_name##_show(struct config_item *item, char *page) \
{ \
struct pci_epf *epf = to_pci_epf_group(item)->epf; \
if (WARN_ON_ONCE(!epf->header)) \
return -EINVAL; \
return sysfs_emit(page, "0x%04x\n", epf->header->_name); \
}
#define PCI_EPF_HEADER_W_u32(_name) \
static ssize_t pci_epf_##_name##_store(struct config_item *item, \
const char *page, size_t len) \
{ \
u32 val; \
struct pci_epf *epf = to_pci_epf_group(item)->epf; \
if (WARN_ON_ONCE(!epf->header)) \
return -EINVAL; \
if (kstrtou32(page, 0, &val) < 0) \
return -EINVAL; \
epf->header->_name = val; \
return len; \
}
#define PCI_EPF_HEADER_W_u16(_name) \
static ssize_t pci_epf_##_name##_store(struct config_item *item, \
const char *page, size_t len) \
{ \
u16 val; \
struct pci_epf *epf = to_pci_epf_group(item)->epf; \
if (WARN_ON_ONCE(!epf->header)) \
return -EINVAL; \
if (kstrtou16(page, 0, &val) < 0) \
return -EINVAL; \
epf->header->_name = val; \
return len; \
}
#define PCI_EPF_HEADER_W_u8(_name) \
static ssize_t pci_epf_##_name##_store(struct config_item *item, \
const char *page, size_t len) \
{ \
u8 val; \
struct pci_epf *epf = to_pci_epf_group(item)->epf; \
if (WARN_ON_ONCE(!epf->header)) \
return -EINVAL; \
if (kstrtou8(page, 0, &val) < 0) \
return -EINVAL; \
epf->header->_name = val; \
return len; \
}
static ssize_t pci_epf_msi_interrupts_store(struct config_item *item,
const char *page, size_t len)
{
u8 val;
if (kstrtou8(page, 0, &val) < 0)
return -EINVAL;
to_pci_epf_group(item)->epf->msi_interrupts = val;
return len;
}
static ssize_t pci_epf_msi_interrupts_show(struct config_item *item,
char *page)
{
return sysfs_emit(page, "%d\n",
to_pci_epf_group(item)->epf->msi_interrupts);
}
static ssize_t pci_epf_msix_interrupts_store(struct config_item *item,
const char *page, size_t len)
{
u16 val;
if (kstrtou16(page, 0, &val) < 0)
return -EINVAL;
to_pci_epf_group(item)->epf->msix_interrupts = val;
return len;
}
static ssize_t pci_epf_msix_interrupts_show(struct config_item *item,
char *page)
{
return sysfs_emit(page, "%d\n",
to_pci_epf_group(item)->epf->msix_interrupts);
}
PCI_EPF_HEADER_R(vendorid)
PCI_EPF_HEADER_W_u16(vendorid)
PCI_EPF_HEADER_R(deviceid)
PCI_EPF_HEADER_W_u16(deviceid)
PCI_EPF_HEADER_R(revid)
PCI_EPF_HEADER_W_u8(revid)
PCI_EPF_HEADER_R(progif_code)
PCI_EPF_HEADER_W_u8(progif_code)
PCI_EPF_HEADER_R(subclass_code)
PCI_EPF_HEADER_W_u8(subclass_code)
PCI_EPF_HEADER_R(baseclass_code)
PCI_EPF_HEADER_W_u8(baseclass_code)
PCI_EPF_HEADER_R(cache_line_size)
PCI_EPF_HEADER_W_u8(cache_line_size)
PCI_EPF_HEADER_R(subsys_vendor_id)
PCI_EPF_HEADER_W_u16(subsys_vendor_id)
PCI_EPF_HEADER_R(subsys_id)
PCI_EPF_HEADER_W_u16(subsys_id)
PCI_EPF_HEADER_R(interrupt_pin)
PCI_EPF_HEADER_W_u8(interrupt_pin)
CONFIGFS_ATTR(pci_epf_, vendorid);
CONFIGFS_ATTR(pci_epf_, deviceid);
CONFIGFS_ATTR(pci_epf_, revid);
CONFIGFS_ATTR(pci_epf_, progif_code);
CONFIGFS_ATTR(pci_epf_, subclass_code);
CONFIGFS_ATTR(pci_epf_, baseclass_code);
CONFIGFS_ATTR(pci_epf_, cache_line_size);
CONFIGFS_ATTR(pci_epf_, subsys_vendor_id);
CONFIGFS_ATTR(pci_epf_, subsys_id);
CONFIGFS_ATTR(pci_epf_, interrupt_pin);
CONFIGFS_ATTR(pci_epf_, msi_interrupts);
CONFIGFS_ATTR(pci_epf_, msix_interrupts);
static struct configfs_attribute *pci_epf_attrs[] = {
&pci_epf_attr_vendorid,
&pci_epf_attr_deviceid,
&pci_epf_attr_revid,
&pci_epf_attr_progif_code,
&pci_epf_attr_subclass_code,
&pci_epf_attr_baseclass_code,
&pci_epf_attr_cache_line_size,
&pci_epf_attr_subsys_vendor_id,
&pci_epf_attr_subsys_id,
&pci_epf_attr_interrupt_pin,
&pci_epf_attr_msi_interrupts,
&pci_epf_attr_msix_interrupts,
NULL,
};
static int pci_epf_vepf_link(struct config_item *epf_pf_item,
struct config_item *epf_vf_item)
{
struct pci_epf_group *epf_vf_group = to_pci_epf_group(epf_vf_item);
struct pci_epf_group *epf_pf_group = to_pci_epf_group(epf_pf_item);
struct pci_epf *epf_pf = epf_pf_group->epf;
struct pci_epf *epf_vf = epf_vf_group->epf;
return pci_epf_add_vepf(epf_pf, epf_vf);
}
static void pci_epf_vepf_unlink(struct config_item *epf_pf_item,
struct config_item *epf_vf_item)
{
struct pci_epf_group *epf_vf_group = to_pci_epf_group(epf_vf_item);
struct pci_epf_group *epf_pf_group = to_pci_epf_group(epf_pf_item);
struct pci_epf *epf_pf = epf_pf_group->epf;
struct pci_epf *epf_vf = epf_vf_group->epf;
pci_epf_remove_vepf(epf_pf, epf_vf);
}
static void pci_epf_release(struct config_item *item)
{
struct pci_epf_group *epf_group = to_pci_epf_group(item);
mutex_lock(&functions_mutex);
idr_remove(&functions_idr, epf_group->index);
mutex_unlock(&functions_mutex);
pci_epf_destroy(epf_group->epf);
kfree(epf_group);
}
static struct configfs_item_operations pci_epf_ops = {
.allow_link = pci_epf_vepf_link,
.drop_link = pci_epf_vepf_unlink,
.release = pci_epf_release,
};
static const struct config_item_type pci_epf_type = {
.ct_item_ops = &pci_epf_ops,
.ct_attrs = pci_epf_attrs,
.ct_owner = THIS_MODULE,
};
/**
* pci_epf_type_add_cfs() - Help function drivers to expose function specific
* attributes in configfs
* @epf: the EPF device that has to be configured using configfs
* @group: the parent configfs group (corresponding to entries in
* pci_epf_device_id)
*
* Invoke to expose function specific attributes in configfs.
*
* Return: A pointer to a config_group structure or NULL if the function driver
* does not have anything to expose (attributes configured by user) or if
* the function driver does not implement the add_cfs() method.
*
* Returns an error pointer if this function is called for an unbound EPF device
* or if the EPF driver add_cfs() method fails.
*/
static struct config_group *pci_epf_type_add_cfs(struct pci_epf *epf,
struct config_group *group)
{
struct config_group *epf_type_group;
if (!epf->driver) {
dev_err(&epf->dev, "epf device not bound to driver\n");
return ERR_PTR(-ENODEV);
}
if (!epf->driver->ops->add_cfs)
return NULL;
mutex_lock(&epf->lock);
epf_type_group = epf->driver->ops->add_cfs(epf, group);
mutex_unlock(&epf->lock);
return epf_type_group;
}
PCI: endpoint: Automatically create a function specific attributes group A PCI endpoint function driver can define function specific attributes under its function configfs directory using the add_cfs() endpoint driver operation. This is done by tying up the mkdir operation for the function configfs directory to a call to the add_cfs() operation. However, there are no checks preventing the user from repeatedly creating function specific attribute directories with different names, resulting in the same endpoint specific attributes group being added multiple times, which also result in an invalid reference counting for the attribute groups. E.g., using the pci-epf-ntb function driver as an example, the user creates the function as follows: $ modprobe pci-epf-ntb $ cd /sys/kernel/config/pci_ep/functions/pci_epf_ntb $ mkdir func0 $ tree func0 func0/ |-- baseclass_code |-- cache_line_size |-- ... `-- vendorid $ mkdir func0/attrs $ tree func0 func0/ |-- attrs | |-- db_count | |-- mw1 | |-- mw2 | |-- mw3 | |-- mw4 | |-- num_mws | `-- spad_count |-- baseclass_code |-- cache_line_size |-- ... `-- vendorid At this point, the function can be started by linking the EP controller. However, if the user mistakenly creates again a directory: $ mkdir func0/attrs2 $ tree func0 func0/ |-- attrs | |-- db_count | |-- mw1 | |-- mw2 | |-- mw3 | |-- mw4 | |-- num_mws | `-- spad_count |-- attrs2 | |-- db_count | |-- mw1 | |-- mw2 | |-- mw3 | |-- mw4 | |-- num_mws | `-- spad_count |-- baseclass_code |-- cache_line_size |-- ... `-- vendorid The endpoint function specific attributes are duplicated and cause a crash when the endpoint function device is torn down: refcount_t: addition on 0; use-after-free. WARNING: CPU: 2 PID: 834 at lib/refcount.c:25 refcount_warn_saturate+0xc8/0x144 CPU: 2 PID: 834 Comm: rmdir Not tainted 6.3.0-rc1 #1 Hardware name: Pine64 RockPro64 v2.1 (DT) pstate: 60000005 (nZCv daif -PAN -UAO -TCO -DIT -SSBS BTYPE=--) ... Call trace: refcount_warn_saturate+0xc8/0x144 config_item_get+0x7c/0x80 configfs_rmdir+0x17c/0x30c vfs_rmdir+0x8c/0x204 do_rmdir+0x158/0x184 __arm64_sys_unlinkat+0x64/0x80 invoke_syscall+0x48/0x114 ... Fix this by modifying pci_epf_cfs_work() to execute the new function pci_ep_cfs_add_type_group() which itself calls pci_epf_type_add_cfs() to obtain the function specific attribute group and the group name (directory name) from the endpoint function driver. If the function driver defines an attribute group, pci_ep_cfs_add_type_group() then proceeds to register this group using configfs_register_group(), thus automatically exposing the function type specific configfs attributes to the user. E.g.: $ modprobe pci-epf-ntb $ cd /sys/kernel/config/pci_ep/functions/pci_epf_ntb $ mkdir func0 $ tree func0 func0/ |-- baseclass_code |-- cache_line_size |-- ... |-- pci_epf_ntb.0 | |-- db_count | |-- mw1 | |-- mw2 | |-- mw3 | |-- mw4 | |-- num_mws | `-- spad_count |-- primary |-- ... `-- vendorid With this change, there is no need for the user to create or delete directories in the endpoint function attributes directory. The pci_epf_type_group_ops group operations are thus removed. Also update the documentation for the pci-epf-ntb and pci-epf-vntb function drivers to reflect this change, removing the explanations showing the need to manually create the sub-directory for the function specific attributes. Link: https://lore.kernel.org/r/20230415023542.77601-2-dlemoal@kernel.org Signed-off-by: Damien Le Moal <dlemoal@kernel.org> Signed-off-by: Lorenzo Pieralisi <lpieralisi@kernel.org> Signed-off-by: Bjorn Helgaas <bhelgaas@google.com> Reviewed-by: Manivannan Sadhasivam <mani@kernel.org>
2023-04-15 05:35:26 +03:00
static void pci_ep_cfs_add_type_group(struct pci_epf_group *epf_group)
{
struct config_group *group;
group = pci_epf_type_add_cfs(epf_group->epf, &epf_group->group);
if (!group)
return;
if (IS_ERR(group)) {
dev_err(&epf_group->epf->dev,
"failed to create epf type specific attributes\n");
return;
}
configfs_register_group(&epf_group->group, group);
}
static void pci_epf_cfs_work(struct work_struct *work)
{
struct pci_epf_group *epf_group;
struct config_group *group;
epf_group = container_of(work, struct pci_epf_group, cfs_work.work);
group = pci_ep_cfs_add_primary_group(epf_group);
if (IS_ERR(group)) {
pr_err("failed to create 'primary' EPC interface\n");
return;
}
group = pci_ep_cfs_add_secondary_group(epf_group);
if (IS_ERR(group)) {
pr_err("failed to create 'secondary' EPC interface\n");
return;
}
PCI: endpoint: Automatically create a function specific attributes group A PCI endpoint function driver can define function specific attributes under its function configfs directory using the add_cfs() endpoint driver operation. This is done by tying up the mkdir operation for the function configfs directory to a call to the add_cfs() operation. However, there are no checks preventing the user from repeatedly creating function specific attribute directories with different names, resulting in the same endpoint specific attributes group being added multiple times, which also result in an invalid reference counting for the attribute groups. E.g., using the pci-epf-ntb function driver as an example, the user creates the function as follows: $ modprobe pci-epf-ntb $ cd /sys/kernel/config/pci_ep/functions/pci_epf_ntb $ mkdir func0 $ tree func0 func0/ |-- baseclass_code |-- cache_line_size |-- ... `-- vendorid $ mkdir func0/attrs $ tree func0 func0/ |-- attrs | |-- db_count | |-- mw1 | |-- mw2 | |-- mw3 | |-- mw4 | |-- num_mws | `-- spad_count |-- baseclass_code |-- cache_line_size |-- ... `-- vendorid At this point, the function can be started by linking the EP controller. However, if the user mistakenly creates again a directory: $ mkdir func0/attrs2 $ tree func0 func0/ |-- attrs | |-- db_count | |-- mw1 | |-- mw2 | |-- mw3 | |-- mw4 | |-- num_mws | `-- spad_count |-- attrs2 | |-- db_count | |-- mw1 | |-- mw2 | |-- mw3 | |-- mw4 | |-- num_mws | `-- spad_count |-- baseclass_code |-- cache_line_size |-- ... `-- vendorid The endpoint function specific attributes are duplicated and cause a crash when the endpoint function device is torn down: refcount_t: addition on 0; use-after-free. WARNING: CPU: 2 PID: 834 at lib/refcount.c:25 refcount_warn_saturate+0xc8/0x144 CPU: 2 PID: 834 Comm: rmdir Not tainted 6.3.0-rc1 #1 Hardware name: Pine64 RockPro64 v2.1 (DT) pstate: 60000005 (nZCv daif -PAN -UAO -TCO -DIT -SSBS BTYPE=--) ... Call trace: refcount_warn_saturate+0xc8/0x144 config_item_get+0x7c/0x80 configfs_rmdir+0x17c/0x30c vfs_rmdir+0x8c/0x204 do_rmdir+0x158/0x184 __arm64_sys_unlinkat+0x64/0x80 invoke_syscall+0x48/0x114 ... Fix this by modifying pci_epf_cfs_work() to execute the new function pci_ep_cfs_add_type_group() which itself calls pci_epf_type_add_cfs() to obtain the function specific attribute group and the group name (directory name) from the endpoint function driver. If the function driver defines an attribute group, pci_ep_cfs_add_type_group() then proceeds to register this group using configfs_register_group(), thus automatically exposing the function type specific configfs attributes to the user. E.g.: $ modprobe pci-epf-ntb $ cd /sys/kernel/config/pci_ep/functions/pci_epf_ntb $ mkdir func0 $ tree func0 func0/ |-- baseclass_code |-- cache_line_size |-- ... |-- pci_epf_ntb.0 | |-- db_count | |-- mw1 | |-- mw2 | |-- mw3 | |-- mw4 | |-- num_mws | `-- spad_count |-- primary |-- ... `-- vendorid With this change, there is no need for the user to create or delete directories in the endpoint function attributes directory. The pci_epf_type_group_ops group operations are thus removed. Also update the documentation for the pci-epf-ntb and pci-epf-vntb function drivers to reflect this change, removing the explanations showing the need to manually create the sub-directory for the function specific attributes. Link: https://lore.kernel.org/r/20230415023542.77601-2-dlemoal@kernel.org Signed-off-by: Damien Le Moal <dlemoal@kernel.org> Signed-off-by: Lorenzo Pieralisi <lpieralisi@kernel.org> Signed-off-by: Bjorn Helgaas <bhelgaas@google.com> Reviewed-by: Manivannan Sadhasivam <mani@kernel.org>
2023-04-15 05:35:26 +03:00
pci_ep_cfs_add_type_group(epf_group);
}
static struct config_group *pci_epf_make(struct config_group *group,
const char *name)
{
struct pci_epf_group *epf_group;
struct pci_epf *epf;
char *epf_name;
int index, err;
epf_group = kzalloc(sizeof(*epf_group), GFP_KERNEL);
if (!epf_group)
return ERR_PTR(-ENOMEM);
mutex_lock(&functions_mutex);
index = idr_alloc(&functions_idr, epf_group, 0, 0, GFP_KERNEL);
mutex_unlock(&functions_mutex);
if (index < 0) {
err = index;
goto free_group;
}
epf_group->index = index;
config_group_init_type_name(&epf_group->group, name, &pci_epf_type);
epf_name = kasprintf(GFP_KERNEL, "%s.%d",
group->cg_item.ci_name, epf_group->index);
if (!epf_name) {
err = -ENOMEM;
goto remove_idr;
}
epf = pci_epf_create(epf_name);
if (IS_ERR(epf)) {
pr_err("failed to create endpoint function device\n");
err = -EINVAL;
goto free_name;
}
epf->group = &epf_group->group;
epf_group->epf = epf;
kfree(epf_name);
INIT_DELAYED_WORK(&epf_group->cfs_work, pci_epf_cfs_work);
queue_delayed_work(system_wq, &epf_group->cfs_work,
msecs_to_jiffies(1));
return &epf_group->group;
free_name:
kfree(epf_name);
remove_idr:
mutex_lock(&functions_mutex);
idr_remove(&functions_idr, epf_group->index);
mutex_unlock(&functions_mutex);
free_group:
kfree(epf_group);
return ERR_PTR(err);
}
static void pci_epf_drop(struct config_group *group, struct config_item *item)
{
config_item_put(item);
}
static struct configfs_group_operations pci_epf_group_ops = {
.make_group = &pci_epf_make,
.drop_item = &pci_epf_drop,
};
static const struct config_item_type pci_epf_group_type = {
.ct_group_ops = &pci_epf_group_ops,
.ct_owner = THIS_MODULE,
};
struct config_group *pci_ep_cfs_add_epf_group(const char *name)
{
struct config_group *group;
group = configfs_register_default_group(functions_group, name,
&pci_epf_group_type);
if (IS_ERR(group))
pr_err("failed to register configfs group for %s function\n",
name);
return group;
}
EXPORT_SYMBOL(pci_ep_cfs_add_epf_group);
void pci_ep_cfs_remove_epf_group(struct config_group *group)
{
if (IS_ERR_OR_NULL(group))
return;
configfs_unregister_default_group(group);
}
EXPORT_SYMBOL(pci_ep_cfs_remove_epf_group);
static const struct config_item_type pci_functions_type = {
.ct_owner = THIS_MODULE,
};
static const struct config_item_type pci_controllers_type = {
.ct_owner = THIS_MODULE,
};
static const struct config_item_type pci_ep_type = {
.ct_owner = THIS_MODULE,
};
static struct configfs_subsystem pci_ep_cfs_subsys = {
.su_group = {
.cg_item = {
.ci_namebuf = "pci_ep",
.ci_type = &pci_ep_type,
},
},
.su_mutex = __MUTEX_INITIALIZER(pci_ep_cfs_subsys.su_mutex),
};
static int __init pci_ep_cfs_init(void)
{
int ret;
struct config_group *root = &pci_ep_cfs_subsys.su_group;
config_group_init(root);
ret = configfs_register_subsystem(&pci_ep_cfs_subsys);
if (ret) {
pr_err("Error %d while registering subsystem %s\n",
ret, root->cg_item.ci_namebuf);
goto err;
}
functions_group = configfs_register_default_group(root, "functions",
&pci_functions_type);
if (IS_ERR(functions_group)) {
ret = PTR_ERR(functions_group);
pr_err("Error %d while registering functions group\n",
ret);
goto err_functions_group;
}
controllers_group =
configfs_register_default_group(root, "controllers",
&pci_controllers_type);
if (IS_ERR(controllers_group)) {
ret = PTR_ERR(controllers_group);
pr_err("Error %d while registering controllers group\n",
ret);
goto err_controllers_group;
}
return 0;
err_controllers_group:
configfs_unregister_default_group(functions_group);
err_functions_group:
configfs_unregister_subsystem(&pci_ep_cfs_subsys);
err:
return ret;
}
module_init(pci_ep_cfs_init);
static void __exit pci_ep_cfs_exit(void)
{
configfs_unregister_default_group(controllers_group);
configfs_unregister_default_group(functions_group);
configfs_unregister_subsystem(&pci_ep_cfs_subsys);
}
module_exit(pci_ep_cfs_exit);
MODULE_DESCRIPTION("PCI EP CONFIGFS");
MODULE_AUTHOR("Kishon Vijay Abraham I <kishon@ti.com>");