Commit e321b10b authored by Bjorn Helgaas's avatar Bjorn Helgaas
Browse files

Merge branch 'pci/endpoint-test'

- Clear pci-epf-test dma_chan_rx, not dma_chan_tx, after freeing
  dma_chan_rx (Mohamed Khalfella)

- Correct the DMA MEMCPY test so it doesn't fail if the Endpoint supports
  both DMA_PRIVATE and DMA_MEMCPY (Manivannan Sadhasivam)

- Add pci-epf-test and pci_endpoint_test support for capabilities (Niklas
  Cassel)

- Add Endpoint test for consecutive BARs (Niklas Cassel)

- Remove redundant comparison from Endpoint BAR test because a > 1MB BAR
  can always be exactly covered by iterating with a 1MB buffer (Hans Zhang)

- Correct the PCI Endpoint test IOCTL return value (Manivannan Sadhasivam)

- Move PCI Endpoint tests from tools/pci to Kselftests (Manivannan
  Sadhasivam)

- Convert PCI Endpoint tests to the Kselftest framework (Manivannan
  Sadhasivam)

* pci/endpoint-test:
  selftests: pci_endpoint: Migrate to Kselftest framework
  selftests: Move PCI Endpoint tests from tools/pci to Kselftests
  misc: pci_endpoint_test: Fix IOCTL return value
  misc: pci_endpoint_test: Remove redundant 'remainder' test
  misc: pci_endpoint_test: Add consecutive BAR test
  misc: pci_endpoint_test: Add support for capabilities
  PCI: endpoint: pci-epf-test: Add support for capabilities
  PCI: endpoint: pci-epf-test: Fix check for DMA MEMCPY test
  PCI: endpoint: pci-epf-test: Set dma_chan_rx pointer to NULL on error
parents 74855f66 392188bb
Loading
Loading
Loading
Loading
+72 −102
Original line number Diff line number Diff line
@@ -81,8 +81,8 @@ device, the following commands can be used::

	# echo 0x104c > functions/pci_epf_test/func1/vendorid
	# echo 0xb500 > functions/pci_epf_test/func1/deviceid
	# echo 16 > functions/pci_epf_test/func1/msi_interrupts
	# echo 8 > functions/pci_epf_test/func1/msix_interrupts
	# echo 32 > functions/pci_epf_test/func1/msi_interrupts
	# echo 2048 > functions/pci_epf_test/func1/msix_interrupts


Binding pci-epf-test Device to EP Controller
@@ -123,113 +123,83 @@ above::
Using Endpoint Test function Device
-----------------------------------

pcitest.sh added in tools/pci/ can be used to run all the default PCI endpoint
tests. To compile this tool the following commands should be used::
Kselftest added in tools/testing/selftests/pci_endpoint can be used to run all
the default PCI endpoint tests. To build the Kselftest for PCI endpoint
subsystem, the following commands should be used::

	# cd <kernel-dir>
	# make -C tools/pci
	# make -C tools/testing/selftests/pci_endpoint

or if you desire to compile and install in your system::

	# cd <kernel-dir>
	# make -C tools/pci install
	# make -C tools/testing/selftests/pci_endpoint INSTALL_PATH=/usr/bin install

The tool and script will be located in <rootfs>/usr/bin/
The test will be located in <rootfs>/usr/bin/


pcitest.sh Output
~~~~~~~~~~~~~~~~~
Kselftest Output
~~~~~~~~~~~~~~~~
::

	# pcitest.sh
	BAR tests

	BAR0:           OKAY
	BAR1:           OKAY
	BAR2:           OKAY
	BAR3:           OKAY
	BAR4:           NOT OKAY
	BAR5:           NOT OKAY

	Interrupt tests

	SET IRQ TYPE TO LEGACY:         OKAY
	LEGACY IRQ:     NOT OKAY
	SET IRQ TYPE TO MSI:            OKAY
	MSI1:           OKAY
	MSI2:           OKAY
	MSI3:           OKAY
	MSI4:           OKAY
	MSI5:           OKAY
	MSI6:           OKAY
	MSI7:           OKAY
	MSI8:           OKAY
	MSI9:           OKAY
	MSI10:          OKAY
	MSI11:          OKAY
	MSI12:          OKAY
	MSI13:          OKAY
	MSI14:          OKAY
	MSI15:          OKAY
	MSI16:          OKAY
	MSI17:          NOT OKAY
	MSI18:          NOT OKAY
	MSI19:          NOT OKAY
	MSI20:          NOT OKAY
	MSI21:          NOT OKAY
	MSI22:          NOT OKAY
	MSI23:          NOT OKAY
	MSI24:          NOT OKAY
	MSI25:          NOT OKAY
	MSI26:          NOT OKAY
	MSI27:          NOT OKAY
	MSI28:          NOT OKAY
	MSI29:          NOT OKAY
	MSI30:          NOT OKAY
	MSI31:          NOT OKAY
	MSI32:          NOT OKAY
	SET IRQ TYPE TO MSI-X:          OKAY
	MSI-X1:         OKAY
	MSI-X2:         OKAY
	MSI-X3:         OKAY
	MSI-X4:         OKAY
	MSI-X5:         OKAY
	MSI-X6:         OKAY
	MSI-X7:         OKAY
	MSI-X8:         OKAY
	MSI-X9:         NOT OKAY
	MSI-X10:        NOT OKAY
	MSI-X11:        NOT OKAY
	MSI-X12:        NOT OKAY
	MSI-X13:        NOT OKAY
	MSI-X14:        NOT OKAY
	MSI-X15:        NOT OKAY
	MSI-X16:        NOT OKAY
	[...]
	MSI-X2047:      NOT OKAY
	MSI-X2048:      NOT OKAY

	Read Tests

	SET IRQ TYPE TO MSI:            OKAY
	READ (      1 bytes):           OKAY
	READ (   1024 bytes):           OKAY
	READ (   1025 bytes):           OKAY
	READ (1024000 bytes):           OKAY
	READ (1024001 bytes):           OKAY

	Write Tests

	WRITE (      1 bytes):          OKAY
	WRITE (   1024 bytes):          OKAY
	WRITE (   1025 bytes):          OKAY
	WRITE (1024000 bytes):          OKAY
	WRITE (1024001 bytes):          OKAY

	Copy Tests

	COPY (      1 bytes):           OKAY
	COPY (   1024 bytes):           OKAY
	COPY (   1025 bytes):           OKAY
	COPY (1024000 bytes):           OKAY
	COPY (1024001 bytes):           OKAY
	# pci_endpoint_test
	TAP version 13
	1..16
	# Starting 16 tests from 9 test cases.
	#  RUN           pci_ep_bar.BAR0.BAR_TEST ...
	#            OK  pci_ep_bar.BAR0.BAR_TEST
	ok 1 pci_ep_bar.BAR0.BAR_TEST
	#  RUN           pci_ep_bar.BAR1.BAR_TEST ...
	#            OK  pci_ep_bar.BAR1.BAR_TEST
	ok 2 pci_ep_bar.BAR1.BAR_TEST
	#  RUN           pci_ep_bar.BAR2.BAR_TEST ...
	#            OK  pci_ep_bar.BAR2.BAR_TEST
	ok 3 pci_ep_bar.BAR2.BAR_TEST
	#  RUN           pci_ep_bar.BAR3.BAR_TEST ...
	#            OK  pci_ep_bar.BAR3.BAR_TEST
	ok 4 pci_ep_bar.BAR3.BAR_TEST
	#  RUN           pci_ep_bar.BAR4.BAR_TEST ...
	#            OK  pci_ep_bar.BAR4.BAR_TEST
	ok 5 pci_ep_bar.BAR4.BAR_TEST
	#  RUN           pci_ep_bar.BAR5.BAR_TEST ...
	#            OK  pci_ep_bar.BAR5.BAR_TEST
	ok 6 pci_ep_bar.BAR5.BAR_TEST
	#  RUN           pci_ep_basic.CONSECUTIVE_BAR_TEST ...
	#            OK  pci_ep_basic.CONSECUTIVE_BAR_TEST
	ok 7 pci_ep_basic.CONSECUTIVE_BAR_TEST
	#  RUN           pci_ep_basic.LEGACY_IRQ_TEST ...
	#            OK  pci_ep_basic.LEGACY_IRQ_TEST
	ok 8 pci_ep_basic.LEGACY_IRQ_TEST
	#  RUN           pci_ep_basic.MSI_TEST ...
	#            OK  pci_ep_basic.MSI_TEST
	ok 9 pci_ep_basic.MSI_TEST
	#  RUN           pci_ep_basic.MSIX_TEST ...
	#            OK  pci_ep_basic.MSIX_TEST
	ok 10 pci_ep_basic.MSIX_TEST
	#  RUN           pci_ep_data_transfer.memcpy.READ_TEST ...
	#            OK  pci_ep_data_transfer.memcpy.READ_TEST
	ok 11 pci_ep_data_transfer.memcpy.READ_TEST
	#  RUN           pci_ep_data_transfer.memcpy.WRITE_TEST ...
	#            OK  pci_ep_data_transfer.memcpy.WRITE_TEST
	ok 12 pci_ep_data_transfer.memcpy.WRITE_TEST
	#  RUN           pci_ep_data_transfer.memcpy.COPY_TEST ...
	#            OK  pci_ep_data_transfer.memcpy.COPY_TEST
	ok 13 pci_ep_data_transfer.memcpy.COPY_TEST
	#  RUN           pci_ep_data_transfer.dma.READ_TEST ...
	#            OK  pci_ep_data_transfer.dma.READ_TEST
	ok 14 pci_ep_data_transfer.dma.READ_TEST
	#  RUN           pci_ep_data_transfer.dma.WRITE_TEST ...
	#            OK  pci_ep_data_transfer.dma.WRITE_TEST
	ok 15 pci_ep_data_transfer.dma.WRITE_TEST
	#  RUN           pci_ep_data_transfer.dma.COPY_TEST ...
	#            OK  pci_ep_data_transfer.dma.COPY_TEST
	ok 16 pci_ep_data_transfer.dma.COPY_TEST
	# PASSED: 16 / 16 tests passed.
	# Totals: pass:16 fail:0 xfail:0 xpass:0 skip:0 error:0


Testcase 16 (pci_ep_data_transfer.dma.COPY_TEST) will fail for most of the DMA
capable endpoint controllers due to the absence of the MEMCPY over DMA. For such
controllers, it is advisable to skip this testcase using this
command::

	# pci_endpoint_test -f pci_ep_bar -f pci_ep_basic -v memcpy -T COPY_TEST -v dma
+1 −1
Original line number Diff line number Diff line
@@ -18003,7 +18003,7 @@ F: Documentation/PCI/endpoint/*
F:	Documentation/misc-devices/pci-endpoint-test.rst
F:	drivers/misc/pci_endpoint_test.c
F:	drivers/pci/endpoint/
F:	tools/pci/
F:	tools/testing/selftests/pci_endpoint/
PCI ENHANCED ERROR HANDLING (EEH) FOR POWERPC
M:	Mahesh J Salgaonkar <mahesh@linux.ibm.com>
+226 −126
Original line number Diff line number Diff line
@@ -69,6 +69,9 @@
#define PCI_ENDPOINT_TEST_FLAGS			0x2c
#define FLAG_USE_DMA				BIT(0)

#define PCI_ENDPOINT_TEST_CAPS			0x30
#define CAP_UNALIGNED_ACCESS			BIT(0)

#define PCI_DEVICE_ID_TI_AM654			0xb00c
#define PCI_DEVICE_ID_TI_J7200			0xb00f
#define PCI_DEVICE_ID_TI_AM64			0xb010
@@ -166,43 +169,47 @@ static void pci_endpoint_test_free_irq_vectors(struct pci_endpoint_test *test)
	test->irq_type = IRQ_TYPE_UNDEFINED;
}

static bool pci_endpoint_test_alloc_irq_vectors(struct pci_endpoint_test *test,
static int pci_endpoint_test_alloc_irq_vectors(struct pci_endpoint_test *test,
						int type)
{
	int irq = -1;
	int irq;
	struct pci_dev *pdev = test->pdev;
	struct device *dev = &pdev->dev;
	bool res = true;

	switch (type) {
	case IRQ_TYPE_INTX:
		irq = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_INTX);
		if (irq < 0)
		if (irq < 0) {
			dev_err(dev, "Failed to get Legacy interrupt\n");
			return irq;
		}

		break;
	case IRQ_TYPE_MSI:
		irq = pci_alloc_irq_vectors(pdev, 1, 32, PCI_IRQ_MSI);
		if (irq < 0)
		if (irq < 0) {
			dev_err(dev, "Failed to get MSI interrupts\n");
			return irq;
		}

		break;
	case IRQ_TYPE_MSIX:
		irq = pci_alloc_irq_vectors(pdev, 1, 2048, PCI_IRQ_MSIX);
		if (irq < 0)
		if (irq < 0) {
			dev_err(dev, "Failed to get MSI-X interrupts\n");
			return irq;
		}

		break;
	default:
		dev_err(dev, "Invalid IRQ type selected\n");
	}

	if (irq < 0) {
		irq = 0;
		res = false;
		return -EINVAL;
	}

	test->irq_type = type;
	test->num_irqs = irq;

	return res;
	return 0;
}

static void pci_endpoint_test_release_irq(struct pci_endpoint_test *test)
@@ -217,22 +224,22 @@ static void pci_endpoint_test_release_irq(struct pci_endpoint_test *test)
	test->num_irqs = 0;
}

static bool pci_endpoint_test_request_irq(struct pci_endpoint_test *test)
static int pci_endpoint_test_request_irq(struct pci_endpoint_test *test)
{
	int i;
	int err;
	int ret;
	struct pci_dev *pdev = test->pdev;
	struct device *dev = &pdev->dev;

	for (i = 0; i < test->num_irqs; i++) {
		err = devm_request_irq(dev, pci_irq_vector(pdev, i),
		ret = devm_request_irq(dev, pci_irq_vector(pdev, i),
				       pci_endpoint_test_irqhandler,
				       IRQF_SHARED, test->name, test);
		if (err)
		if (ret)
			goto fail;
	}

	return true;
	return 0;

fail:
	switch (irq_type) {
@@ -252,7 +259,7 @@ static bool pci_endpoint_test_request_irq(struct pci_endpoint_test *test)
		break;
	}

	return false;
	return ret;
}

static const u32 bar_test_pattern[] = {
@@ -277,16 +284,16 @@ static int pci_endpoint_test_bar_memcmp(struct pci_endpoint_test *test,
	return memcmp(write_buf, read_buf, size);
}

static bool pci_endpoint_test_bar(struct pci_endpoint_test *test,
static int pci_endpoint_test_bar(struct pci_endpoint_test *test,
				  enum pci_barno barno)
{
	int j, bar_size, buf_size, iters, remain;
	int j, bar_size, buf_size, iters;
	void *write_buf __free(kfree) = NULL;
	void *read_buf __free(kfree) = NULL;
	struct pci_dev *pdev = test->pdev;

	if (!test->bar[barno])
		return false;
		return -ENOMEM;

	bar_size = pci_resource_len(pdev, barno);

@@ -301,28 +308,105 @@ static bool pci_endpoint_test_bar(struct pci_endpoint_test *test,

	write_buf = kmalloc(buf_size, GFP_KERNEL);
	if (!write_buf)
		return false;
		return -ENOMEM;

	read_buf = kmalloc(buf_size, GFP_KERNEL);
	if (!read_buf)
		return false;
		return -ENOMEM;

	iters = bar_size / buf_size;
	for (j = 0; j < iters; j++)
		if (pci_endpoint_test_bar_memcmp(test, barno, buf_size * j,
						 write_buf, read_buf, buf_size))
			return false;
			return -EIO;

	return 0;
}

	remain = bar_size % buf_size;
	if (remain)
		if (pci_endpoint_test_bar_memcmp(test, barno, buf_size * iters,
						 write_buf, read_buf, remain))
			return false;
static u32 bar_test_pattern_with_offset(enum pci_barno barno, int offset)
{
	u32 val;

	return true;
	/* Keep the BAR pattern in the top byte. */
	val = bar_test_pattern[barno] & 0xff000000;
	/* Store the (partial) offset in the remaining bytes. */
	val |= offset & 0x00ffffff;

	return val;
}

static bool pci_endpoint_test_intx_irq(struct pci_endpoint_test *test)
static void pci_endpoint_test_bars_write_bar(struct pci_endpoint_test *test,
					     enum pci_barno barno)
{
	struct pci_dev *pdev = test->pdev;
	int j, size;

	size = pci_resource_len(pdev, barno);

	if (barno == test->test_reg_bar)
		size = 0x4;

	for (j = 0; j < size; j += 4)
		writel_relaxed(bar_test_pattern_with_offset(barno, j),
			       test->bar[barno] + j);
}

static int pci_endpoint_test_bars_read_bar(struct pci_endpoint_test *test,
					    enum pci_barno barno)
{
	struct pci_dev *pdev = test->pdev;
	struct device *dev = &pdev->dev;
	int j, size;
	u32 val;

	size = pci_resource_len(pdev, barno);

	if (barno == test->test_reg_bar)
		size = 0x4;

	for (j = 0; j < size; j += 4) {
		u32 expected = bar_test_pattern_with_offset(barno, j);

		val = readl_relaxed(test->bar[barno] + j);
		if (val != expected) {
			dev_err(dev,
				"BAR%d incorrect data at offset: %#x, got: %#x expected: %#x\n",
				barno, j, val, expected);
			return -EIO;
		}
	}

	return 0;
}

static int pci_endpoint_test_bars(struct pci_endpoint_test *test)
{
	enum pci_barno bar;
	bool ret;

	/* Write all BARs in order (without reading). */
	for (bar = 0; bar < PCI_STD_NUM_BARS; bar++)
		if (test->bar[bar])
			pci_endpoint_test_bars_write_bar(test, bar);

	/*
	 * Read all BARs in order (without writing).
	 * If there is an address translation issue on the EP, writing one BAR
	 * might have overwritten another BAR. Ensure that this is not the case.
	 * (Reading back the BAR directly after writing can not detect this.)
	 */
	for (bar = 0; bar < PCI_STD_NUM_BARS; bar++) {
		if (test->bar[bar]) {
			ret = pci_endpoint_test_bars_read_bar(test, bar);
			if (!ret)
				return ret;
		}
	}

	return 0;
}

static int pci_endpoint_test_intx_irq(struct pci_endpoint_test *test)
{
	u32 val;

@@ -334,16 +418,17 @@ static bool pci_endpoint_test_intx_irq(struct pci_endpoint_test *test)
	val = wait_for_completion_timeout(&test->irq_raised,
					  msecs_to_jiffies(1000));
	if (!val)
		return false;
		return -ETIMEDOUT;

	return true;
	return 0;
}

static bool pci_endpoint_test_msi_irq(struct pci_endpoint_test *test,
static int pci_endpoint_test_msi_irq(struct pci_endpoint_test *test,
				       u16 msi_num, bool msix)
{
	u32 val;
	struct pci_dev *pdev = test->pdev;
	u32 val;
	int ret;

	pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_TYPE,
				 msix ? IRQ_TYPE_MSIX : IRQ_TYPE_MSI);
@@ -354,9 +439,16 @@ static bool pci_endpoint_test_msi_irq(struct pci_endpoint_test *test,
	val = wait_for_completion_timeout(&test->irq_raised,
					  msecs_to_jiffies(1000));
	if (!val)
		return false;
		return -ETIMEDOUT;

	return pci_irq_vector(pdev, msi_num - 1) == test->last_irq;
	ret = pci_irq_vector(pdev, msi_num - 1);
	if (ret < 0)
		return ret;

	if (ret != test->last_irq)
		return -EIO;

	return 0;
}

static int pci_endpoint_test_validate_xfer_params(struct device *dev,
@@ -375,11 +467,10 @@ static int pci_endpoint_test_validate_xfer_params(struct device *dev,
	return 0;
}

static bool pci_endpoint_test_copy(struct pci_endpoint_test *test,
static int pci_endpoint_test_copy(struct pci_endpoint_test *test,
				   unsigned long arg)
{
	struct pci_endpoint_test_xfer_param param;
	bool ret = false;
	void *src_addr;
	void *dst_addr;
	u32 flags = 0;
@@ -398,17 +489,17 @@ static bool pci_endpoint_test_copy(struct pci_endpoint_test *test,
	int irq_type = test->irq_type;
	u32 src_crc32;
	u32 dst_crc32;
	int err;
	int ret;

	err = copy_from_user(&param, (void __user *)arg, sizeof(param));
	if (err) {
	ret = copy_from_user(&param, (void __user *)arg, sizeof(param));
	if (ret) {
		dev_err(dev, "Failed to get transfer param\n");
		return false;
		return -EFAULT;
	}

	err = pci_endpoint_test_validate_xfer_params(dev, &param, alignment);
	if (err)
		return false;
	ret = pci_endpoint_test_validate_xfer_params(dev, &param, alignment);
	if (ret)
		return ret;

	size = param.size;

@@ -418,22 +509,21 @@ static bool pci_endpoint_test_copy(struct pci_endpoint_test *test,

	if (irq_type < IRQ_TYPE_INTX || irq_type > IRQ_TYPE_MSIX) {
		dev_err(dev, "Invalid IRQ type option\n");
		goto err;
		return -EINVAL;
	}

	orig_src_addr = kzalloc(size + alignment, GFP_KERNEL);
	if (!orig_src_addr) {
		dev_err(dev, "Failed to allocate source buffer\n");
		ret = false;
		goto err;
		return -ENOMEM;
	}

	get_random_bytes(orig_src_addr, size + alignment);
	orig_src_phys_addr = dma_map_single(dev, orig_src_addr,
					    size + alignment, DMA_TO_DEVICE);
	if (dma_mapping_error(dev, orig_src_phys_addr)) {
	ret = dma_mapping_error(dev, orig_src_phys_addr);
	if (ret) {
		dev_err(dev, "failed to map source buffer address\n");
		ret = false;
		goto err_src_phys_addr;
	}

@@ -457,15 +547,15 @@ static bool pci_endpoint_test_copy(struct pci_endpoint_test *test,
	orig_dst_addr = kzalloc(size + alignment, GFP_KERNEL);
	if (!orig_dst_addr) {
		dev_err(dev, "Failed to allocate destination address\n");
		ret = false;
		ret = -ENOMEM;
		goto err_dst_addr;
	}

	orig_dst_phys_addr = dma_map_single(dev, orig_dst_addr,
					    size + alignment, DMA_FROM_DEVICE);
	if (dma_mapping_error(dev, orig_dst_phys_addr)) {
	ret = dma_mapping_error(dev, orig_dst_phys_addr);
	if (ret) {
		dev_err(dev, "failed to map destination buffer address\n");
		ret = false;
		goto err_dst_phys_addr;
	}

@@ -498,8 +588,8 @@ static bool pci_endpoint_test_copy(struct pci_endpoint_test *test,
			 DMA_FROM_DEVICE);

	dst_crc32 = crc32_le(~0, dst_addr, size);
	if (dst_crc32 == src_crc32)
		ret = true;
	if (dst_crc32 != src_crc32)
		ret = -EIO;

err_dst_phys_addr:
	kfree(orig_dst_addr);
@@ -510,16 +600,13 @@ static bool pci_endpoint_test_copy(struct pci_endpoint_test *test,

err_src_phys_addr:
	kfree(orig_src_addr);

err:
	return ret;
}

static bool pci_endpoint_test_write(struct pci_endpoint_test *test,
static int pci_endpoint_test_write(struct pci_endpoint_test *test,
				    unsigned long arg)
{
	struct pci_endpoint_test_xfer_param param;
	bool ret = false;
	u32 flags = 0;
	bool use_dma;
	u32 reg;
@@ -534,17 +621,17 @@ static bool pci_endpoint_test_write(struct pci_endpoint_test *test,
	int irq_type = test->irq_type;
	size_t size;
	u32 crc32;
	int err;
	int ret;

	err = copy_from_user(&param, (void __user *)arg, sizeof(param));
	if (err != 0) {
	ret = copy_from_user(&param, (void __user *)arg, sizeof(param));
	if (ret) {
		dev_err(dev, "Failed to get transfer param\n");
		return false;
		return -EFAULT;
	}

	err = pci_endpoint_test_validate_xfer_params(dev, &param, alignment);
	if (err)
		return false;
	ret = pci_endpoint_test_validate_xfer_params(dev, &param, alignment);
	if (ret)
		return ret;

	size = param.size;

@@ -554,23 +641,22 @@ static bool pci_endpoint_test_write(struct pci_endpoint_test *test,

	if (irq_type < IRQ_TYPE_INTX || irq_type > IRQ_TYPE_MSIX) {
		dev_err(dev, "Invalid IRQ type option\n");
		goto err;
		return -EINVAL;
	}

	orig_addr = kzalloc(size + alignment, GFP_KERNEL);
	if (!orig_addr) {
		dev_err(dev, "Failed to allocate address\n");
		ret = false;
		goto err;
		return -ENOMEM;
	}

	get_random_bytes(orig_addr, size + alignment);

	orig_phys_addr = dma_map_single(dev, orig_addr, size + alignment,
					DMA_TO_DEVICE);
	if (dma_mapping_error(dev, orig_phys_addr)) {
	ret = dma_mapping_error(dev, orig_phys_addr);
	if (ret) {
		dev_err(dev, "failed to map source buffer address\n");
		ret = false;
		goto err_phys_addr;
	}

@@ -603,24 +689,21 @@ static bool pci_endpoint_test_write(struct pci_endpoint_test *test,
	wait_for_completion(&test->irq_raised);

	reg = pci_endpoint_test_readl(test, PCI_ENDPOINT_TEST_STATUS);
	if (reg & STATUS_READ_SUCCESS)
		ret = true;
	if (!(reg & STATUS_READ_SUCCESS))
		ret = -EIO;

	dma_unmap_single(dev, orig_phys_addr, size + alignment,
			 DMA_TO_DEVICE);

err_phys_addr:
	kfree(orig_addr);

err:
	return ret;
}

static bool pci_endpoint_test_read(struct pci_endpoint_test *test,
static int pci_endpoint_test_read(struct pci_endpoint_test *test,
				   unsigned long arg)
{
	struct pci_endpoint_test_xfer_param param;
	bool ret = false;
	u32 flags = 0;
	bool use_dma;
	size_t size;
@@ -634,17 +717,17 @@ static bool pci_endpoint_test_read(struct pci_endpoint_test *test,
	size_t alignment = test->alignment;
	int irq_type = test->irq_type;
	u32 crc32;
	int err;
	int ret;

	err = copy_from_user(&param, (void __user *)arg, sizeof(param));
	if (err) {
	ret = copy_from_user(&param, (void __user *)arg, sizeof(param));
	if (ret) {
		dev_err(dev, "Failed to get transfer param\n");
		return false;
		return -EFAULT;
	}

	err = pci_endpoint_test_validate_xfer_params(dev, &param, alignment);
	if (err)
		return false;
	ret = pci_endpoint_test_validate_xfer_params(dev, &param, alignment);
	if (ret)
		return ret;

	size = param.size;

@@ -654,21 +737,20 @@ static bool pci_endpoint_test_read(struct pci_endpoint_test *test,

	if (irq_type < IRQ_TYPE_INTX || irq_type > IRQ_TYPE_MSIX) {
		dev_err(dev, "Invalid IRQ type option\n");
		goto err;
		return -EINVAL;
	}

	orig_addr = kzalloc(size + alignment, GFP_KERNEL);
	if (!orig_addr) {
		dev_err(dev, "Failed to allocate destination address\n");
		ret = false;
		goto err;
		return -ENOMEM;
	}

	orig_phys_addr = dma_map_single(dev, orig_addr, size + alignment,
					DMA_FROM_DEVICE);
	if (dma_mapping_error(dev, orig_phys_addr)) {
	ret = dma_mapping_error(dev, orig_phys_addr);
	if (ret) {
		dev_err(dev, "failed to map source buffer address\n");
		ret = false;
		goto err_phys_addr;
	}

@@ -700,50 +782,51 @@ static bool pci_endpoint_test_read(struct pci_endpoint_test *test,
			 DMA_FROM_DEVICE);

	crc32 = crc32_le(~0, addr, size);
	if (crc32 == pci_endpoint_test_readl(test, PCI_ENDPOINT_TEST_CHECKSUM))
		ret = true;
	if (crc32 != pci_endpoint_test_readl(test, PCI_ENDPOINT_TEST_CHECKSUM))
		ret = -EIO;

err_phys_addr:
	kfree(orig_addr);
err:
	return ret;
}

static bool pci_endpoint_test_clear_irq(struct pci_endpoint_test *test)
static int pci_endpoint_test_clear_irq(struct pci_endpoint_test *test)
{
	pci_endpoint_test_release_irq(test);
	pci_endpoint_test_free_irq_vectors(test);
	return true;

	return 0;
}

static bool pci_endpoint_test_set_irq(struct pci_endpoint_test *test,
static int pci_endpoint_test_set_irq(struct pci_endpoint_test *test,
				      int req_irq_type)
{
	struct pci_dev *pdev = test->pdev;
	struct device *dev = &pdev->dev;
	int ret;

	if (req_irq_type < IRQ_TYPE_INTX || req_irq_type > IRQ_TYPE_MSIX) {
		dev_err(dev, "Invalid IRQ type option\n");
		return false;
		return -EINVAL;
	}

	if (test->irq_type == req_irq_type)
		return true;
		return 0;

	pci_endpoint_test_release_irq(test);
	pci_endpoint_test_free_irq_vectors(test);

	if (!pci_endpoint_test_alloc_irq_vectors(test, req_irq_type))
		goto err;

	if (!pci_endpoint_test_request_irq(test))
		goto err;

	return true;
	ret = pci_endpoint_test_alloc_irq_vectors(test, req_irq_type);
	if (ret)
		return ret;

err:
	ret = pci_endpoint_test_request_irq(test);
	if (ret) {
		pci_endpoint_test_free_irq_vectors(test);
	return false;
		return ret;
	}

	return 0;
}

static long pci_endpoint_test_ioctl(struct file *file, unsigned int cmd,
@@ -768,6 +851,9 @@ static long pci_endpoint_test_ioctl(struct file *file, unsigned int cmd,
			goto ret;
		ret = pci_endpoint_test_bar(test, bar);
		break;
	case PCITEST_BARS:
		ret = pci_endpoint_test_bars(test);
		break;
	case PCITEST_INTX_IRQ:
		ret = pci_endpoint_test_intx_irq(test);
		break;
@@ -805,10 +891,24 @@ static const struct file_operations pci_endpoint_test_fops = {
	.unlocked_ioctl = pci_endpoint_test_ioctl,
};

static void pci_endpoint_test_get_capabilities(struct pci_endpoint_test *test)
{
	struct pci_dev *pdev = test->pdev;
	struct device *dev = &pdev->dev;
	u32 caps;

	caps = pci_endpoint_test_readl(test, PCI_ENDPOINT_TEST_CAPS);
	dev_dbg(dev, "PCI_ENDPOINT_TEST_CAPS: %#x\n", caps);

	/* CAP_UNALIGNED_ACCESS is set if the EP can do unaligned access */
	if (caps & CAP_UNALIGNED_ACCESS)
		test->alignment = 0;
}

static int pci_endpoint_test_probe(struct pci_dev *pdev,
				   const struct pci_device_id *ent)
{
	int err;
	int ret;
	int id;
	char name[24];
	enum pci_barno bar;
@@ -847,24 +947,23 @@ static int pci_endpoint_test_probe(struct pci_dev *pdev,

	dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(48));

	err = pci_enable_device(pdev);
	if (err) {
	ret = pci_enable_device(pdev);
	if (ret) {
		dev_err(dev, "Cannot enable PCI device\n");
		return err;
		return ret;
	}

	err = pci_request_regions(pdev, DRV_MODULE_NAME);
	if (err) {
	ret = pci_request_regions(pdev, DRV_MODULE_NAME);
	if (ret) {
		dev_err(dev, "Cannot obtain PCI resources\n");
		goto err_disable_pdev;
	}

	pci_set_master(pdev);

	if (!pci_endpoint_test_alloc_irq_vectors(test, irq_type)) {
		err = -EINVAL;
	ret = pci_endpoint_test_alloc_irq_vectors(test, irq_type);
	if (ret)
		goto err_disable_irq;
	}

	for (bar = 0; bar < PCI_STD_NUM_BARS; bar++) {
		if (pci_resource_flags(pdev, bar) & IORESOURCE_MEM) {
@@ -879,7 +978,7 @@ static int pci_endpoint_test_probe(struct pci_dev *pdev,

	test->base = test->bar[test_reg_bar];
	if (!test->base) {
		err = -ENOMEM;
		ret = -ENOMEM;
		dev_err(dev, "Cannot perform PCI test without BAR%d\n",
			test_reg_bar);
		goto err_iounmap;
@@ -889,7 +988,7 @@ static int pci_endpoint_test_probe(struct pci_dev *pdev,

	id = ida_alloc(&pci_endpoint_test_ida, GFP_KERNEL);
	if (id < 0) {
		err = id;
		ret = id;
		dev_err(dev, "Unable to get id\n");
		goto err_iounmap;
	}
@@ -897,27 +996,28 @@ static int pci_endpoint_test_probe(struct pci_dev *pdev,
	snprintf(name, sizeof(name), DRV_MODULE_NAME ".%d", id);
	test->name = kstrdup(name, GFP_KERNEL);
	if (!test->name) {
		err = -ENOMEM;
		ret = -ENOMEM;
		goto err_ida_remove;
	}

	if (!pci_endpoint_test_request_irq(test)) {
		err = -EINVAL;
	ret = pci_endpoint_test_request_irq(test);
	if (ret)
		goto err_kfree_test_name;
	}

	pci_endpoint_test_get_capabilities(test);

	misc_device = &test->miscdev;
	misc_device->minor = MISC_DYNAMIC_MINOR;
	misc_device->name = kstrdup(name, GFP_KERNEL);
	if (!misc_device->name) {
		err = -ENOMEM;
		ret = -ENOMEM;
		goto err_release_irq;
	}
	misc_device->parent = &pdev->dev;
	misc_device->fops = &pci_endpoint_test_fops;

	err = misc_register(misc_device);
	if (err) {
	ret = misc_register(misc_device);
	if (ret) {
		dev_err(dev, "Failed to register device\n");
		goto err_kfree_name;
	}
@@ -949,7 +1049,7 @@ static int pci_endpoint_test_probe(struct pci_dev *pdev,
err_disable_pdev:
	pci_disable_device(pdev);

	return err;
	return ret;
}

static void pci_endpoint_test_remove(struct pci_dev *pdev)
+22 −3
Original line number Diff line number Diff line
@@ -44,6 +44,8 @@

#define TIMER_RESOLUTION		1

#define CAP_UNALIGNED_ACCESS		BIT(0)

static struct workqueue_struct *kpcitest_workqueue;

struct pci_epf_test {
@@ -74,6 +76,7 @@ struct pci_epf_test_reg {
	u32	irq_type;
	u32	irq_number;
	u32	flags;
	u32	caps;
} __packed;

static struct pci_epf_header test_header = {
@@ -251,7 +254,7 @@ static int pci_epf_test_init_dma_chan(struct pci_epf_test *epf_test)

fail_back_rx:
	dma_release_channel(epf_test->dma_chan_rx);
	epf_test->dma_chan_tx = NULL;
	epf_test->dma_chan_rx = NULL;

fail_back_tx:
	dma_cap_zero(mask);
@@ -328,8 +331,8 @@ static void pci_epf_test_copy(struct pci_epf_test *epf_test,
	void *copy_buf = NULL, *buf;

	if (reg->flags & FLAG_USE_DMA) {
		if (epf_test->dma_private) {
			dev_err(dev, "Cannot transfer data using DMA\n");
		if (!dma_has_cap(DMA_MEMCPY, epf_test->dma_chan_tx->device->cap_mask)) {
			dev_err(dev, "DMA controller doesn't support MEMCPY\n");
			ret = -EINVAL;
			goto set_status;
		}
@@ -739,6 +742,20 @@ static void pci_epf_test_clear_bar(struct pci_epf *epf)
	}
}

static void pci_epf_test_set_capabilities(struct pci_epf *epf)
{
	struct pci_epf_test *epf_test = epf_get_drvdata(epf);
	enum pci_barno test_reg_bar = epf_test->test_reg_bar;
	struct pci_epf_test_reg *reg = epf_test->reg[test_reg_bar];
	struct pci_epc *epc = epf->epc;
	u32 caps = 0;

	if (epc->ops->align_addr)
		caps |= CAP_UNALIGNED_ACCESS;

	reg->caps = cpu_to_le32(caps);
}

static int pci_epf_test_epc_init(struct pci_epf *epf)
{
	struct pci_epf_test *epf_test = epf_get_drvdata(epf);
@@ -763,6 +780,8 @@ static int pci_epf_test_epc_init(struct pci_epf *epf)
		}
	}

	pci_epf_test_set_capabilities(epf);

	ret = pci_epf_test_set_bar(epf);
	if (ret)
		return ret;
+1 −0
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@
#define PCITEST_MSIX		_IOW('P', 0x7, int)
#define PCITEST_SET_IRQTYPE	_IOW('P', 0x8, int)
#define PCITEST_GET_IRQTYPE	_IO('P', 0x9)
#define PCITEST_BARS		_IO('P', 0xa)
#define PCITEST_CLEAR_IRQ	_IO('P', 0x10)

#define PCITEST_FLAGS_USE_DMA	0x00000001
Loading