Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1/*
  2 * Intel MIC Platform Software Stack (MPSS)
  3 *
  4 * Copyright(c) 2015 Intel Corporation.
  5 *
  6 * This program is free software; you can redistribute it and/or modify
  7 * it under the terms of the GNU General Public License, version 2, as
  8 * published by the Free Software Foundation.
  9 *
 10 * This program is distributed in the hope that it will be useful, but
 11 * WITHOUT ANY WARRANTY; without even the implied warranty of
 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 13 * General Public License for more details.
 14 *
 15 * Intel SCIF driver.
 16 *
 17 */
 18#include "scif_main.h"
 19#include <linux/mmu_notifier.h>
 20#include <linux/highmem.h>
 21
 22/*
 23 * scif_insert_tcw:
 24 *
 25 * Insert a temp window to the temp registration list sorted by va_for_temp.
 26 * RMA lock must be held.
 27 */
 28void scif_insert_tcw(struct scif_window *window, struct list_head *head)
 29{
 30	struct scif_window *curr = NULL;
 31	struct scif_window *prev = list_entry(head, struct scif_window, list);
 32	struct list_head *item;
 33
 34	INIT_LIST_HEAD(&window->list);
 35	/* Compare with tail and if the entry is new tail add it to the end */
 36	if (!list_empty(head)) {
 37		curr = list_entry(head->prev, struct scif_window, list);
 38		if (curr->va_for_temp < window->va_for_temp) {
 39			list_add_tail(&window->list, head);
 40			return;
 41		}
 42	}
 43	list_for_each(item, head) {
 44		curr = list_entry(item, struct scif_window, list);
 45		if (curr->va_for_temp > window->va_for_temp)
 46			break;
 47		prev = curr;
 48	}
 49	list_add(&window->list, &prev->list);
 50}
 51
 52/*
 53 * scif_insert_window:
 54 *
 55 * Insert a window to the self registration list sorted by offset.
 56 * RMA lock must be held.
 57 */
 58void scif_insert_window(struct scif_window *window, struct list_head *head)
 59{
 60	struct scif_window *curr = NULL, *prev = NULL;
 61	struct list_head *item;
 62
 63	INIT_LIST_HEAD(&window->list);
 64	list_for_each(item, head) {
 65		curr = list_entry(item, struct scif_window, list);
 66		if (curr->offset > window->offset)
 67			break;
 68		prev = curr;
 69	}
 70	if (!prev)
 71		list_add(&window->list, head);
 72	else
 73		list_add(&window->list, &prev->list);
 74	scif_set_window_ref(window, window->nr_pages);
 75}
 76
 77/*
 78 * scif_query_tcw:
 79 *
 80 * Query the temp cached registration list of ep for an overlapping window
 81 * in case of permission mismatch, destroy the previous window. if permissions
 82 * match and overlap is partial, destroy the window but return the new range
 83 * RMA lock must be held.
 84 */
 85int scif_query_tcw(struct scif_endpt *ep, struct scif_rma_req *req)
 86{
 87	struct list_head *item, *temp, *head = req->head;
 88	struct scif_window *window;
 89	u64 start_va_window, start_va_req = req->va_for_temp;
 90	u64 end_va_window, end_va_req = start_va_req + req->nr_bytes;
 91
 92	if (!req->nr_bytes)
 93		return -EINVAL;
 94	/*
 95	 * Avoid traversing the entire list to find out that there
 96	 * is no entry that matches
 97	 */
 98	if (!list_empty(head)) {
 99		window = list_last_entry(head, struct scif_window, list);
100		end_va_window = window->va_for_temp +
101			(window->nr_pages << PAGE_SHIFT);
102		if (start_va_req > end_va_window)
103			return -ENXIO;
104	}
105	list_for_each_safe(item, temp, head) {
106		window = list_entry(item, struct scif_window, list);
107		start_va_window = window->va_for_temp;
108		end_va_window = window->va_for_temp +
109			(window->nr_pages << PAGE_SHIFT);
110		if (start_va_req < start_va_window &&
111		    end_va_req < start_va_window)
112			break;
113		if (start_va_req >= end_va_window)
114			continue;
115		if ((window->prot & req->prot) == req->prot) {
116			if (start_va_req >= start_va_window &&
117			    end_va_req <= end_va_window) {
118				*req->out_window = window;
119				return 0;
120			}
121			/* expand window */
122			if (start_va_req < start_va_window) {
123				req->nr_bytes +=
124					start_va_window - start_va_req;
125				req->va_for_temp = start_va_window;
126			}
127			if (end_va_req >= end_va_window)
128				req->nr_bytes += end_va_window - end_va_req;
129		}
130		/* Destroy the old window to create a new one */
131		__scif_rma_destroy_tcw_helper(window);
132		break;
133	}
134	return -ENXIO;
135}
136
137/*
138 * scif_query_window:
139 *
140 * Query the registration list and check if a valid contiguous
141 * range of windows exist.
142 * RMA lock must be held.
143 */
144int scif_query_window(struct scif_rma_req *req)
145{
146	struct list_head *item;
147	struct scif_window *window;
148	s64 end_offset, offset = req->offset;
149	u64 tmp_min, nr_bytes_left = req->nr_bytes;
150
151	if (!req->nr_bytes)
152		return -EINVAL;
153
154	list_for_each(item, req->head) {
155		window = list_entry(item, struct scif_window, list);
156		end_offset = window->offset +
157			(window->nr_pages << PAGE_SHIFT);
158		if (offset < window->offset)
159			/* Offset not found! */
160			return -ENXIO;
161		if (offset >= end_offset)
162			continue;
163		/* Check read/write protections. */
164		if ((window->prot & req->prot) != req->prot)
165			return -EPERM;
166		if (nr_bytes_left == req->nr_bytes)
167			/* Store the first window */
168			*req->out_window = window;
169		tmp_min = min((u64)end_offset - offset, nr_bytes_left);
170		nr_bytes_left -= tmp_min;
171		offset += tmp_min;
172		/*
173		 * Range requested encompasses
174		 * multiple windows contiguously.
175		 */
176		if (!nr_bytes_left) {
177			/* Done for partial window */
178			if (req->type == SCIF_WINDOW_PARTIAL ||
179			    req->type == SCIF_WINDOW_SINGLE)
180				return 0;
181			/* Extra logic for full windows */
182			if (offset == end_offset)
183				/* Spanning multiple whole windows */
184				return 0;
185				/* Not spanning multiple whole windows */
186			return -ENXIO;
187		}
188		if (req->type == SCIF_WINDOW_SINGLE)
189			break;
190	}
191	dev_err(scif_info.mdev.this_device,
192		"%s %d ENXIO\n", __func__, __LINE__);
193	return -ENXIO;
194}
195
196/*
197 * scif_rma_list_unregister:
198 *
199 * Traverse the self registration list starting from window:
200 * 1) Call scif_unregister_window(..)
201 * RMA lock must be held.
202 */
203int scif_rma_list_unregister(struct scif_window *window,
204			     s64 offset, int nr_pages)
205{
206	struct scif_endpt *ep = (struct scif_endpt *)window->ep;
207	struct list_head *head = &ep->rma_info.reg_list;
208	s64 end_offset;
209	int err = 0;
210	int loop_nr_pages;
211	struct scif_window *_window;
212
213	list_for_each_entry_safe_from(window, _window, head, list) {
214		end_offset = window->offset + (window->nr_pages << PAGE_SHIFT);
215		loop_nr_pages = min((int)((end_offset - offset) >> PAGE_SHIFT),
216				    nr_pages);
217		err = scif_unregister_window(window);
218		if (err)
219			return err;
220		nr_pages -= loop_nr_pages;
221		offset += (loop_nr_pages << PAGE_SHIFT);
222		if (!nr_pages)
223			break;
224	}
225	return 0;
226}
227
228/*
229 * scif_unmap_all_window:
230 *
231 * Traverse all the windows in the self registration list and:
232 * 1) Delete any DMA mappings created
233 */
234void scif_unmap_all_windows(scif_epd_t epd)
235{
236	struct list_head *item, *tmp;
237	struct scif_window *window;
238	struct scif_endpt *ep = (struct scif_endpt *)epd;
239	struct list_head *head = &ep->rma_info.reg_list;
240
241	mutex_lock(&ep->rma_info.rma_lock);
242	list_for_each_safe(item, tmp, head) {
243		window = list_entry(item, struct scif_window, list);
244		scif_unmap_window(ep->remote_dev, window);
245	}
246	mutex_unlock(&ep->rma_info.rma_lock);
247}
248
249/*
250 * scif_unregister_all_window:
251 *
252 * Traverse all the windows in the self registration list and:
253 * 1) Call scif_unregister_window(..)
254 * RMA lock must be held.
255 */
256int scif_unregister_all_windows(scif_epd_t epd)
257{
258	struct list_head *item, *tmp;
259	struct scif_window *window;
260	struct scif_endpt *ep = (struct scif_endpt *)epd;
261	struct list_head *head = &ep->rma_info.reg_list;
262	int err = 0;
263
264	mutex_lock(&ep->rma_info.rma_lock);
265retry:
266	item = NULL;
267	tmp = NULL;
268	list_for_each_safe(item, tmp, head) {
269		window = list_entry(item, struct scif_window, list);
270		ep->rma_info.async_list_del = 0;
271		err = scif_unregister_window(window);
272		if (err)
273			dev_err(scif_info.mdev.this_device,
274				"%s %d err %d\n",
275				__func__, __LINE__, err);
276		/*
277		 * Need to restart list traversal if there has been
278		 * an asynchronous list entry deletion.
279		 */
280		if (ACCESS_ONCE(ep->rma_info.async_list_del))
281			goto retry;
282	}
283	mutex_unlock(&ep->rma_info.rma_lock);
284	if (!list_empty(&ep->rma_info.mmn_list)) {
285		spin_lock(&scif_info.rmalock);
286		list_add_tail(&ep->mmu_list, &scif_info.mmu_notif_cleanup);
287		spin_unlock(&scif_info.rmalock);
288		schedule_work(&scif_info.mmu_notif_work);
289	}
290	return err;
291}