2 * Intel MIC Platform Software Stack (MPSS)
4 * Copyright(c) 2015 Intel Corporation.
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.
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.
18 #include "scif_main.h"
19 #include <linux/mmu_notifier.h>
20 #include <linux/highmem.h>
25 * Insert a temp window to the temp registration list sorted by va_for_temp.
26 * RMA lock must be held.
28 void scif_insert_tcw(struct scif_window
*window
, struct list_head
*head
)
30 struct scif_window
*curr
= NULL
;
31 struct scif_window
*prev
= list_entry(head
, struct scif_window
, list
);
32 struct list_head
*item
;
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
);
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
)
49 list_add(&window
->list
, &prev
->list
);
55 * Insert a window to the self registration list sorted by offset.
56 * RMA lock must be held.
58 void scif_insert_window(struct scif_window
*window
, struct list_head
*head
)
60 struct scif_window
*curr
= NULL
, *prev
= NULL
;
61 struct list_head
*item
;
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
)
71 list_add(&window
->list
, head
);
73 list_add(&window
->list
, &prev
->list
);
74 scif_set_window_ref(window
, window
->nr_pages
);
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.
85 int scif_query_tcw(struct scif_endpt
*ep
, struct scif_rma_req
*req
)
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
;
95 * Avoid traversing the entire list to find out that there
96 * is no entry that matches
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
)
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
)
113 if (start_va_req
>= end_va_window
)
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
;
122 if (start_va_req
< start_va_window
) {
124 start_va_window
- start_va_req
;
125 req
->va_for_temp
= start_va_window
;
127 if (end_va_req
>= end_va_window
)
128 req
->nr_bytes
+= end_va_window
- end_va_req
;
130 /* Destroy the old window to create a new one */
131 __scif_rma_destroy_tcw_helper(window
);
140 * Query the registration list and check if a valid contiguous
141 * range of windows exist.
142 * RMA lock must be held.
144 int scif_query_window(struct scif_rma_req
*req
)
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
;
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! */
161 if (offset
>= end_offset
)
163 /* Check read/write protections. */
164 if ((window
->prot
& req
->prot
) != req
->prot
)
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
;
173 * Range requested encompasses
174 * multiple windows contiguously.
176 if (!nr_bytes_left
) {
177 /* Done for partial window */
178 if (req
->type
== SCIF_WINDOW_PARTIAL
||
179 req
->type
== SCIF_WINDOW_SINGLE
)
181 /* Extra logic for full windows */
182 if (offset
== end_offset
)
183 /* Spanning multiple whole windows */
185 /* Not spanning multiple whole windows */
188 if (req
->type
== SCIF_WINDOW_SINGLE
)
191 dev_err(scif_info
.mdev
.this_device
,
192 "%s %d ENXIO\n", __func__
, __LINE__
);
197 * scif_rma_list_unregister:
199 * Traverse the self registration list starting from window:
200 * 1) Call scif_unregister_window(..)
201 * RMA lock must be held.
203 int scif_rma_list_unregister(struct scif_window
*window
,
204 s64 offset
, int nr_pages
)
206 struct scif_endpt
*ep
= (struct scif_endpt
*)window
->ep
;
207 struct list_head
*head
= &ep
->rma_info
.reg_list
;
211 struct scif_window
*_window
;
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
),
217 err
= scif_unregister_window(window
);
220 nr_pages
-= loop_nr_pages
;
221 offset
+= (loop_nr_pages
<< PAGE_SHIFT
);
229 * scif_unmap_all_window:
231 * Traverse all the windows in the self registration list and:
232 * 1) Delete any DMA mappings created
234 void scif_unmap_all_windows(scif_epd_t epd
)
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
;
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
);
246 mutex_unlock(&ep
->rma_info
.rma_lock
);
250 * scif_unregister_all_window:
252 * Traverse all the windows in the self registration list and:
253 * 1) Call scif_unregister_window(..)
254 * RMA lock must be held.
256 int scif_unregister_all_windows(scif_epd_t epd
)
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
;
264 mutex_lock(&ep
->rma_info
.rma_lock
);
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
);
273 dev_err(scif_info
.mdev
.this_device
,
275 __func__
, __LINE__
, err
);
277 * Need to restart list traversal if there has been
278 * an asynchronous list entry deletion.
280 if (ACCESS_ONCE(ep
->rma_info
.async_list_del
))
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
);