xref: /openbmc/linux/drivers/dma/idxd/submit.c (revision 42d279f9)
1d1dfe5b8SDave Jiang // SPDX-License-Identifier: GPL-2.0
2d1dfe5b8SDave Jiang /* Copyright(c) 2019 Intel Corporation. All rights rsvd. */
3d1dfe5b8SDave Jiang #include <linux/init.h>
4d1dfe5b8SDave Jiang #include <linux/kernel.h>
5d1dfe5b8SDave Jiang #include <linux/module.h>
6d1dfe5b8SDave Jiang #include <linux/pci.h>
7d1dfe5b8SDave Jiang #include <uapi/linux/idxd.h>
8d1dfe5b8SDave Jiang #include "idxd.h"
9d1dfe5b8SDave Jiang #include "registers.h"
10d1dfe5b8SDave Jiang 
11d1dfe5b8SDave Jiang struct idxd_desc *idxd_alloc_desc(struct idxd_wq *wq, enum idxd_op_type optype)
12d1dfe5b8SDave Jiang {
13d1dfe5b8SDave Jiang 	struct idxd_desc *desc;
14d1dfe5b8SDave Jiang 	int idx;
15d1dfe5b8SDave Jiang 	struct idxd_device *idxd = wq->idxd;
16d1dfe5b8SDave Jiang 
17d1dfe5b8SDave Jiang 	if (idxd->state != IDXD_DEV_ENABLED)
18d1dfe5b8SDave Jiang 		return ERR_PTR(-EIO);
19d1dfe5b8SDave Jiang 
20d1dfe5b8SDave Jiang 	if (optype == IDXD_OP_BLOCK)
21d1dfe5b8SDave Jiang 		percpu_down_read(&wq->submit_lock);
22d1dfe5b8SDave Jiang 	else if (!percpu_down_read_trylock(&wq->submit_lock))
23d1dfe5b8SDave Jiang 		return ERR_PTR(-EBUSY);
24d1dfe5b8SDave Jiang 
25d1dfe5b8SDave Jiang 	if (!atomic_add_unless(&wq->dq_count, 1, wq->size)) {
26d1dfe5b8SDave Jiang 		int rc;
27d1dfe5b8SDave Jiang 
28d1dfe5b8SDave Jiang 		if (optype == IDXD_OP_NONBLOCK) {
29d1dfe5b8SDave Jiang 			percpu_up_read(&wq->submit_lock);
30d1dfe5b8SDave Jiang 			return ERR_PTR(-EAGAIN);
31d1dfe5b8SDave Jiang 		}
32d1dfe5b8SDave Jiang 
33d1dfe5b8SDave Jiang 		percpu_up_read(&wq->submit_lock);
34d1dfe5b8SDave Jiang 		percpu_down_write(&wq->submit_lock);
35d1dfe5b8SDave Jiang 		rc = wait_event_interruptible(wq->submit_waitq,
36d1dfe5b8SDave Jiang 					      atomic_add_unless(&wq->dq_count,
37d1dfe5b8SDave Jiang 								1, wq->size) ||
38d1dfe5b8SDave Jiang 					       idxd->state != IDXD_DEV_ENABLED);
39d1dfe5b8SDave Jiang 		percpu_up_write(&wq->submit_lock);
40d1dfe5b8SDave Jiang 		if (rc < 0)
41d1dfe5b8SDave Jiang 			return ERR_PTR(-EINTR);
42d1dfe5b8SDave Jiang 		if (idxd->state != IDXD_DEV_ENABLED)
43d1dfe5b8SDave Jiang 			return ERR_PTR(-EIO);
44d1dfe5b8SDave Jiang 	} else {
45d1dfe5b8SDave Jiang 		percpu_up_read(&wq->submit_lock);
46d1dfe5b8SDave Jiang 	}
47d1dfe5b8SDave Jiang 
48d1dfe5b8SDave Jiang 	idx = sbitmap_get(&wq->sbmap, 0, false);
49d1dfe5b8SDave Jiang 	if (idx < 0) {
50d1dfe5b8SDave Jiang 		atomic_dec(&wq->dq_count);
51d1dfe5b8SDave Jiang 		return ERR_PTR(-EAGAIN);
52d1dfe5b8SDave Jiang 	}
53d1dfe5b8SDave Jiang 
54d1dfe5b8SDave Jiang 	desc = wq->descs[idx];
55d1dfe5b8SDave Jiang 	memset(desc->hw, 0, sizeof(struct dsa_hw_desc));
56d1dfe5b8SDave Jiang 	memset(desc->completion, 0, sizeof(struct dsa_completion_record));
57d1dfe5b8SDave Jiang 	return desc;
58d1dfe5b8SDave Jiang }
59d1dfe5b8SDave Jiang 
60d1dfe5b8SDave Jiang void idxd_free_desc(struct idxd_wq *wq, struct idxd_desc *desc)
61d1dfe5b8SDave Jiang {
62d1dfe5b8SDave Jiang 	atomic_dec(&wq->dq_count);
63d1dfe5b8SDave Jiang 
64d1dfe5b8SDave Jiang 	sbitmap_clear_bit(&wq->sbmap, desc->id);
65d1dfe5b8SDave Jiang 	wake_up(&wq->submit_waitq);
66d1dfe5b8SDave Jiang }
67d1dfe5b8SDave Jiang 
68d1dfe5b8SDave Jiang int idxd_submit_desc(struct idxd_wq *wq, struct idxd_desc *desc)
69d1dfe5b8SDave Jiang {
70d1dfe5b8SDave Jiang 	struct idxd_device *idxd = wq->idxd;
71d1dfe5b8SDave Jiang 	int vec = desc->hw->int_handle;
7242d279f9SDave Jiang 	void __iomem *portal;
73d1dfe5b8SDave Jiang 
74d1dfe5b8SDave Jiang 	if (idxd->state != IDXD_DEV_ENABLED)
75d1dfe5b8SDave Jiang 		return -EIO;
76d1dfe5b8SDave Jiang 
7742d279f9SDave Jiang 	portal = wq->dportal + idxd_get_wq_portal_offset(IDXD_PORTAL_UNLIMITED);
78d1dfe5b8SDave Jiang 	/*
79d1dfe5b8SDave Jiang 	 * The wmb() flushes writes to coherent DMA data before possibly
80d1dfe5b8SDave Jiang 	 * triggering a DMA read. The wmb() is necessary even on UP because
81d1dfe5b8SDave Jiang 	 * the recipient is a device.
82d1dfe5b8SDave Jiang 	 */
83d1dfe5b8SDave Jiang 	wmb();
8442d279f9SDave Jiang 	iosubmit_cmds512(portal, desc->hw, 1);
85d1dfe5b8SDave Jiang 
86d1dfe5b8SDave Jiang 	/*
87d1dfe5b8SDave Jiang 	 * Pending the descriptor to the lockless list for the irq_entry
88d1dfe5b8SDave Jiang 	 * that we designated the descriptor to.
89d1dfe5b8SDave Jiang 	 */
908f47d1a5SDave Jiang 	if (desc->hw->flags & IDXD_OP_FLAG_RCI)
918f47d1a5SDave Jiang 		llist_add(&desc->llnode,
928f47d1a5SDave Jiang 			  &idxd->irq_entries[vec].pending_llist);
93d1dfe5b8SDave Jiang 
94d1dfe5b8SDave Jiang 	return 0;
95d1dfe5b8SDave Jiang }
96