PCI endpoint fixes and improvements
Subsystems (4)
v5 cover PCI endpoint fixes and improvements
This series fixes several issues with the PCI endpoint code and endpoint test drivers (RC side and EP side).
The first 2 patches address an issue with the use of configfs to create an endpoint driver type attributes group, preventing a potential crash if the user creates a directory multiple times for the driver type attributes.
The following patches are fixes and improvements for the endpoint test drivers, EP side and RC side.
This is all tested using a Pine Rockpro64 board, with the rockchip ep driver fixed using Rick Wertenbroek <[email protected]> patches [1].
[1] https://lore.kernel.org/linux-pci/[email protected]/
Changes from v4:
- Addressed Bjorn comments on the commit messages (typos, text clarity)
- Changed patch 1 to use dev_err() instead of pr_err().
- Add print of invalid command value in patch 10
- Chnaged all patches author and Signed-off-by tag to use my kernel.org email address
- Added Mani's review tags
Changes from v3:
- Corrected patch 7 and 12 title
- Added patch 11
Changes from v2:
- Add updates of the ntb and vntb function driver documentation in patch 1 to reflect the patch changes.
- Removed unnecessary WARN_ON() call in patch 4
- Added missing cc: stable tags
- Added review tags
Changes from v1:
- Improved patch 1 commit message
- Modified patch 2 to not have to add an internal header file
- Split former patch 3 into patch 3, 4 and 5
- Removed former patch 4 introducing volatile casts and replaced it with patch 9
- Added patch 6, 7, 8 and 10
- Added Reviewed-by tags in patches not modified
Damien Le Moal (17):
PCI: endpoint: Automatically create a function specific attributes
group
PCI: endpoint: Move pci_epf_type_add_cfs() code
PCI: epf-test: Fix DMA transfer completion initialization
PCI: epf-test: Fix DMA transfer completion detection
PCI: epf-test: Use dmaengine_submit() to initiate DMA transfer
PCI: epf-test: Simplify read/write/copy test functions
PCI: epf-test: Simplify pci_epf_test_raise_irq()
PCI: epf-test: Simplify IRQ test commands execution
PCI: epf-test: Improve handling of command and status registers
PCI: epf-test: Cleanup pci_epf_test_cmd_handler()
PCI: epf-test: Cleanup request result handling
PCI: epf-test: Simplify DMA support checks
PCI: epf-test: Simplify transfers result print
misc: pci_endpoint_test: Free IRQs before removing the device
misc: pci_endpoint_test: Re-init completion for every test
misc: pci_endpoint_test: Do not write status in IRQ handler
misc: pci_endpoint_test: Simplify pci_endpoint_test_msi_irq()
Documentation/PCI/endpoint/pci-ntb-howto.rst | 11 +- Documentation/PCI/endpoint/pci-vntb-howto.rst | 13 +- drivers/misc/pci_endpoint_test.c | 25 +- drivers/pci/endpoint/functions/pci-epf-test.c | 266 ++++++++---------- drivers/pci/endpoint/pci-ep-cfs.c | 54 ++-- drivers/pci/endpoint/pci-epf-core.c | 32 --- include/linux/pci-epf.h | 2 - 7 files changed, 175 insertions(+), 228 deletions(-)
Signature · 1 line
2.39.2
PATCH v5 1/17 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.
alos 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.
--- Documentation/PCI/endpoint/pci-ntb-howto.rst | 11 ++--- Documentation/PCI/endpoint/pci-vntb-howto.rst | 13 +++--- drivers/pci/endpoint/pci-ep-cfs.c | 42 +++++++++---------- 3 files changed, 29 insertions(+), 37 deletions(-)
Documentation/PCI/endpoint/pci-ntb-howto.rst +4 -7
@@ -88,13 +88,10 @@ commands can be used:: # echo 0x104c > functions/pci_epf_ntb/func1/vendorid # echo 0xb00d > functions/pci_epf_ntb/func1/deviceid -In order to configure NTB specific attributes, a new sub-directory to func1 -should be created:: - - # mkdir functions/pci_epf_ntb/func1/pci_epf_ntb.0/ - -The NTB function driver will populate this directory with various attributes -that can be configured by the user:: +The PCI endpoint framework also automatically creates a sub-directory in the +function attribute directory. This sub-directory has the same name as the name +of the function device and is populated with the following NTB specific +attributes that can be configured by the user:: # ls functions/pci_epf_ntb/func1/pci_epf_ntb.0/ db_count mw1 mw2 mw3 mw4 num_mws
Documentation/PCI/endpoint/pci-vntb-howto.rst +5 -8
@@ -84,13 +84,10 @@ commands can be used:: # echo 0x1957 > functions/pci_epf_vntb/func1/vendorid # echo 0x0809 > functions/pci_epf_vntb/func1/deviceid -In order to configure NTB specific attributes, a new sub-directory to func1 -should be created:: - - # mkdir functions/pci_epf_vntb/func1/pci_epf_vntb.0/ - -The NTB function driver will populate this directory with various attributes -that can be configured by the user:: +The PCI endpoint framework also automatically creates a sub-directory in the +function attribute directory. This sub-directory has the same name as the name +of the function device and is populated with the following NTB specific +attributes that can be configured by the user:: # ls functions/pci_epf_vntb/func1/pci_epf_vntb.0/ db_count mw1 mw2 mw3 mw4 num_mws
@@ -103,7 +100,7 @@ A sample configuration for NTB function is given below:: # echo 1 > functions/pci_epf_vntb/func1/pci_epf_vntb.0/num_mws # echo 0x100000 > functions/pci_epf_vntb/func1/pci_epf_vntb.0/mw1 -A sample configuration for virtual NTB driver for virutal PCI bus:: +A sample configuration for virtual NTB driver for virtual PCI bus:: # echo 0x1957 > functions/pci_epf_vntb/func1/pci_epf_vntb.0/vntb_vid # echo 0x080A > functions/pci_epf_vntb/func1/pci_epf_vntb.0/vntb_pid
drivers/pci/endpoint/pci-ep-cfs.c +20 -22
66 lines not on this page · raw message
Signature · 1 line
2.39.2
PATCH v5 2/17 PCI: endpoint: Move pci_epf_type_add_cfs() code
In reply to Damien Le Moalpci_epf_type_add_cfs() is called only from pci_ep_cfs_add_type_group() in drivers/pci/endpoint/pci-ep-cfs.c, so there is no need to export this function and we can move its code from pci-epf-core.c to pci-ep-cfs.c as a static function.
--- drivers/pci/endpoint/pci-ep-cfs.c | 20 ++++++++++++++++++ drivers/pci/endpoint/pci-epf-core.c | 32 ----------------------------- include/linux/pci-epf.h | 2 -- 3 files changed, 20 insertions(+), 34 deletions(-)
drivers/pci/endpoint/pci-ep-cfs.c +20 -0
@@ -509,6 +509,26 @@ static const struct config_item_type pci_epf_type = { .ct_owner = THIS_MODULE, }; +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 NULL ; + } + +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; +} +static void pci_ep_cfs_add_type_group (struct pci_epf_group *epf_group) {struct config_group *group;
drivers/pci/endpoint/pci-epf-core.c +0 -32
@@ -20,38 +20,6 @@ static DEFINE_MUTEX(pci_epf_mutex);static struct bus_type pci_epf_bus_type;static const struct device_type pci_epf_type; -/** -* 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. If the function -* driver does not have anything to expose (attributes configured by user), -* return NULL. -*/ -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 NULL ; - } - -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; -} -EXPORT_SYMBOL_GPL (pci_epf_type_add_cfs); -/** * pci_epf_unbind() - Notify the function driver that the binding between the * EPF device and EPC device has been lost
include/linux/pci-epf.h +0 -2
@@ -214,8 +214,6 @@ void pci_epf_free_space(struct pci_epf *epf, void *addr, enum pci_barno bar, enum pci_epc_interface_type type);int pci_epf_bind (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 *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 */
Signature · 1 line
2.39.2
PATCH v5 3/17 PCI: epf-test: Fix DMA transfer completion initialization
In reply to Damien Le MoalRe-initialize the transfer_complete DMA transfer completion before calling tx_submit(), to avoid seeing the DMA transfer complete before the completion is initialized, thus potentially losing the completion notification.
--- drivers/pci/endpoint/functions/pci-epf-test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/pci/endpoint/functions/pci-epf-test.c +1 -1
@@ -151,10 +151,10 @@ static int pci_epf_test_data_transfer(struct pci_epf_test *epf_test,return -EIO ; } +reinit_completion (&epf_test->transfer_complete); tx->callback = pci_epf_test_dma_callback; tx->callback_param = epf_test; cookie = tx->tx_submit (tx); -reinit_completion (&epf_test->transfer_complete); ret =dma_submit_error (cookie);if (ret) {
Signature · 1 line
2.39.2
PATCH v5 4/17 PCI: epf-test: Fix DMA transfer completion detection
In reply to Damien Le Moalpci_epf_test_data_transfer() and pci_epf_test_dma_callback() are not handling DMA transfer completion correctly, leading to completion notifications to the RC side that are too early. This problem can be detected when the RC side is running an IOMMU with messages such as:
pci-endpoint-test 0000:0b:00.0: AMD-Vi: Event logged [IO_PAGE_FAULT domain=0x001c address=0xfff00000 flags=0x0000]
When running the pcitest.sh tests: the address used for a previous test transfer generates the above error while the next test transfer is running.
Fix this by testing the DMA transfer status in pci_epf_test_dma_callback() and notifying the completion only when the transfer status is DMA_COMPLETE or DMA_ERROR. Furthermore, in pci_epf_test_data_transfer(), be paranoid and check again the transfer status and always call dmaengine_terminate_sync() before returning.
--- drivers/pci/endpoint/functions/pci-epf-test.c | 38 +++++++++++++------ 1 file changed, 27 insertions(+), 11 deletions(-)
drivers/pci/endpoint/functions/pci-epf-test.c +27 -11
@@ -54,6 +54,9 @@ struct pci_epf_test {struct delayed_work cmd_handler;struct dma_chan *dma_chan_tx;struct dma_chan *dma_chan_rx; +struct dma_chan *transfer_chan; +dma_cookie_t transfer_cookie; +enum dma_status transfer_status;struct completion transfer_complete;bool dma_supported;bool dma_private;
@@ -85,8 +88,14 @@ static size_t bar_size[] = { 512, 512, 1024, 16384, 131072, 1048576 };static void pci_epf_test_dma_callback (void *param) {struct pci_epf_test *epf_test = param; - -complete (&epf_test->transfer_complete); +struct dma_tx_state state; + + epf_test->transfer_status = +dmaengine_tx_status (epf_test->transfer_chan, + epf_test->transfer_cookie, &state); +if (epf_test->transfer_status ==DMA_COMPLETE || + epf_test->transfer_status ==DMA_ERROR ) +complete (&epf_test->transfer_complete); }/**
@@ -120,7 +129,6 @@ static int pci_epf_test_data_transfer(struct pci_epf_test *epf_test,struct dma_async_tx_descriptor *tx;struct dma_slave_config sconf = {};struct device *dev = &epf->dev; -dma_cookie_t cookie;int ret;if (IS_ERR_OR_NULL (chan)) {
@@ -152,25 +160,33 @@ static int pci_epf_test_data_transfer(struct pci_epf_test *epf_test, }reinit_completion (&epf_test->transfer_complete); + epf_test->transfer_chan = chan; tx->callback = pci_epf_test_dma_callback; tx->callback_param = epf_test; - cookie = tx->tx_submit (tx); + epf_test->transfer_cookie = tx->tx_submit (tx); - ret =dma_submit_error (cookie); + ret =dma_submit_error (epf_test->transfer_cookie);if (ret) { -dev_err (dev,"Failed to do DMA tx_submit %d\n" , cookie); -return -EIO ; +dev_err (dev,"Failed to do DMA tx_submit %d\n" , ret); + goto terminate; }dma_async_issue_pending (chan); ret =wait_for_completion_interruptible (&epf_test->transfer_complete);if (ret <0 ) { -dmaengine_terminate_sync (chan); -dev_err (dev,"DMA wait_for_completion_timeout\n" ); -return -ETIMEDOUT ; +dev_err (dev,"DMA wait_for_completion interrupted\n" ); + goto terminate; } -return 0 ; +if (epf_test->transfer_status ==DMA_ERROR ) { +dev_err (dev,"DMA transfer failed\n" ); + ret = -EIO ; + } + +terminate: +dmaengine_terminate_sync (chan); + +return ret; }struct epf_dma_filter {
Signature · 1 line
2.39.2
PATCH v5 5/17 PCI: epf-test: Use dmaengine_submit() to initiate DMA transfer
In reply to Damien Le MoalInstead of an open coded call to the tx_submit() operation of struct dma_async_tx_descriptor, use the helper function dmaengine_submit(). No functional change is introduced with this.
--- drivers/pci/endpoint/functions/pci-epf-test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/pci/endpoint/functions/pci-epf-test.c +1 -1
@@ -163,7 +163,7 @@ static int pci_epf_test_data_transfer(struct pci_epf_test *epf_test, epf_test->transfer_chan = chan; tx->callback = pci_epf_test_dma_callback; tx->callback_param = epf_test; - epf_test->transfer_cookie = tx->tx_submit (tx); + epf_test->transfer_cookie =dmaengine_submit (tx); ret =dma_submit_error (epf_test->transfer_cookie);if (ret) {
Signature · 1 line
2.39.2
PATCH v5 6/17 PCI: epf-test: Simplify read/write/copy test functions
In reply to Damien Le MoalThe function pci_epf_test_cmd_handler() uses the register BAR address as a pointer to a struct pci_epf_test_reg to determine the command sent by the host and to execute the test function accordingly. There is no need for doing this assignment again in each of the read, write and copy test functions. We can simply pass the reg pointer as an argument to the functions pci_epf_test_write(), pci_epf_test_read() and pci_epf_test_copy().
--- drivers/pci/endpoint/functions/pci-epf-test.c | 21 ++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-)
drivers/pci/endpoint/functions/pci-epf-test.c +9 -12
@@ -325,7 +325,8 @@ static void pci_epf_test_print_rate(const char *ops, u64 size, (u64)ts.tv_sec, (u32)ts.tv_nsec, rate / 1024); } -static int pci_epf_test_copy (struct pci_epf_test *epf_test) +static int pci_epf_test_copy (struct pci_epf_test *epf_test, +struct pci_epf_test_reg *reg) {int ret;bool use_dma;
@@ -337,8 +338,6 @@ static int pci_epf_test_copy(struct pci_epf_test *epf_test)struct pci_epf *epf = epf_test->epf;struct device *dev = &epf->dev;struct pci_epc *epc = epf->epc; -enum pci_barno test_reg_bar = epf_test->test_reg_bar; -struct pci_epf_test_reg *reg = epf_test->reg[test_reg_bar]; src_addr =pci_epc_mem_alloc_addr (epc, &src_phys_addr, reg->size);if (!src_addr) {
@@ -424,7 +423,8 @@ static int pci_epf_test_copy(struct pci_epf_test *epf_test)return ret; } -static int pci_epf_test_read (struct pci_epf_test *epf_test) +static int pci_epf_test_read (struct pci_epf_test *epf_test, +struct pci_epf_test_reg *reg) {int ret;void __iomem *src_addr;
@@ -438,8 +438,6 @@ static int pci_epf_test_read(struct pci_epf_test *epf_test)struct device *dev = &epf->dev;struct pci_epc *epc = epf->epc;struct device *dma_dev = epf->epc->dev.parent; -enum pci_barno test_reg_bar = epf_test->test_reg_bar; -struct pci_epf_test_reg *reg = epf_test->reg[test_reg_bar]; src_addr =pci_epc_mem_alloc_addr (epc, &phys_addr, reg->size);if (!src_addr) {
@@ -514,7 +512,8 @@ static int pci_epf_test_read(struct pci_epf_test *epf_test)return ret; } -static int pci_epf_test_write (struct pci_epf_test *epf_test) +static int pci_epf_test_write (struct pci_epf_test *epf_test, +struct pci_epf_test_reg *reg) {int ret;void __iomem *dst_addr;
@@ -527,8 +526,6 @@ static int pci_epf_test_write(struct pci_epf_test *epf_test)struct device *dev = &epf->dev;struct pci_epc *epc = epf->epc;struct device *dma_dev = epf->epc->dev.parent; -enum pci_barno test_reg_bar = epf_test->test_reg_bar; -struct pci_epf_test_reg *reg = epf_test->reg[test_reg_bar]; dst_addr =pci_epc_mem_alloc_addr (epc, &phys_addr, reg->size);if (!dst_addr) {
@@ -673,7 +670,7 @@ static void pci_epf_test_cmd_handler(struct work_struct *work) }if (command &COMMAND_WRITE ) { - ret =pci_epf_test_write (epf_test); + ret =pci_epf_test_write (epf_test, reg);if (ret) reg->status |=STATUS_WRITE_FAIL ;else
@@ -684,7 +681,7 @@ static void pci_epf_test_cmd_handler(struct work_struct *work) }if (command &COMMAND_READ ) { - ret =pci_epf_test_read (epf_test); + ret =pci_epf_test_read (epf_test, reg);if (!ret) reg->status |=STATUS_READ_SUCCESS ;else
@@ -695,7 +692,7 @@ static void pci_epf_test_cmd_handler(struct work_struct *work) }if (command &COMMAND_COPY ) { - ret =pci_epf_test_copy (epf_test); + ret =pci_epf_test_copy (epf_test, reg);if (!ret) reg->status |=STATUS_COPY_SUCCESS ;else
Signature · 1 line
2.39.2
PATCH v5 7/17 PCI: epf-test: Simplify pci_epf_test_raise_irq()
In reply to Damien Le MoalChange the interface of the function pci_epf_test_raise_irq() to directly pass a pointer to the struct pci_epf_test_reg defining the test being executed. This avoids the need for grabbing this pointer using the register BAR address and simplifies the call sites as the IRQ type and IRQ numbers do not have to be passed as arguments.
--- drivers/pci/endpoint/functions/pci-epf-test.c | 21 +++++++------------ 1 file changed, 8 insertions(+), 13 deletions(-)
drivers/pci/endpoint/functions/pci-epf-test.c +8 -13
@@ -607,29 +607,27 @@ static int pci_epf_test_write(struct pci_epf_test *epf_test,return ret; } -static void pci_epf_test_raise_irq (struct pci_epf_test *epf_test,u8 irq_type, -u16 irq) +static void pci_epf_test_raise_irq (struct pci_epf_test *epf_test, +struct pci_epf_test_reg *reg) {struct pci_epf *epf = epf_test->epf;struct device *dev = &epf->dev;struct pci_epc *epc = epf->epc; -enum pci_barno test_reg_bar = epf_test->test_reg_bar; -struct pci_epf_test_reg *reg = epf_test->reg[test_reg_bar]; reg->status |=STATUS_IRQ_RAISED ; -switch (irq_type) { +switch (reg->irq_type) {case IRQ_TYPE_LEGACY :pci_epc_raise_irq (epc, epf->func_no, epf->vfunc_no,PCI_EPC_IRQ_LEGACY ,0 );break ;case IRQ_TYPE_MSI :pci_epc_raise_irq (epc, epf->func_no, epf->vfunc_no, -PCI_EPC_IRQ_MSI , irq); +PCI_EPC_IRQ_MSI , reg->irq_number);break ;case IRQ_TYPE_MSIX :pci_epc_raise_irq (epc, epf->func_no, epf->vfunc_no, -PCI_EPC_IRQ_MSIX , irq); +PCI_EPC_IRQ_MSIX , reg->irq_number);break ;default :dev_err (dev,"Failed to raise IRQ, unknown type\n" );
@@ -675,8 +673,7 @@ static void pci_epf_test_cmd_handler(struct work_struct *work) reg->status |=STATUS_WRITE_FAIL ; else reg->status |= STATUS_WRITE_SUCCESS; -pci_epf_test_raise_irq (epf_test, reg->irq_type, - reg->irq_number); +pci_epf_test_raise_irq (epf_test, reg); goto reset_handler; }
@@ -686,8 +683,7 @@ static void pci_epf_test_cmd_handler(struct work_struct *work) reg->status |=STATUS_READ_SUCCESS ; else reg->status |= STATUS_READ_FAIL; -pci_epf_test_raise_irq (epf_test, reg->irq_type, - reg->irq_number); +pci_epf_test_raise_irq (epf_test, reg); goto reset_handler; }
@@ -697,8 +693,7 @@ static void pci_epf_test_cmd_handler(struct work_struct *work) reg->status |=STATUS_COPY_SUCCESS ; else reg->status |= STATUS_COPY_FAIL; -pci_epf_test_raise_irq (epf_test, reg->irq_type, - reg->irq_number); +pci_epf_test_raise_irq (epf_test, reg); goto reset_handler; }
Signature · 1 line
2.39.2
PATCH v5 8/17 PCI: epf-test: Simplify IRQ test commands execution
In reply to Damien Le MoalFor the commands COMMAND_RAISE_LEGACY_IRQ, COMMAND_RAISE_MSI_IRQ and COMMAND_RAISE_MSIX_IRQ, the function pci_epf_test_cmd_handler() sets the STATUS_IRQ_RAISED status flag and calls the epc function pci_epc_raise_irq() directly. However, this is also exactly what the pci_epf_test_raise_irq() function does. Avoid duplicating these operations by directly using pci_epf_test_raise_irq() for the IRQ test commands. It is OK to do so as the host side endpoint test driver always set the correct IRQ type for the IRQ test commands.
At the same time, the IRQ number check done for the COMMAND_RAISE_MSI_IRQ and COMMAND_RAISE_MSIX_IRQ commands can also be moved to pci_epf_test_raise_irq() to also check the IRQ number requested by the host for other test commands.
Overall, this significantly simplifies the pci_epf_test_cmd_handler() function.
--- drivers/pci/endpoint/functions/pci-epf-test.c | 43 ++++++++----------- 1 file changed, 17 insertions(+), 26 deletions(-)
drivers/pci/endpoint/functions/pci-epf-test.c +17 -26
@@ -613,6 +613,7 @@ static void pci_epf_test_raise_irq(struct pci_epf_test *epf_test,struct pci_epf *epf = epf_test->epf;struct device *dev = &epf->dev;struct pci_epc *epc = epf->epc; +int count; reg->status |=STATUS_IRQ_RAISED ;
@@ -622,10 +623,22 @@ static void pci_epf_test_raise_irq(struct pci_epf_test *epf_test, PCI_EPC_IRQ_LEGACY, 0);break ;case IRQ_TYPE_MSI : + count =pci_epc_get_msi (epc, epf->func_no, epf->vfunc_no); +if (reg->irq_number > count || count <=0 ) { +dev_err (dev,"Invalid MSI IRQ number %d / %d\n" , + reg->irq_number, count); +return ; + }pci_epc_raise_irq (epc, epf->func_no, epf->vfunc_no,PCI_EPC_IRQ_MSI , reg->irq_number);break ;case IRQ_TYPE_MSIX : + count =pci_epc_get_msix (epc, epf->func_no, epf->vfunc_no); +if (reg->irq_number > count || count <=0 ) { +dev_err (dev,"Invalid MSIX IRQ number %d / %d\n" , + reg->irq_number, count); +return ; + }pci_epc_raise_irq (epc, epf->func_no, epf->vfunc_no,PCI_EPC_IRQ_MSIX , reg->irq_number);break ;
@@ -638,13 +651,11 @@ static void pci_epf_test_raise_irq(struct pci_epf_test *epf_test,static void pci_epf_test_cmd_handler (struct work_struct *work) {int ret; -int count;u32 command; struct pci_epf_test *epf_test = container_of(work, struct pci_epf_test, cmd_handler.work);struct pci_epf *epf = epf_test->epf;struct device *dev = &epf->dev; -struct pci_epc *epc = epf->epc;enum pci_barno test_reg_bar = epf_test->test_reg_bar;struct pci_epf_test_reg *reg = epf_test->reg[test_reg_bar];
@@ -660,10 +671,10 @@ static void pci_epf_test_cmd_handler(struct work_struct *work) goto reset_handler; } -if (command &COMMAND_RAISE_LEGACY_IRQ ) { - reg->status =STATUS_IRQ_RAISED ; -pci_epc_raise_irq (epc, epf->func_no, epf->vfunc_no, -PCI_EPC_IRQ_LEGACY ,0 ); +if ((command &COMMAND_RAISE_LEGACY_IRQ ) || + (command &COMMAND_RAISE_MSI_IRQ ) || + (command &COMMAND_RAISE_MSIX_IRQ )) { +pci_epf_test_raise_irq (epf_test, reg); goto reset_handler; }
@@ -697,26 +708,6 @@ static void pci_epf_test_cmd_handler(struct work_struct *work) goto reset_handler; } -if (command &COMMAND_RAISE_MSI_IRQ ) { - count =pci_epc_get_msi (epc, epf->func_no, epf->vfunc_no); -if (reg->irq_number > count || count <=0 ) - goto reset_handler; - reg->status =STATUS_IRQ_RAISED ; -pci_epc_raise_irq (epc, epf->func_no, epf->vfunc_no, -PCI_EPC_IRQ_MSI , reg->irq_number); - goto reset_handler; - } - -if (command &COMMAND_RAISE_MSIX_IRQ ) { - count =pci_epc_get_msix (epc, epf->func_no, epf->vfunc_no); -if (reg->irq_number > count || count <=0 ) - goto reset_handler; - reg->status =STATUS_IRQ_RAISED ; -pci_epc_raise_irq (epc, epf->func_no, epf->vfunc_no, -PCI_EPC_IRQ_MSIX , reg->irq_number); - goto reset_handler; - } - reset_handler:queue_delayed_work (kpcitest_workqueue, &epf_test->cmd_handler,msecs_to_jiffies (1 ));
Signature · 1 line
2.39.2
PATCH v5 9/17 PCI: epf-test: Improve handling of command and status registers
In reply to Damien Le MoalThe pci-epf-test driver uses the test register BAR memory directly to get and execute a test registers set by the RC side and defined using a struct pci_epf_test_reg. This direct use relies on using the register BAR address as a pointer to a struct pci_epf_test_reg to execute the test case and to send back the test result through the status field of struct pci_epf_test_reg. In practice, the status field is always updated before an interrupt is raised in pci_epf_test_raise_irq(), to ensure that the RC side sees the updated status when receiving an interrupt.
However, such assignment direct access does not ensure that changes to the status register make it to memory, and so visible to the host, before an interrupt is raised, thus potentially resulting in the RC host not seeing the correct status result for a test.
Avoid this potential problem by using READ_ONCE()/WRITE_ONCE() when accessing the command and status fields of a pci_epf_test_reg structure. This ensure that a test start (pci_epf_test_cmd_handler() function) and completion (with the function pci_epf_test_raise_irq()) achieve a correct synchronization with the MMIO register accesses on the RC host.
--- drivers/pci/endpoint/functions/pci-epf-test.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-)
drivers/pci/endpoint/functions/pci-epf-test.c +9 -4
@@ -613,9 +613,14 @@ static void pci_epf_test_raise_irq(struct pci_epf_test *epf_test,struct pci_epf *epf = epf_test->epf;struct device *dev = &epf->dev;struct pci_epc *epc = epf->epc; +u32 status = reg->status |STATUS_IRQ_RAISED ;int count; - reg->status |=STATUS_IRQ_RAISED ; +/* +* Set the status before raising the IRQ to ensure that the host sees +* the updated value when it gets the IRQ. +*/ +WRITE_ONCE (reg->status, status);switch (reg->irq_type) {case IRQ_TYPE_LEGACY :
@@ -659,12 +664,12 @@ static void pci_epf_test_cmd_handler(struct work_struct *work)enum pci_barno test_reg_bar = epf_test->test_reg_bar;struct pci_epf_test_reg *reg = epf_test->reg[test_reg_bar]; - command = reg->command; + command =READ_ONCE (reg->command);if (!command) goto reset_handler; - reg->command =0 ; - reg->status =0 ; +WRITE_ONCE (reg->command,0 ); +WRITE_ONCE (reg->status,0 );if (reg->irq_type >IRQ_TYPE_MSIX ) {dev_err (dev,"Failed to detect IRQ type\n" );
Signature · 1 line
2.39.2
PATCH v5 10/17 PCI: epf-test: Cleanup pci_epf_test_cmd_handler()
In reply to Damien Le MoalCommand codes are never combined together as flags into a single value. Thus we can replace the series of "if" tests in pci_epf_test_cmd_handler() with a cleaner switch-case statement. This also allows checking that we got a valid command and print an error message if we did not.
--- drivers/pci/endpoint/functions/pci-epf-test.c | 30 +++++++++---------- 1 file changed, 14 insertions(+), 16 deletions(-)
drivers/pci/endpoint/functions/pci-epf-test.c +14 -16
@@ -676,41 +676,39 @@ static void pci_epf_test_cmd_handler(struct work_struct *work) goto reset_handler; } -if ((command &COMMAND_RAISE_LEGACY_IRQ ) || - (command &COMMAND_RAISE_MSI_IRQ ) || - (command &COMMAND_RAISE_MSIX_IRQ )) { +switch (command) { +case COMMAND_RAISE_LEGACY_IRQ : +case COMMAND_RAISE_MSI_IRQ : +case COMMAND_RAISE_MSIX_IRQ :pci_epf_test_raise_irq (epf_test, reg); - goto reset_handler; - } - -if (command &COMMAND_WRITE ) { +break ; +case COMMAND_WRITE : ret =pci_epf_test_write (epf_test, reg);if (ret) reg->status |=STATUS_WRITE_FAIL ;else reg->status |=STATUS_WRITE_SUCCESS ;pci_epf_test_raise_irq (epf_test, reg); - goto reset_handler; - } - -if (command &COMMAND_READ ) { +break ; +case COMMAND_READ : ret =pci_epf_test_read (epf_test, reg);if (!ret) reg->status |=STATUS_READ_SUCCESS ;else reg->status |=STATUS_READ_FAIL ;pci_epf_test_raise_irq (epf_test, reg); - goto reset_handler; - } - -if (command &COMMAND_COPY ) { +break ; +case COMMAND_COPY : ret =pci_epf_test_copy (epf_test, reg);if (!ret) reg->status |=STATUS_COPY_SUCCESS ;else reg->status |=STATUS_COPY_FAIL ;pci_epf_test_raise_irq (epf_test, reg); - goto reset_handler; +break ; +default : +dev_err (dev,"Invalid command 0x%x\n" , command); +break ; } reset_handler:
Signature · 1 line
2.39.2
PATCH v5 11/17 PCI: epf-test: Cleanup request result handling
In reply to Damien Le MoalEach of the test functions pci_epf_test_write(), pci_epf_test_read() and pci_epf_test_copy() return an int result which is used by pci_epf_test_cmd_handler() to set a success or error bit in the request status.
In the spirit of keeping the processing of each test case self-contained within its own test function, move the request status field update from pci_epf_test_cmd_handler() to each of these test functions and change these functions declaration to returning void.
--- drivers/pci/endpoint/functions/pci-epf-test.c | 46 +++++++++---------- 1 file changed, 21 insertions(+), 25 deletions(-)
drivers/pci/endpoint/functions/pci-epf-test.c +21 -25
@@ -325,8 +325,8 @@ static void pci_epf_test_print_rate(const char *ops, u64 size, (u64)ts.tv_sec, (u32)ts.tv_nsec, rate / 1024); } -static int pci_epf_test_copy (struct pci_epf_test *epf_test, -struct pci_epf_test_reg *reg) +static void pci_epf_test_copy (struct pci_epf_test *epf_test, +struct pci_epf_test_reg *reg) {int ret;bool use_dma;
@@ -420,11 +420,14 @@ static int pci_epf_test_copy(struct pci_epf_test *epf_test,pci_epc_mem_free_addr (epc, src_phys_addr, src_addr, reg->size); err: -return ret; +if (!ret) + reg->status |=STATUS_COPY_SUCCESS ; +else + reg->status |=STATUS_COPY_FAIL ; } -static int pci_epf_test_read (struct pci_epf_test *epf_test, -struct pci_epf_test_reg *reg) +static void pci_epf_test_read (struct pci_epf_test *epf_test, +struct pci_epf_test_reg *reg) {int ret;void __iomem *src_addr;
@@ -509,11 +512,14 @@ static int pci_epf_test_read(struct pci_epf_test *epf_test,pci_epc_mem_free_addr (epc, phys_addr, src_addr, reg->size); err: -return ret; +if (!ret) + reg->status |=STATUS_READ_SUCCESS ; +else + reg->status |=STATUS_READ_FAIL ; } -static int pci_epf_test_write (struct pci_epf_test *epf_test, -struct pci_epf_test_reg *reg) +static void pci_epf_test_write (struct pci_epf_test *epf_test, +struct pci_epf_test_reg *reg) {int ret;void __iomem *dst_addr;
@@ -604,7 +610,10 @@ static int pci_epf_test_write(struct pci_epf_test *epf_test,pci_epc_mem_free_addr (epc, phys_addr, dst_addr, reg->size); err: -return ret; +if (!ret) + reg->status |=STATUS_WRITE_SUCCESS ; +else + reg->status |=STATUS_WRITE_FAIL ; } static void pci_epf_test_raise_irq(struct pci_epf_test *epf_test,
@@ -655,7 +664,6 @@ static void pci_epf_test_raise_irq(struct pci_epf_test *epf_test,static void pci_epf_test_cmd_handler (struct work_struct *work) { -int ret;u32 command; struct pci_epf_test *epf_test = container_of(work, struct pci_epf_test, cmd_handler.work);
@@ -683,27 +691,15 @@ static void pci_epf_test_cmd_handler(struct work_struct *work)pci_epf_test_raise_irq (epf_test, reg);break ;case COMMAND_WRITE : - ret =pci_epf_test_write (epf_test, reg); -if (ret) - reg->status |=STATUS_WRITE_FAIL ; -else - reg->status |=STATUS_WRITE_SUCCESS ; +pci_epf_test_write (epf_test, reg);pci_epf_test_raise_irq (epf_test, reg);break ;case COMMAND_READ : - ret =pci_epf_test_read (epf_test, reg); -if (!ret) - reg->status |=STATUS_READ_SUCCESS ; -else - reg->status |=STATUS_READ_FAIL ; +pci_epf_test_read (epf_test, reg);pci_epf_test_raise_irq (epf_test, reg);break ;case COMMAND_COPY : - ret =pci_epf_test_copy (epf_test, reg); -if (!ret) - reg->status |=STATUS_COPY_SUCCESS ; -else - reg->status |=STATUS_COPY_FAIL ; +pci_epf_test_copy (epf_test, reg);pci_epf_test_raise_irq (epf_test, reg);break ;default :
Signature · 1 line
2.39.2
PATCH v5 12/17 PCI: epf-test: Simplify DMA support checks
In reply to Damien Le MoalThere is no need to have each read, write and copy test functions check for the FLAG_USE_DMA flag against the DMA support status indicated by epf_test->dma_supported. Move this test to the command handler function pci_epf_test_cmd_handler() to check once for all cases.
--- drivers/pci/endpoint/functions/pci-epf-test.c | 45 +++++++------------ 1 file changed, 15 insertions(+), 30 deletions(-)
drivers/pci/endpoint/functions/pci-epf-test.c +15 -30
@@ -329,7 +329,6 @@ static void pci_epf_test_copy(struct pci_epf_test *epf_test, struct pci_epf_test_reg *reg) {int ret; -bool use_dma;void __iomem *src_addr;void __iomem *dst_addr;phys_addr_t src_phys_addr;
@@ -372,14 +371,7 @@ static void pci_epf_test_copy(struct pci_epf_test *epf_test, }ktime_get_ts64 (&start); - use_dma = !!(reg->flags &FLAG_USE_DMA ); -if (use_dma) { -if (!epf_test->dma_supported) { -dev_err (dev,"Cannot transfer data using DMA\n" ); - ret = -EINVAL ; - goto err_map_addr; - } - +if (reg->flags &FLAG_USE_DMA ) {if (epf_test->dma_private) {dev_err (dev,"Cannot transfer data using DMA\n" ); ret = -EINVAL ;
@@ -405,7 +397,8 @@ static void pci_epf_test_copy(struct pci_epf_test *epf_test,kfree (buf); }ktime_get_ts64 (&end); -pci_epf_test_print_rate ("COPY" , reg->size, &start, &end, use_dma); +pci_epf_test_print_rate ("COPY" , reg->size, &start, &end, + reg->flags &FLAG_USE_DMA ); err_map_addr:pci_epc_unmap_addr (epc, epf->func_no, epf->vfunc_no, dst_phys_addr);
@@ -433,7 +426,6 @@ static void pci_epf_test_read(struct pci_epf_test *epf_test,void __iomem *src_addr;void *buf;u32 crc32; -bool use_dma;phys_addr_t phys_addr;phys_addr_t dst_phys_addr;struct timespec64 start, end;
@@ -464,14 +456,7 @@ static void pci_epf_test_read(struct pci_epf_test *epf_test, goto err_map_addr; } - use_dma = !!(reg->flags &FLAG_USE_DMA ); -if (use_dma) { -if (!epf_test->dma_supported) { -dev_err (dev,"Cannot transfer data using DMA\n" ); - ret = -EINVAL ; - goto err_dma_map; - } - +if (reg->flags &FLAG_USE_DMA ) { dst_phys_addr =dma_map_single (dma_dev, buf, reg->size,DMA_FROM_DEVICE );if (dma_mapping_error (dma_dev, dst_phys_addr)) {
@@ -496,7 +481,8 @@ static void pci_epf_test_read(struct pci_epf_test *epf_test,ktime_get_ts64 (&end); } -pci_epf_test_print_rate ("READ" , reg->size, &start, &end, use_dma); +pci_epf_test_print_rate ("READ" , reg->size, &start, &end, + reg->flags &FLAG_USE_DMA ); crc32 =crc32_le (~0 , buf, reg->size);if (crc32 != reg->checksum)
@@ -524,7 +510,6 @@ static void pci_epf_test_write(struct pci_epf_test *epf_test,int ret;void __iomem *dst_addr;void *buf; -bool use_dma;phys_addr_t phys_addr;phys_addr_t src_phys_addr;struct timespec64 start, end;
@@ -558,14 +543,7 @@ static void pci_epf_test_write(struct pci_epf_test *epf_test,get_random_bytes (buf, reg->size); reg->checksum =crc32_le (~0 , buf, reg->size); - use_dma = !!(reg->flags &FLAG_USE_DMA ); -if (use_dma) { -if (!epf_test->dma_supported) { -dev_err (dev,"Cannot transfer data using DMA\n" ); - ret = -EINVAL ; - goto err_dma_map; - } - +if (reg->flags &FLAG_USE_DMA ) { src_phys_addr =dma_map_single (dma_dev, buf, reg->size,DMA_TO_DEVICE );if (dma_mapping_error (dma_dev, src_phys_addr)) {
@@ -592,7 +570,8 @@ static void pci_epf_test_write(struct pci_epf_test *epf_test,ktime_get_ts64 (&end); } -pci_epf_test_print_rate ("WRITE" , reg->size, &start, &end, use_dma); +pci_epf_test_print_rate ("WRITE" , reg->size, &start, &end, + reg->flags &FLAG_USE_DMA );/* * wait 1ms inorder for the write to complete. Without this delay L3
@@ -679,6 +658,12 @@ static void pci_epf_test_cmd_handler(struct work_struct *work)WRITE_ONCE (reg->command,0 );WRITE_ONCE (reg->status,0 ); +if ((READ_ONCE (reg->flags) &FLAG_USE_DMA ) && + !epf_test->dma_supported) { +dev_err (dev,"Cannot transfer data using DMA\n" ); + goto reset_handler; + } +if (reg->irq_type >IRQ_TYPE_MSIX ) {dev_err (dev,"Failed to detect IRQ type\n" ); goto reset_handler;
Signature · 1 line
2.39.2
PATCH v5 13/17 PCI: epf-test: Simplify transfers result print
In reply to Damien Le MoalIn pci_epf_test_print_rate(), instead of open coding a reduction loop to allow for a division by a 32-bits ns value, simply use div64_u64() to calculate the transfer rate. To match the printed unit of KB/s, this calculation divides the rate by 1000 instead of 1024 (that would be KiB/s unit).
Change the format of the results printed by pci_epf_test_print_rate() to be more compact without the double new line. Also use dev_info() instead of pr_info().
--- drivers/pci/endpoint/functions/pci-epf-test.c | 39 +++++++------------ 1 file changed, 14 insertions(+), 25 deletions(-)
drivers/pci/endpoint/functions/pci-epf-test.c +14 -25
@@ -295,34 +295,23 @@ static void pci_epf_test_clean_dma_chan(struct pci_epf_test *epf_test)return ; } -static void pci_epf_test_print_rate (const char *ops,u64 size, +static void pci_epf_test_print_rate (struct pci_epf_test *epf_test, +const char *op,u64 size,struct timespec64 *start,struct timespec64 *end,bool dma) { -struct timespec64 ts; -u64 rate, ns; - - ts =timespec64_sub (*end, *start); - -/* convert both size (stored in 'rate') and time in terms of 'ns' */ - ns =timespec64_to_ns (&ts); - rate = size *NSEC_PER_SEC ; - -/* Divide both size (stored in 'rate') and ns by a common factor */ -while (ns >UINT_MAX ) { - rate >>=1 ; - ns >>=1 ; - } - -if (!ns) -return ; +struct timespec64 ts =timespec64_sub (*end, *start); +u64 rate =0 , ns;/* calculate the rate */ -do_div (rate, (uint32_t )ns); + ns =timespec64_to_ns (&ts); +if (ns) + rate =div64_u64 (size *NSEC_PER_SEC , ns *1000 ); -pr_info ("\n%s => Size: %llu bytes\t DMA: %s\t Time: %llu.%09u seconds\t" -"Rate: %llu KB/s\n" , ops, size, dma ?"YES" :"NO" , - (u64 )ts.tv_sec, (u32 )ts.tv_nsec, rate /1024 ); +dev_info (&epf_test->epf->dev, +"%s => Size: %llu B, DMA: %s, Time: %llu.%09u s, Rate: %llu KB/s\n" , + op, size, dma ?"YES" :"NO" , + (u64 )ts.tv_sec, (u32 )ts.tv_nsec, rate); } static void pci_epf_test_copy(struct pci_epf_test *epf_test,
@@ -397,7 +386,7 @@ static void pci_epf_test_copy(struct pci_epf_test *epf_test,kfree (buf); }ktime_get_ts64 (&end); -pci_epf_test_print_rate ("COPY" , reg->size, &start, &end, +pci_epf_test_print_rate (epf_test,"COPY" , reg->size, &start, &end, reg->flags &FLAG_USE_DMA ); err_map_addr:
@@ -481,7 +470,7 @@ static void pci_epf_test_read(struct pci_epf_test *epf_test,ktime_get_ts64 (&end); } -pci_epf_test_print_rate ("READ" , reg->size, &start, &end, +pci_epf_test_print_rate (epf_test,"READ" , reg->size, &start, &end, reg->flags &FLAG_USE_DMA ); crc32 =crc32_le (~0 , buf, reg->size);
@@ -570,7 +559,7 @@ static void pci_epf_test_write(struct pci_epf_test *epf_test,ktime_get_ts64 (&end); } -pci_epf_test_print_rate ("WRITE" , reg->size, &start, &end, +pci_epf_test_print_rate (epf_test,"WRITE" , reg->size, &start, &end, reg->flags &FLAG_USE_DMA );/*
Signature · 1 line
2.39.2
PATCH v5 14/17 misc: pci_endpoint_test: Free IRQs before removing the device
In reply to Damien Le MoalIn pci_endpoint_test_remove(), freeing the IRQs after removing the device creates a small race window for IRQs to be received with the test device memory already released, causing the IRQ handler to access invalid memory, resulting in an oops.
Free the device IRQs before removing the device to avoid this issue.
--- drivers/misc/pci_endpoint_test.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
drivers/misc/pci_endpoint_test.c +3 -3
@@ -938,6 +938,9 @@ static void pci_endpoint_test_remove(struct pci_dev *pdev)if (id <0 )return ; +pci_endpoint_test_release_irq (test); +pci_endpoint_test_free_irq_vectors (test); +misc_deregister (&test->miscdev);kfree (misc_device->name);kfree (test->name);
@@ -947,9 +950,6 @@ static void pci_endpoint_test_remove(struct pci_dev *pdev)pci_iounmap (pdev, test->bar[bar]); } -pci_endpoint_test_release_irq (test); -pci_endpoint_test_free_irq_vectors (test); -pci_release_regions (pdev);pci_disable_device (pdev); }
Signature · 1 line
2.39.2
PATCH v5 15/17 misc: pci_endpoint_test: Re-init completion for every test
In reply to Damien Le MoalThe irq_raised completion used to detect the end of a test case is initialized when the test device is probed, but never reinitialized again before a test case. As a result, the irq_raised completion synchronization is effective only for the first ioctl test case executed. Any subsequent call to wait_for_completion() by another ioctl() call will immediately return, potentially too early, leading to false positive failures.
Fix this by reinitializing the irq_raised completion before starting a new ioctl() test command.
--- drivers/misc/pci_endpoint_test.c | 4 ++++ 1 file changed, 4 insertions(+)
drivers/misc/pci_endpoint_test.c +4 -0
@@ -729,6 +729,10 @@ static long pci_endpoint_test_ioctl(struct file *file, unsigned int cmd,struct pci_dev *pdev = test->pdev;mutex_lock (&test->mutex); + +reinit_completion (&test->irq_raised); + test->last_irq = -ENODATA ; +switch (cmd) {case PCITEST_BAR : bar = arg;
Signature · 1 line
2.39.2
PATCH v5 16/17 misc: pci_endpoint_test: Do not write status in IRQ handler
In reply to Damien Le Moalpci_endpoint_test_irqhandler() always rewrites the status register when an IRQ is raised, either as-is if STATUS_IRQ_RAISED is not set, or with STATUS_IRQ_RAISED cleared if that flag is set. The first case creates a race window with the endpoint side, meaning that the host side test driver may end up reading what it just wrote, thus losing the real status as set by the endpoint side before raising the next interrupt. This can prevent detecting that the STATUS_IRQ_RAISED flag was set by the endpoint.
Remove this race window by not clearing the STATUS_IRQ_RAISED status flag and not rewriting that register for every IRQ received.
--- drivers/misc/pci_endpoint_test.c | 3 --- 1 file changed, 3 deletions(-)
drivers/misc/pci_endpoint_test.c +0 -3
@@ -159,10 +159,7 @@ static irqreturn_t pci_endpoint_test_irqhandler(int irq, void *dev_id)if (reg &STATUS_IRQ_RAISED ) { test->last_irq = irq;complete (&test->irq_raised); - reg &= ~STATUS_IRQ_RAISED ; } -pci_endpoint_test_writel (test,PCI_ENDPOINT_TEST_STATUS , - reg);return IRQ_HANDLED ; }
Signature · 1 line
2.39.2
PATCH v5 17/17 misc: pci_endpoint_test: Simplify pci_endpoint_test_msi_irq()
In reply to Damien Le MoalSimplify the code of pci_endpoint_test_msi_irq() by correctly using
booleans: remove the msix comparison to false as that variable is already a boolean, and directly return the result of the comparison of the raised interrupt number.
--- drivers/misc/pci_endpoint_test.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-)
drivers/misc/pci_endpoint_test.c +4 -8
@@ -313,21 +313,17 @@ static bool pci_endpoint_test_msi_irq(struct pci_endpoint_test *test,struct pci_dev *pdev = test->pdev;pci_endpoint_test_writel (test,PCI_ENDPOINT_TEST_IRQ_TYPE , - msix == false ?IRQ_TYPE_MSI : -IRQ_TYPE_MSIX ); + msix ?IRQ_TYPE_MSIX :IRQ_TYPE_MSI );pci_endpoint_test_writel (test,PCI_ENDPOINT_TEST_IRQ_NUMBER , msi_num);pci_endpoint_test_writel (test,PCI_ENDPOINT_TEST_COMMAND , - msix == false ?COMMAND_RAISE_MSI_IRQ : -COMMAND_RAISE_MSIX_IRQ ); + msix ?COMMAND_RAISE_MSIX_IRQ : +COMMAND_RAISE_MSI_IRQ ); val =wait_for_completion_timeout (&test->irq_raised,msecs_to_jiffies (1000 ));if (!val)return false; -if (pci_irq_vector (pdev, msi_num -1 ) == test->last_irq) -return true; - -return false; +return pci_irq_vector (pdev, msi_num -1 ) == test->last_irq; } static int pci_endpoint_test_validate_xfer_params(struct device *dev,
Signature · 1 line
2.39.2
Lorenzo Pieralisi
AppliedQuoted 9 lines · Damien Le Moal
This series fixes several issues with the PCI endpoint code and endpoint test drivers (RC side and EP side).
The first 2 patches address an issue with the use of configfs to create an endpoint driver type attributes group, preventing a potential crash if the user creates a directory multiple times for the driver type attributes.
[...]
Applied to controller/endpoint, thanks!
[01/17] PCI: endpoint: Automatically create a function specific attributes group
https://git.kernel.org/pci/pci/c/8dcae97d9819 [02/17] PCI: endpoint: Move pci_epf_type_add_cfs() code https://git.kernel.org/pci/pci/c/f373bc7cee2a [03/17] PCI: epf-test: Fix DMA transfer completion initialization https://git.kernel.org/pci/pci/c/8b60b96dc008 [04/17] PCI: epf-test: Fix DMA transfer completion detection https://git.kernel.org/pci/pci/c/5628aaabe60e [05/17] PCI: epf-test: Use dmaengine_submit() to initiate DMA transfer https://git.kernel.org/pci/pci/c/62d58b868ebe [06/17] PCI: epf-test: Simplify read/write/copy test functions https://git.kernel.org/pci/pci/c/8b5609b95b7b [07/17] PCI: epf-test: Simplify pci_epf_test_raise_irq() https://git.kernel.org/pci/pci/c/508f488b0913 [08/17] PCI: epf-test: Simplify IRQ test commands execution https://git.kernel.org/pci/pci/c/82e72fc97901 [09/17] PCI: epf-test: Improve handling of command and status registers https://git.kernel.org/pci/pci/c/ad15592c26d5 [10/17] PCI: epf-test: Cleanup pci_epf_test_cmd_handler() https://git.kernel.org/pci/pci/c/0674535d28a8 [11/17] PCI: epf-test: Cleanup request result handling https://git.kernel.org/pci/pci/c/00b225e73f30 [12/17] PCI: epf-test: Simplify DMA support checks https://git.kernel.org/pci/pci/c/64cedb06b1d9 [13/17] PCI: epf-test: Simplify transfers result print https://git.kernel.org/pci/pci/c/e68f5440ffb6 [14/17] misc: pci_endpoint_test: Free IRQs before removing the device https://git.kernel.org/pci/pci/c/0ca8e07fc751 [15/17] misc: pci_endpoint_test: Re-init completion for every test https://git.kernel.org/pci/pci/c/259456ae2804 [16/17] misc: pci_endpoint_test: Do not write status in IRQ handler https://git.kernel.org/pci/pci/c/53a52a0d213e [17/17] misc: pci_endpoint_test: Simplify pci_endpoint_test_msi_irq() https://git.kernel.org/pci/pci/c/7885dbeb8780
Thanks,
Lorenzo