Uwe Kleine-König (The Capable Hub)thunderbolt: Drop comma after device id array terminator9990c495 months agoHistory
763 lines · 18.8 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0/* * DMA traffic test driver * * Copyright (C) 2020, Intel Corporation * Authors: Isaac Hazan <isaac.hazan@intel.com> *	    Mika Westerberg <mika.westerberg@linux.intel.com> */ #include <linux/completion.h>#include <linux/debugfs.h>#include <linux/module.h>#include <linux/sizes.h>#include <linux/thunderbolt.h> #define DMA_TEST_TX_RING_SIZE		64#define DMA_TEST_RX_RING_SIZE		256#define DMA_TEST_FRAME_SIZE		SZ_4K#define DMA_TEST_DATA_PATTERN		0x0123456789abcdefLL#define DMA_TEST_MAX_PACKETS		1000 enum dma_test_frame_pdf {	DMA_TEST_PDF_FRAME_START = 1,	DMA_TEST_PDF_FRAME_END,}; struct dma_test_frame {	struct dma_test *dma_test;	void *data;	struct ring_frame frame;}; enum dma_test_test_error {	DMA_TEST_NO_ERROR,	DMA_TEST_INTERRUPTED,	DMA_TEST_BUFFER_ERROR,	DMA_TEST_DMA_ERROR,	DMA_TEST_CONFIG_ERROR,	DMA_TEST_SPEED_ERROR,	DMA_TEST_WIDTH_ERROR,	DMA_TEST_BONDING_ERROR,	DMA_TEST_PACKET_ERROR,}; static const char * const dma_test_error_names[] = {	[DMA_TEST_NO_ERROR] = "no errors",	[DMA_TEST_INTERRUPTED] = "interrupted by signal",	[DMA_TEST_BUFFER_ERROR] = "no memory for packet buffers",	[DMA_TEST_DMA_ERROR] = "DMA ring setup failed",	[DMA_TEST_CONFIG_ERROR] = "configuration is not valid",	[DMA_TEST_SPEED_ERROR] = "unexpected link speed",	[DMA_TEST_WIDTH_ERROR] = "unexpected link width",	[DMA_TEST_BONDING_ERROR] = "lane bonding configuration error",	[DMA_TEST_PACKET_ERROR] = "packet check failed",}; enum dma_test_result {	DMA_TEST_NOT_RUN,	DMA_TEST_SUCCESS,	DMA_TEST_FAIL,}; static const char * const dma_test_result_names[] = {	[DMA_TEST_NOT_RUN] = "not run",	[DMA_TEST_SUCCESS] = "success",	[DMA_TEST_FAIL] = "failed",}; /** * struct dma_test - DMA test device driver private data * @svc: XDomain service the driver is bound to * @xd: XDomain the service belongs to * @rx_ring: Software ring holding RX frames * @rx_hopid: HopID used for receiving frames * @tx_ring: Software ring holding TX frames * @tx_hopid: HopID used for sending fames * @packets_to_send: Number of packets to send * @packets_to_receive: Number of packets to receive * @packets_sent: Actual number of packets sent * @packets_received: Actual number of packets received * @link_speed: Expected link speed (Gb/s), %0 to use whatever is negotiated * @link_width: Expected link width (Gb/s), %0 to use whatever is negotiated * @crc_errors: Number of CRC errors during the test run * @buffer_overflow_errors: Number of buffer overflow errors during the test *			    run * @result: Result of the last run * @error_code: Error code of the last run * @complete: Used to wait for the Rx to complete * @lock: Lock serializing access to this structure */struct dma_test {	const struct tb_service *svc;	struct tb_xdomain *xd;	struct tb_ring *rx_ring;	int rx_hopid;	struct tb_ring *tx_ring;	int tx_hopid;	unsigned int packets_to_send;	unsigned int packets_to_receive;	unsigned int packets_sent;	unsigned int packets_received;	unsigned int link_speed;	enum tb_link_width link_width;	unsigned int crc_errors;	unsigned int buffer_overflow_errors;	enum dma_test_result result;	enum dma_test_test_error error_code;	struct completion complete;	struct mutex lock;}; /* DMA test property directory UUID: 3188cd10-6523-4a5a-a682-fdca07a248d8 */static const uuid_t dma_test_dir_uuid =	UUID_INIT(0x3188cd10, 0x6523, 0x4a5a,		  0xa6, 0x82, 0xfd, 0xca, 0x07, 0xa2, 0x48, 0xd8); static struct tb_property_dir *dma_test_dir;static void *dma_test_pattern; static void dma_test_free_rings(struct dma_test *dt){	if (dt->rx_ring) {		tb_xdomain_release_in_hopid(dt->xd, dt->rx_hopid);		tb_ring_free(dt->rx_ring);		dt->rx_ring = NULL;	}	if (dt->tx_ring) {		tb_xdomain_release_out_hopid(dt->xd, dt->tx_hopid);		tb_ring_free(dt->tx_ring);		dt->tx_ring = NULL;	}} static int dma_test_start_rings(struct dma_test *dt){	unsigned int flags = RING_FLAG_FRAME;	struct tb_xdomain *xd = dt->xd;	int ret, e2e_tx_hop = 0;	struct tb_ring *ring; 	/*	 * If we are both sender and receiver (traffic goes over a	 * special loopback dongle) enable E2E flow control. This avoids	 * losing packets.	 */	if (dt->packets_to_send && dt->packets_to_receive)		flags |= RING_FLAG_E2E; 	if (dt->packets_to_send) {		ring = tb_ring_alloc_tx(xd->tb->nhi, -1, DMA_TEST_TX_RING_SIZE,					flags);		if (!ring)			return -ENOMEM; 		dt->tx_ring = ring;		e2e_tx_hop = ring->hop; 		tb_ring_throttling(ring, 128000); 		ret = tb_xdomain_alloc_out_hopid(xd, -1);		if (ret < 0) {			dma_test_free_rings(dt);			return ret;		} 		dt->tx_hopid = ret; 	} 	if (dt->packets_to_receive) {		u16 sof_mask, eof_mask; 		sof_mask = BIT(DMA_TEST_PDF_FRAME_START);		eof_mask = BIT(DMA_TEST_PDF_FRAME_END); 		ring = tb_ring_alloc_rx(xd->tb->nhi, -1, DMA_TEST_RX_RING_SIZE,					flags, e2e_tx_hop, sof_mask, eof_mask,					NULL, NULL);		if (!ring) {			dma_test_free_rings(dt);			return -ENOMEM;		} 		dt->rx_ring = ring; 		tb_ring_throttling(ring, 128000); 		ret = tb_xdomain_alloc_in_hopid(xd, -1);		if (ret < 0) {			dma_test_free_rings(dt);			return ret;		} 		dt->rx_hopid = ret;	} 	ret = tb_xdomain_enable_paths(dt->xd, dt->tx_hopid,				      dt->tx_ring ? dt->tx_ring->hop : -1,				      dt->rx_hopid,				      dt->rx_ring ? dt->rx_ring->hop : -1);	if (ret) {		dma_test_free_rings(dt);		return ret;	} 	if (dt->tx_ring)		tb_ring_start(dt->tx_ring);	if (dt->rx_ring)		tb_ring_start(dt->rx_ring); 	return 0;} static void dma_test_stop_rings(struct dma_test *dt){	int ret; 	if (dt->rx_ring)		tb_ring_stop(dt->rx_ring);	if (dt->tx_ring)		tb_ring_stop(dt->tx_ring); 	ret = tb_xdomain_disable_paths(dt->xd, dt->tx_hopid,				       dt->tx_ring ? dt->tx_ring->hop : -1,				       dt->rx_hopid,				       dt->rx_ring ? dt->rx_ring->hop : -1);	if (ret)		dev_warn(&dt->svc->dev, "failed to disable DMA paths\n"); 	dma_test_free_rings(dt);} static void dma_test_rx_callback(struct tb_ring *ring, struct ring_frame *frame,				 bool canceled){	struct dma_test_frame *tf = container_of(frame, typeof(*tf), frame);	struct dma_test *dt = tf->dma_test;	struct device *dma_dev = tb_ring_dma_device(dt->rx_ring); 	dma_unmap_single(dma_dev, tf->frame.buffer_phy, DMA_TEST_FRAME_SIZE,			 DMA_FROM_DEVICE);	kfree(tf->data); 	if (canceled) {		kfree(tf);		return;	} 	dt->packets_received++;	dev_dbg(&dt->svc->dev, "packet %u/%u received\n", dt->packets_received,		dt->packets_to_receive); 	if (tf->frame.flags & RING_DESC_CRC_ERROR)		dt->crc_errors++;	if (tf->frame.flags & RING_DESC_BUFFER_OVERRUN)		dt->buffer_overflow_errors++; 	kfree(tf); 	if (dt->packets_received == dt->packets_to_receive)		complete(&dt->complete);} static int dma_test_submit_rx(struct dma_test *dt, size_t npackets){	struct device *dma_dev = tb_ring_dma_device(dt->rx_ring);	int i; 	for (i = 0; i < npackets; i++) {		struct dma_test_frame *tf;		dma_addr_t dma_addr; 		tf = kzalloc_obj(*tf);		if (!tf)			return -ENOMEM; 		tf->data = kzalloc(DMA_TEST_FRAME_SIZE, GFP_KERNEL);		if (!tf->data) {			kfree(tf);			return -ENOMEM;		} 		dma_addr = dma_map_single(dma_dev, tf->data, DMA_TEST_FRAME_SIZE,					  DMA_FROM_DEVICE);		if (dma_mapping_error(dma_dev, dma_addr)) {			kfree(tf->data);			kfree(tf);			return -ENOMEM;		} 		tf->frame.buffer_phy = dma_addr;		tf->frame.callback = dma_test_rx_callback;		tf->dma_test = dt;		INIT_LIST_HEAD(&tf->frame.list); 		tb_ring_rx(dt->rx_ring, &tf->frame);	} 	return 0;} static void dma_test_tx_callback(struct tb_ring *ring, struct ring_frame *frame,				 bool canceled){	struct dma_test_frame *tf = container_of(frame, typeof(*tf), frame);	struct dma_test *dt = tf->dma_test;	struct device *dma_dev = tb_ring_dma_device(dt->tx_ring); 	dma_unmap_single(dma_dev, tf->frame.buffer_phy, DMA_TEST_FRAME_SIZE,			 DMA_TO_DEVICE);	kfree(tf->data);	kfree(tf);} static int dma_test_submit_tx(struct dma_test *dt, size_t npackets){	struct device *dma_dev = tb_ring_dma_device(dt->tx_ring);	int i; 	for (i = 0; i < npackets; i++) {		struct dma_test_frame *tf;		dma_addr_t dma_addr; 		tf = kzalloc_obj(*tf);		if (!tf)			return -ENOMEM; 		tf->frame.size = 0; /* means 4096 */		tf->dma_test = dt; 		tf->data = kmemdup(dma_test_pattern, DMA_TEST_FRAME_SIZE, GFP_KERNEL);		if (!tf->data) {			kfree(tf);			return -ENOMEM;		} 		dma_addr = dma_map_single(dma_dev, tf->data, DMA_TEST_FRAME_SIZE,					  DMA_TO_DEVICE);		if (dma_mapping_error(dma_dev, dma_addr)) {			kfree(tf->data);			kfree(tf);			return -ENOMEM;		} 		tf->frame.buffer_phy = dma_addr;		tf->frame.callback = dma_test_tx_callback;		tf->frame.sof = DMA_TEST_PDF_FRAME_START;		tf->frame.eof = DMA_TEST_PDF_FRAME_END;		INIT_LIST_HEAD(&tf->frame.list); 		dt->packets_sent++;		dev_dbg(&dt->svc->dev, "packet %u/%u sent\n", dt->packets_sent,			dt->packets_to_send); 		tb_ring_tx(dt->tx_ring, &tf->frame);	} 	return 0;} #define DMA_TEST_DEBUGFS_ATTR(__fops, __get, __validate, __set)	\static int __fops ## _show(void *data, u64 *val)		\{								\	struct tb_service *svc = data;				\	struct dma_test *dt = tb_service_get_drvdata(svc);	\	int ret;						\								\	ret = mutex_lock_interruptible(&dt->lock);		\	if (ret)						\		return ret;					\	__get(dt, val);						\	mutex_unlock(&dt->lock);				\	return 0;						\}								\static int __fops ## _store(void *data, u64 val)		\{								\	struct tb_service *svc = data;				\	struct dma_test *dt = tb_service_get_drvdata(svc);	\	int ret;						\								\	ret = __validate(val);					\	if (ret)						\		return ret;					\	ret = mutex_lock_interruptible(&dt->lock);		\	if (ret)						\		return ret;					\	__set(dt, val);						\	mutex_unlock(&dt->lock);				\	return 0;						\}								\DEFINE_DEBUGFS_ATTRIBUTE(__fops ## _fops, __fops ## _show,	\			 __fops ## _store, "%llu\n") static void lanes_get(const struct dma_test *dt, u64 *val){	*val = dt->link_width;} static int lanes_validate(u64 val){	return val > 2 ? -EINVAL : 0;} static void lanes_set(struct dma_test *dt, u64 val){	dt->link_width = val;}DMA_TEST_DEBUGFS_ATTR(lanes, lanes_get, lanes_validate, lanes_set); static void speed_get(const struct dma_test *dt, u64 *val){	*val = dt->link_speed;} static int speed_validate(u64 val){	switch (val) {	case 40:	case 20:	case 10:	case 0:		return 0;	default:		return -EINVAL;	}} static void speed_set(struct dma_test *dt, u64 val){	dt->link_speed = val;}DMA_TEST_DEBUGFS_ATTR(speed, speed_get, speed_validate, speed_set); static void packets_to_receive_get(const struct dma_test *dt, u64 *val){	*val = dt->packets_to_receive;} static int packets_to_receive_validate(u64 val){	return val > DMA_TEST_MAX_PACKETS ? -EINVAL : 0;} static void packets_to_receive_set(struct dma_test *dt, u64 val){	dt->packets_to_receive = val;}DMA_TEST_DEBUGFS_ATTR(packets_to_receive, packets_to_receive_get,		      packets_to_receive_validate, packets_to_receive_set); static void packets_to_send_get(const struct dma_test *dt, u64 *val){	*val = dt->packets_to_send;} static int packets_to_send_validate(u64 val){	return val > DMA_TEST_MAX_PACKETS ? -EINVAL : 0;} static void packets_to_send_set(struct dma_test *dt, u64 val){	dt->packets_to_send = val;}DMA_TEST_DEBUGFS_ATTR(packets_to_send, packets_to_send_get,		      packets_to_send_validate, packets_to_send_set); static int dma_test_set_bonding(struct dma_test *dt){	switch (dt->link_width) {	case TB_LINK_WIDTH_DUAL:		return tb_xdomain_lane_bonding_enable(dt->xd);	case TB_LINK_WIDTH_SINGLE:		tb_xdomain_lane_bonding_disable(dt->xd);		fallthrough;	default:		return 0;	}} static bool dma_test_validate_config(struct dma_test *dt){	if (!dt->packets_to_send && !dt->packets_to_receive)		return false;	if (dt->packets_to_send && dt->packets_to_receive &&	    dt->packets_to_send != dt->packets_to_receive)		return false;	return true;} static void dma_test_check_errors(struct dma_test *dt, int ret){	if (!dt->error_code) {		if (dt->link_speed && dt->xd->link_speed != dt->link_speed) {			dt->error_code = DMA_TEST_SPEED_ERROR;		} else if (dt->link_width && dt->link_width != dt->xd->link_width) {			dt->error_code = DMA_TEST_WIDTH_ERROR;		} else if (dt->packets_to_send != dt->packets_sent ||			 dt->packets_to_receive != dt->packets_received ||			 dt->crc_errors || dt->buffer_overflow_errors) {

Lines 1–500 of 763.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t