Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Damien Le Moal and committed by
Bjorn Helgaas
70b3740f 37587673

+29 -37
+4 -7
Documentation/PCI/endpoint/pci-ntb-howto.rst
··· 88 88 # echo 0x104c > functions/pci_epf_ntb/func1/vendorid 89 89 # echo 0xb00d > functions/pci_epf_ntb/func1/deviceid 90 90 91 - In order to configure NTB specific attributes, a new sub-directory to func1 92 - should be created:: 93 - 94 - # mkdir functions/pci_epf_ntb/func1/pci_epf_ntb.0/ 95 - 96 - The NTB function driver will populate this directory with various attributes 97 - that can be configured by the user:: 91 + The PCI endpoint framework also automatically creates a sub-directory in the 92 + function attribute directory. This sub-directory has the same name as the name 93 + of the function device and is populated with the following NTB specific 94 + attributes that can be configured by the user:: 98 95 99 96 # ls functions/pci_epf_ntb/func1/pci_epf_ntb.0/ 100 97 db_count mw1 mw2 mw3 mw4 num_mws
+5 -8
Documentation/PCI/endpoint/pci-vntb-howto.rst
··· 84 84 # echo 0x1957 > functions/pci_epf_vntb/func1/vendorid 85 85 # echo 0x0809 > functions/pci_epf_vntb/func1/deviceid 86 86 87 - In order to configure NTB specific attributes, a new sub-directory to func1 88 - should be created:: 89 - 90 - # mkdir functions/pci_epf_vntb/func1/pci_epf_vntb.0/ 91 - 92 - The NTB function driver will populate this directory with various attributes 93 - that can be configured by the user:: 87 + The PCI endpoint framework also automatically creates a sub-directory in the 88 + function attribute directory. This sub-directory has the same name as the name 89 + of the function device and is populated with the following NTB specific 90 + attributes that can be configured by the user:: 94 91 95 92 # ls functions/pci_epf_vntb/func1/pci_epf_vntb.0/ 96 93 db_count mw1 mw2 mw3 mw4 num_mws ··· 100 103 # echo 1 > functions/pci_epf_vntb/func1/pci_epf_vntb.0/num_mws 101 104 # echo 0x100000 > functions/pci_epf_vntb/func1/pci_epf_vntb.0/mw1 102 105 103 - A sample configuration for virtual NTB driver for virutal PCI bus:: 106 + A sample configuration for virtual NTB driver for virtual PCI bus:: 104 107 105 108 # echo 0x1957 > functions/pci_epf_vntb/func1/pci_epf_vntb.0/vntb_vid 106 109 # echo 0x080A > functions/pci_epf_vntb/func1/pci_epf_vntb.0/vntb_pid
+20 -22
drivers/pci/endpoint/pci-ep-cfs.c
··· 23 23 struct config_group group; 24 24 struct config_group primary_epc_group; 25 25 struct config_group secondary_epc_group; 26 + struct config_group *type_group; 26 27 struct delayed_work cfs_work; 27 28 struct pci_epf *epf; 28 29 int index; ··· 503 502 .release = pci_epf_release, 504 503 }; 505 504 506 - static struct config_group *pci_epf_type_make(struct config_group *group, 507 - const char *name) 508 - { 509 - struct pci_epf_group *epf_group = to_pci_epf_group(&group->cg_item); 510 - struct config_group *epf_type_group; 511 - 512 - epf_type_group = pci_epf_type_add_cfs(epf_group->epf, group); 513 - return epf_type_group; 514 - } 515 - 516 - static void pci_epf_type_drop(struct config_group *group, 517 - struct config_item *item) 518 - { 519 - config_item_put(item); 520 - } 521 - 522 - static struct configfs_group_operations pci_epf_type_group_ops = { 523 - .make_group = &pci_epf_type_make, 524 - .drop_item = &pci_epf_type_drop, 525 - }; 526 - 527 505 static const struct config_item_type pci_epf_type = { 528 - .ct_group_ops = &pci_epf_type_group_ops, 529 506 .ct_item_ops = &pci_epf_ops, 530 507 .ct_attrs = pci_epf_attrs, 531 508 .ct_owner = THIS_MODULE, 532 509 }; 510 + 511 + static void pci_ep_cfs_add_type_group(struct pci_epf_group *epf_group) 512 + { 513 + struct config_group *group; 514 + 515 + group = pci_epf_type_add_cfs(epf_group->epf, &epf_group->group); 516 + if (!group) 517 + return; 518 + 519 + if (IS_ERR(group)) { 520 + dev_err(&epf_group->epf->dev, 521 + "failed to create epf type specific attributes\n"); 522 + return; 523 + } 524 + 525 + configfs_register_group(&epf_group->group, group); 526 + } 533 527 534 528 static void pci_epf_cfs_work(struct work_struct *work) 535 529 { ··· 543 547 pr_err("failed to create 'secondary' EPC interface\n"); 544 548 return; 545 549 } 550 + 551 + pci_ep_cfs_add_type_group(epf_group); 546 552 } 547 553 548 554 static struct config_group *pci_epf_make(struct config_group *group,