ia64/xen-unstable

view linux-2.6-xen-sparse/drivers/xen/core/gnttab.c @ 10614:86cae321e707

Use explicit accessors to handle unusually-sized atomic operations in grant table code.
Signed-off-by: Hollis Blanchard <hollisb@us.ibm.com>
author kaf24@firebug.cl.cam.ac.uk
date Fri Jun 30 10:10:39 2006 +0100 (2006-06-30)
parents 231e07e22f9c
children b2f077bbca89
line source
1 /******************************************************************************
2 * gnttab.c
3 *
4 * Granting foreign access to our memory reservation.
5 *
6 * Copyright (c) 2005, Christopher Clark
7 * Copyright (c) 2004-2005, K A Fraser
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License version 2
11 * as published by the Free Software Foundation; or, when distributed
12 * separately from the Linux kernel or incorporated into other
13 * software packages, subject to the following license:
14 *
15 * Permission is hereby granted, free of charge, to any person obtaining a copy
16 * of this source file (the "Software"), to deal in the Software without
17 * restriction, including without limitation the rights to use, copy, modify,
18 * merge, publish, distribute, sublicense, and/or sell copies of the Software,
19 * and to permit persons to whom the Software is furnished to do so, subject to
20 * the following conditions:
21 *
22 * The above copyright notice and this permission notice shall be included in
23 * all copies or substantial portions of the Software.
24 *
25 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
26 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
27 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
28 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
29 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
30 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
31 * IN THE SOFTWARE.
32 */
34 #include <linux/config.h>
35 #include <linux/module.h>
36 #include <linux/sched.h>
37 #include <linux/mm.h>
38 #include <linux/vmalloc.h>
39 #include <xen/interface/xen.h>
40 #include <xen/gnttab.h>
41 #include <asm/pgtable.h>
42 #include <asm/uaccess.h>
43 #include <asm/synch_bitops.h>
45 /* External tools reserve first few grant table entries. */
46 #define NR_RESERVED_ENTRIES 8
48 #define NR_GRANT_ENTRIES \
49 (NR_GRANT_FRAMES * PAGE_SIZE / sizeof(struct grant_entry))
50 #define GNTTAB_LIST_END (NR_GRANT_ENTRIES + 1)
52 static grant_ref_t gnttab_list[NR_GRANT_ENTRIES];
53 static int gnttab_free_count;
54 static grant_ref_t gnttab_free_head;
55 static DEFINE_SPINLOCK(gnttab_list_lock);
57 static struct grant_entry *shared;
59 static struct gnttab_free_callback *gnttab_free_callback_list;
61 static int get_free_entries(int count)
62 {
63 unsigned long flags;
64 int ref;
65 grant_ref_t head;
66 spin_lock_irqsave(&gnttab_list_lock, flags);
67 if (gnttab_free_count < count) {
68 spin_unlock_irqrestore(&gnttab_list_lock, flags);
69 return -1;
70 }
71 ref = head = gnttab_free_head;
72 gnttab_free_count -= count;
73 while (count-- > 1)
74 head = gnttab_list[head];
75 gnttab_free_head = gnttab_list[head];
76 gnttab_list[head] = GNTTAB_LIST_END;
77 spin_unlock_irqrestore(&gnttab_list_lock, flags);
78 return ref;
79 }
81 #define get_free_entry() get_free_entries(1)
83 static void do_free_callbacks(void)
84 {
85 struct gnttab_free_callback *callback, *next;
87 callback = gnttab_free_callback_list;
88 gnttab_free_callback_list = NULL;
90 while (callback != NULL) {
91 next = callback->next;
92 if (gnttab_free_count >= callback->count) {
93 callback->next = NULL;
94 callback->fn(callback->arg);
95 } else {
96 callback->next = gnttab_free_callback_list;
97 gnttab_free_callback_list = callback;
98 }
99 callback = next;
100 }
101 }
103 static inline void check_free_callbacks(void)
104 {
105 if (unlikely(gnttab_free_callback_list))
106 do_free_callbacks();
107 }
109 static void put_free_entry(grant_ref_t ref)
110 {
111 unsigned long flags;
112 spin_lock_irqsave(&gnttab_list_lock, flags);
113 gnttab_list[ref] = gnttab_free_head;
114 gnttab_free_head = ref;
115 gnttab_free_count++;
116 check_free_callbacks();
117 spin_unlock_irqrestore(&gnttab_list_lock, flags);
118 }
120 /*
121 * Public grant-issuing interface functions
122 */
124 int gnttab_grant_foreign_access(domid_t domid, unsigned long frame,
125 int readonly)
126 {
127 int ref;
129 if (unlikely((ref = get_free_entry()) == -1))
130 return -ENOSPC;
132 shared[ref].frame = frame;
133 shared[ref].domid = domid;
134 wmb();
135 shared[ref].flags = GTF_permit_access | (readonly ? GTF_readonly : 0);
137 return ref;
138 }
139 EXPORT_SYMBOL_GPL(gnttab_grant_foreign_access);
141 void gnttab_grant_foreign_access_ref(grant_ref_t ref, domid_t domid,
142 unsigned long frame, int readonly)
143 {
144 shared[ref].frame = frame;
145 shared[ref].domid = domid;
146 wmb();
147 shared[ref].flags = GTF_permit_access | (readonly ? GTF_readonly : 0);
148 }
149 EXPORT_SYMBOL_GPL(gnttab_grant_foreign_access_ref);
152 int gnttab_query_foreign_access(grant_ref_t ref)
153 {
154 u16 nflags;
156 nflags = shared[ref].flags;
158 return (nflags & (GTF_reading|GTF_writing));
159 }
160 EXPORT_SYMBOL_GPL(gnttab_query_foreign_access);
162 int gnttab_end_foreign_access_ref(grant_ref_t ref, int readonly)
163 {
164 u16 flags, nflags;
166 nflags = shared[ref].flags;
167 do {
168 if ((flags = nflags) & (GTF_reading|GTF_writing)) {
169 printk(KERN_ALERT "WARNING: g.e. still in use!\n");
170 return 0;
171 }
172 } while ((nflags = synch_cmpxchg_subword(&shared[ref].flags, flags, 0)) !=
173 flags);
175 return 1;
176 }
177 EXPORT_SYMBOL_GPL(gnttab_end_foreign_access_ref);
179 void gnttab_end_foreign_access(grant_ref_t ref, int readonly,
180 unsigned long page)
181 {
182 if (gnttab_end_foreign_access_ref(ref, readonly)) {
183 put_free_entry(ref);
184 if (page != 0)
185 free_page(page);
186 } else {
187 /* XXX This needs to be fixed so that the ref and page are
188 placed on a list to be freed up later. */
189 printk(KERN_WARNING
190 "WARNING: leaking g.e. and page still in use!\n");
191 }
192 }
193 EXPORT_SYMBOL_GPL(gnttab_end_foreign_access);
195 int gnttab_grant_foreign_transfer(domid_t domid, unsigned long pfn)
196 {
197 int ref;
199 if (unlikely((ref = get_free_entry()) == -1))
200 return -ENOSPC;
201 gnttab_grant_foreign_transfer_ref(ref, domid, pfn);
203 return ref;
204 }
205 EXPORT_SYMBOL_GPL(gnttab_grant_foreign_transfer);
207 void gnttab_grant_foreign_transfer_ref(grant_ref_t ref, domid_t domid,
208 unsigned long pfn)
209 {
210 shared[ref].frame = pfn;
211 shared[ref].domid = domid;
212 wmb();
213 shared[ref].flags = GTF_accept_transfer;
214 }
215 EXPORT_SYMBOL_GPL(gnttab_grant_foreign_transfer_ref);
217 unsigned long gnttab_end_foreign_transfer_ref(grant_ref_t ref)
218 {
219 unsigned long frame;
220 u16 flags;
222 /*
223 * If a transfer is not even yet started, try to reclaim the grant
224 * reference and return failure (== 0).
225 */
226 while (!((flags = shared[ref].flags) & GTF_transfer_committed)) {
227 if (synch_cmpxchg_subword(&shared[ref].flags, flags, 0) == flags)
228 return 0;
229 cpu_relax();
230 }
232 /* If a transfer is in progress then wait until it is completed. */
233 while (!(flags & GTF_transfer_completed)) {
234 flags = shared[ref].flags;
235 cpu_relax();
236 }
238 /* Read the frame number /after/ reading completion status. */
239 rmb();
240 frame = shared[ref].frame;
241 BUG_ON(frame == 0);
243 return frame;
244 }
245 EXPORT_SYMBOL_GPL(gnttab_end_foreign_transfer_ref);
247 unsigned long gnttab_end_foreign_transfer(grant_ref_t ref)
248 {
249 unsigned long frame = gnttab_end_foreign_transfer_ref(ref);
250 put_free_entry(ref);
251 return frame;
252 }
253 EXPORT_SYMBOL_GPL(gnttab_end_foreign_transfer);
255 void gnttab_free_grant_reference(grant_ref_t ref)
256 {
257 put_free_entry(ref);
258 }
259 EXPORT_SYMBOL_GPL(gnttab_free_grant_reference);
261 void gnttab_free_grant_references(grant_ref_t head)
262 {
263 grant_ref_t ref;
264 unsigned long flags;
265 int count = 1;
266 if (head == GNTTAB_LIST_END)
267 return;
268 spin_lock_irqsave(&gnttab_list_lock, flags);
269 ref = head;
270 while (gnttab_list[ref] != GNTTAB_LIST_END) {
271 ref = gnttab_list[ref];
272 count++;
273 }
274 gnttab_list[ref] = gnttab_free_head;
275 gnttab_free_head = head;
276 gnttab_free_count += count;
277 check_free_callbacks();
278 spin_unlock_irqrestore(&gnttab_list_lock, flags);
279 }
280 EXPORT_SYMBOL_GPL(gnttab_free_grant_references);
282 int gnttab_alloc_grant_references(u16 count, grant_ref_t *head)
283 {
284 int h = get_free_entries(count);
286 if (h == -1)
287 return -ENOSPC;
289 *head = h;
291 return 0;
292 }
293 EXPORT_SYMBOL_GPL(gnttab_alloc_grant_references);
295 int gnttab_empty_grant_references(const grant_ref_t *private_head)
296 {
297 return (*private_head == GNTTAB_LIST_END);
298 }
299 EXPORT_SYMBOL_GPL(gnttab_empty_grant_references);
301 int gnttab_claim_grant_reference(grant_ref_t *private_head)
302 {
303 grant_ref_t g = *private_head;
304 if (unlikely(g == GNTTAB_LIST_END))
305 return -ENOSPC;
306 *private_head = gnttab_list[g];
307 return g;
308 }
309 EXPORT_SYMBOL_GPL(gnttab_claim_grant_reference);
311 void gnttab_release_grant_reference(grant_ref_t *private_head,
312 grant_ref_t release)
313 {
314 gnttab_list[release] = *private_head;
315 *private_head = release;
316 }
317 EXPORT_SYMBOL_GPL(gnttab_release_grant_reference);
319 void gnttab_request_free_callback(struct gnttab_free_callback *callback,
320 void (*fn)(void *), void *arg, u16 count)
321 {
322 unsigned long flags;
323 spin_lock_irqsave(&gnttab_list_lock, flags);
324 if (callback->next)
325 goto out;
326 callback->fn = fn;
327 callback->arg = arg;
328 callback->count = count;
329 callback->next = gnttab_free_callback_list;
330 gnttab_free_callback_list = callback;
331 check_free_callbacks();
332 out:
333 spin_unlock_irqrestore(&gnttab_list_lock, flags);
334 }
335 EXPORT_SYMBOL_GPL(gnttab_request_free_callback);
337 void gnttab_cancel_free_callback(struct gnttab_free_callback *callback)
338 {
339 struct gnttab_free_callback **pcb;
340 unsigned long flags;
342 spin_lock_irqsave(&gnttab_list_lock, flags);
343 for (pcb = &gnttab_free_callback_list; *pcb; pcb = &(*pcb)->next) {
344 if (*pcb == callback) {
345 *pcb = callback->next;
346 break;
347 }
348 }
349 spin_unlock_irqrestore(&gnttab_list_lock, flags);
350 }
351 EXPORT_SYMBOL_GPL(gnttab_cancel_free_callback);
353 #ifndef __ia64__
354 static int map_pte_fn(pte_t *pte, struct page *pmd_page,
355 unsigned long addr, void *data)
356 {
357 unsigned long **frames = (unsigned long **)data;
359 set_pte_at(&init_mm, addr, pte, pfn_pte_ma((*frames)[0], PAGE_KERNEL));
360 (*frames)++;
361 return 0;
362 }
364 static int unmap_pte_fn(pte_t *pte, struct page *pmd_page,
365 unsigned long addr, void *data)
366 {
368 set_pte_at(&init_mm, addr, pte, __pte(0));
369 return 0;
370 }
371 #endif
373 int gnttab_resume(void)
374 {
375 struct gnttab_setup_table setup;
376 unsigned long frames[NR_GRANT_FRAMES];
377 int rc;
378 #ifndef __ia64__
379 void *pframes = frames;
380 struct vm_struct *area;
381 #endif
383 setup.dom = DOMID_SELF;
384 setup.nr_frames = NR_GRANT_FRAMES;
385 set_xen_guest_handle(setup.frame_list, frames);
387 rc = HYPERVISOR_grant_table_op(GNTTABOP_setup_table, &setup, 1);
388 if (rc == -ENOSYS)
389 return -ENOSYS;
391 BUG_ON(rc || setup.status);
393 #ifndef __ia64__
394 if (shared == NULL) {
395 area = get_vm_area(PAGE_SIZE * NR_GRANT_FRAMES, VM_IOREMAP);
396 BUG_ON(area == NULL);
397 shared = area->addr;
398 }
399 rc = apply_to_page_range(&init_mm, (unsigned long)shared,
400 PAGE_SIZE * NR_GRANT_FRAMES,
401 map_pte_fn, &pframes);
402 BUG_ON(rc);
403 #else
404 shared = __va(frames[0] << PAGE_SHIFT);
405 printk("grant table at %p\n", shared);
406 #endif
408 return 0;
409 }
411 int gnttab_suspend(void)
412 {
414 #ifndef __ia64__
415 apply_to_page_range(&init_mm, (unsigned long)shared,
416 PAGE_SIZE * NR_GRANT_FRAMES,
417 unmap_pte_fn, NULL);
418 #endif
420 return 0;
421 }
423 static int __init gnttab_init(void)
424 {
425 int i;
427 if (!is_running_on_xen())
428 return -ENODEV;
430 if (gnttab_resume() < 0)
431 return -ENODEV;
433 for (i = NR_RESERVED_ENTRIES; i < NR_GRANT_ENTRIES; i++)
434 gnttab_list[i] = i + 1;
435 gnttab_free_count = NR_GRANT_ENTRIES - NR_RESERVED_ENTRIES;
436 gnttab_free_head = NR_RESERVED_ENTRIES;
438 printk("Grant table initialized\n");
439 return 0;
440 }
442 core_initcall(gnttab_init);