ia64/xen-unstable

view linux-2.6-xen-sparse/drivers/xen/core/gnttab.c @ 14100:e47738923a05

[LINUX] Purge include <linux/config.h>. It has been obsolete for some time now.

Signed-off-by: Ian Campbell <ian.campbell@xensource.com>
author Ian Campbell <ian.campbell@xensource.com>
date Fri Feb 23 16:56:45 2007 +0000 (2007-02-23)
parents 77298360e365
children 2bbd28891160 5d7fb634ec1a
line source
1 /******************************************************************************
2 * gnttab.c
3 *
4 * Granting foreign access to our memory reservation.
5 *
6 * Copyright (c) 2005-2006, 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/module.h>
35 #include <linux/sched.h>
36 #include <linux/mm.h>
37 #include <xen/interface/xen.h>
38 #include <xen/gnttab.h>
39 #include <asm/pgtable.h>
40 #include <asm/uaccess.h>
41 #include <asm/synch_bitops.h>
42 #include <asm/io.h>
43 #include <xen/interface/memory.h>
44 #include <xen/driver_util.h>
46 #ifdef HAVE_XEN_PLATFORM_COMPAT_H
47 #include <xen/platform-compat.h>
48 #endif
50 /* External tools reserve first few grant table entries. */
51 #define NR_RESERVED_ENTRIES 8
52 #define GNTTAB_LIST_END 0xffffffff
53 #define GREFS_PER_GRANT_FRAME (PAGE_SIZE / sizeof(grant_entry_t))
55 static grant_ref_t **gnttab_list;
56 static unsigned int nr_grant_frames;
57 static unsigned int boot_max_nr_grant_frames;
58 static int gnttab_free_count;
59 static grant_ref_t gnttab_free_head;
60 static DEFINE_SPINLOCK(gnttab_list_lock);
62 static struct grant_entry *shared;
63 #ifndef CONFIG_XEN
64 static unsigned long resume_frames;
65 #endif
67 static struct gnttab_free_callback *gnttab_free_callback_list;
69 static int gnttab_expand(unsigned int req_entries);
71 #define RPP (PAGE_SIZE / sizeof(grant_ref_t))
72 #define gnttab_entry(entry) (gnttab_list[(entry) / RPP][(entry) % RPP])
74 static int get_free_entries(int count)
75 {
76 unsigned long flags;
77 int ref, rc;
78 grant_ref_t head;
80 spin_lock_irqsave(&gnttab_list_lock, flags);
82 if ((gnttab_free_count < count) &&
83 ((rc = gnttab_expand(count - gnttab_free_count)) < 0)) {
84 spin_unlock_irqrestore(&gnttab_list_lock, flags);
85 return rc;
86 }
88 ref = head = gnttab_free_head;
89 gnttab_free_count -= count;
90 while (count-- > 1)
91 head = gnttab_entry(head);
92 gnttab_free_head = gnttab_entry(head);
93 gnttab_entry(head) = GNTTAB_LIST_END;
95 spin_unlock_irqrestore(&gnttab_list_lock, flags);
97 return ref;
98 }
100 #define get_free_entry() get_free_entries(1)
102 static void do_free_callbacks(void)
103 {
104 struct gnttab_free_callback *callback, *next;
106 callback = gnttab_free_callback_list;
107 gnttab_free_callback_list = NULL;
109 while (callback != NULL) {
110 next = callback->next;
111 if (gnttab_free_count >= callback->count) {
112 callback->next = NULL;
113 callback->fn(callback->arg);
114 } else {
115 callback->next = gnttab_free_callback_list;
116 gnttab_free_callback_list = callback;
117 }
118 callback = next;
119 }
120 }
122 static inline void check_free_callbacks(void)
123 {
124 if (unlikely(gnttab_free_callback_list))
125 do_free_callbacks();
126 }
128 static void put_free_entry(grant_ref_t ref)
129 {
130 unsigned long flags;
131 spin_lock_irqsave(&gnttab_list_lock, flags);
132 gnttab_entry(ref) = gnttab_free_head;
133 gnttab_free_head = ref;
134 gnttab_free_count++;
135 check_free_callbacks();
136 spin_unlock_irqrestore(&gnttab_list_lock, flags);
137 }
139 /*
140 * Public grant-issuing interface functions
141 */
143 int gnttab_grant_foreign_access(domid_t domid, unsigned long frame,
144 int readonly)
145 {
146 int ref;
148 if (unlikely((ref = get_free_entry()) < 0))
149 return -ENOSPC;
151 shared[ref].frame = frame;
152 shared[ref].domid = domid;
153 wmb();
154 shared[ref].flags = GTF_permit_access | (readonly ? GTF_readonly : 0);
156 return ref;
157 }
158 EXPORT_SYMBOL_GPL(gnttab_grant_foreign_access);
160 void gnttab_grant_foreign_access_ref(grant_ref_t ref, domid_t domid,
161 unsigned long frame, int readonly)
162 {
163 shared[ref].frame = frame;
164 shared[ref].domid = domid;
165 wmb();
166 shared[ref].flags = GTF_permit_access | (readonly ? GTF_readonly : 0);
167 }
168 EXPORT_SYMBOL_GPL(gnttab_grant_foreign_access_ref);
171 int gnttab_query_foreign_access(grant_ref_t ref)
172 {
173 u16 nflags;
175 nflags = shared[ref].flags;
177 return (nflags & (GTF_reading|GTF_writing));
178 }
179 EXPORT_SYMBOL_GPL(gnttab_query_foreign_access);
181 int gnttab_end_foreign_access_ref(grant_ref_t ref, int readonly)
182 {
183 u16 flags, nflags;
185 nflags = shared[ref].flags;
186 do {
187 if ((flags = nflags) & (GTF_reading|GTF_writing)) {
188 printk(KERN_ALERT "WARNING: g.e. still in use!\n");
189 return 0;
190 }
191 } while ((nflags = synch_cmpxchg_subword(&shared[ref].flags, flags, 0)) !=
192 flags);
194 return 1;
195 }
196 EXPORT_SYMBOL_GPL(gnttab_end_foreign_access_ref);
198 void gnttab_end_foreign_access(grant_ref_t ref, int readonly,
199 unsigned long page)
200 {
201 if (gnttab_end_foreign_access_ref(ref, readonly)) {
202 put_free_entry(ref);
203 if (page != 0)
204 free_page(page);
205 } else {
206 /* XXX This needs to be fixed so that the ref and page are
207 placed on a list to be freed up later. */
208 printk(KERN_WARNING
209 "WARNING: leaking g.e. and page still in use!\n");
210 }
211 }
212 EXPORT_SYMBOL_GPL(gnttab_end_foreign_access);
214 int gnttab_grant_foreign_transfer(domid_t domid, unsigned long pfn)
215 {
216 int ref;
218 if (unlikely((ref = get_free_entry()) < 0))
219 return -ENOSPC;
220 gnttab_grant_foreign_transfer_ref(ref, domid, pfn);
222 return ref;
223 }
224 EXPORT_SYMBOL_GPL(gnttab_grant_foreign_transfer);
226 void gnttab_grant_foreign_transfer_ref(grant_ref_t ref, domid_t domid,
227 unsigned long pfn)
228 {
229 shared[ref].frame = pfn;
230 shared[ref].domid = domid;
231 wmb();
232 shared[ref].flags = GTF_accept_transfer;
233 }
234 EXPORT_SYMBOL_GPL(gnttab_grant_foreign_transfer_ref);
236 unsigned long gnttab_end_foreign_transfer_ref(grant_ref_t ref)
237 {
238 unsigned long frame;
239 u16 flags;
241 /*
242 * If a transfer is not even yet started, try to reclaim the grant
243 * reference and return failure (== 0).
244 */
245 while (!((flags = shared[ref].flags) & GTF_transfer_committed)) {
246 if (synch_cmpxchg_subword(&shared[ref].flags, flags, 0) == flags)
247 return 0;
248 cpu_relax();
249 }
251 /* If a transfer is in progress then wait until it is completed. */
252 while (!(flags & GTF_transfer_completed)) {
253 flags = shared[ref].flags;
254 cpu_relax();
255 }
257 /* Read the frame number /after/ reading completion status. */
258 rmb();
259 frame = shared[ref].frame;
260 BUG_ON(frame == 0);
262 return frame;
263 }
264 EXPORT_SYMBOL_GPL(gnttab_end_foreign_transfer_ref);
266 unsigned long gnttab_end_foreign_transfer(grant_ref_t ref)
267 {
268 unsigned long frame = gnttab_end_foreign_transfer_ref(ref);
269 put_free_entry(ref);
270 return frame;
271 }
272 EXPORT_SYMBOL_GPL(gnttab_end_foreign_transfer);
274 void gnttab_free_grant_reference(grant_ref_t ref)
275 {
276 put_free_entry(ref);
277 }
278 EXPORT_SYMBOL_GPL(gnttab_free_grant_reference);
280 void gnttab_free_grant_references(grant_ref_t head)
281 {
282 grant_ref_t ref;
283 unsigned long flags;
284 int count = 1;
285 if (head == GNTTAB_LIST_END)
286 return;
287 spin_lock_irqsave(&gnttab_list_lock, flags);
288 ref = head;
289 while (gnttab_entry(ref) != GNTTAB_LIST_END) {
290 ref = gnttab_entry(ref);
291 count++;
292 }
293 gnttab_entry(ref) = gnttab_free_head;
294 gnttab_free_head = head;
295 gnttab_free_count += count;
296 check_free_callbacks();
297 spin_unlock_irqrestore(&gnttab_list_lock, flags);
298 }
299 EXPORT_SYMBOL_GPL(gnttab_free_grant_references);
301 int gnttab_alloc_grant_references(u16 count, grant_ref_t *head)
302 {
303 int h = get_free_entries(count);
305 if (h < 0)
306 return -ENOSPC;
308 *head = h;
310 return 0;
311 }
312 EXPORT_SYMBOL_GPL(gnttab_alloc_grant_references);
314 int gnttab_empty_grant_references(const grant_ref_t *private_head)
315 {
316 return (*private_head == GNTTAB_LIST_END);
317 }
318 EXPORT_SYMBOL_GPL(gnttab_empty_grant_references);
320 int gnttab_claim_grant_reference(grant_ref_t *private_head)
321 {
322 grant_ref_t g = *private_head;
323 if (unlikely(g == GNTTAB_LIST_END))
324 return -ENOSPC;
325 *private_head = gnttab_entry(g);
326 return g;
327 }
328 EXPORT_SYMBOL_GPL(gnttab_claim_grant_reference);
330 void gnttab_release_grant_reference(grant_ref_t *private_head,
331 grant_ref_t release)
332 {
333 gnttab_entry(release) = *private_head;
334 *private_head = release;
335 }
336 EXPORT_SYMBOL_GPL(gnttab_release_grant_reference);
338 void gnttab_request_free_callback(struct gnttab_free_callback *callback,
339 void (*fn)(void *), void *arg, u16 count)
340 {
341 unsigned long flags;
342 spin_lock_irqsave(&gnttab_list_lock, flags);
343 if (callback->next)
344 goto out;
345 callback->fn = fn;
346 callback->arg = arg;
347 callback->count = count;
348 callback->next = gnttab_free_callback_list;
349 gnttab_free_callback_list = callback;
350 check_free_callbacks();
351 out:
352 spin_unlock_irqrestore(&gnttab_list_lock, flags);
353 }
354 EXPORT_SYMBOL_GPL(gnttab_request_free_callback);
356 void gnttab_cancel_free_callback(struct gnttab_free_callback *callback)
357 {
358 struct gnttab_free_callback **pcb;
359 unsigned long flags;
361 spin_lock_irqsave(&gnttab_list_lock, flags);
362 for (pcb = &gnttab_free_callback_list; *pcb; pcb = &(*pcb)->next) {
363 if (*pcb == callback) {
364 *pcb = callback->next;
365 break;
366 }
367 }
368 spin_unlock_irqrestore(&gnttab_list_lock, flags);
369 }
370 EXPORT_SYMBOL_GPL(gnttab_cancel_free_callback);
372 static int grow_gnttab_list(unsigned int more_frames)
373 {
374 unsigned int new_nr_grant_frames, extra_entries, i;
376 new_nr_grant_frames = nr_grant_frames + more_frames;
377 extra_entries = more_frames * GREFS_PER_GRANT_FRAME;
379 for (i = nr_grant_frames; i < new_nr_grant_frames; i++)
380 {
381 gnttab_list[i] = (grant_ref_t *)__get_free_page(GFP_ATOMIC);
382 if (!gnttab_list[i])
383 goto grow_nomem;
384 }
387 for (i = GREFS_PER_GRANT_FRAME * nr_grant_frames;
388 i < GREFS_PER_GRANT_FRAME * new_nr_grant_frames - 1; i++)
389 gnttab_entry(i) = i + 1;
391 gnttab_entry(i) = gnttab_free_head;
392 gnttab_free_head = GREFS_PER_GRANT_FRAME * nr_grant_frames;
393 gnttab_free_count += extra_entries;
395 nr_grant_frames = new_nr_grant_frames;
397 check_free_callbacks();
399 return 0;
401 grow_nomem:
402 for ( ; i >= nr_grant_frames; i--)
403 free_page((unsigned long) gnttab_list[i]);
404 return -ENOMEM;
405 }
407 static unsigned int __max_nr_grant_frames(void)
408 {
409 struct gnttab_query_size query;
410 int rc;
412 query.dom = DOMID_SELF;
414 rc = HYPERVISOR_grant_table_op(GNTTABOP_query_size, &query, 1);
415 if ((rc < 0) || (query.status != GNTST_okay))
416 return 4; /* Legacy max supported number of frames */
418 return query.max_nr_frames;
419 }
421 static inline unsigned int max_nr_grant_frames(void)
422 {
423 unsigned int xen_max = __max_nr_grant_frames();
425 if (xen_max > boot_max_nr_grant_frames)
426 return boot_max_nr_grant_frames;
427 return xen_max;
428 }
430 #ifdef CONFIG_XEN
432 #ifndef __ia64__
433 static int map_pte_fn(pte_t *pte, struct page *pmd_page,
434 unsigned long addr, void *data)
435 {
436 unsigned long **frames = (unsigned long **)data;
438 set_pte_at(&init_mm, addr, pte, pfn_pte_ma((*frames)[0], PAGE_KERNEL));
439 (*frames)++;
440 return 0;
441 }
443 static int unmap_pte_fn(pte_t *pte, struct page *pmd_page,
444 unsigned long addr, void *data)
445 {
447 set_pte_at(&init_mm, addr, pte, __pte(0));
448 return 0;
449 }
450 #endif
452 static int gnttab_map(unsigned int start_idx, unsigned int end_idx)
453 {
454 struct gnttab_setup_table setup;
455 unsigned long *frames;
456 unsigned int nr_gframes = end_idx + 1;
457 int rc;
459 frames = kmalloc(nr_gframes * sizeof(unsigned long), GFP_ATOMIC);
460 if (!frames)
461 return -ENOMEM;
463 setup.dom = DOMID_SELF;
464 setup.nr_frames = nr_gframes;
465 set_xen_guest_handle(setup.frame_list, frames);
467 rc = HYPERVISOR_grant_table_op(GNTTABOP_setup_table, &setup, 1);
468 if (rc == -ENOSYS) {
469 kfree(frames);
470 return -ENOSYS;
471 }
473 BUG_ON(rc || setup.status);
475 #ifndef __ia64__
476 if (shared == NULL) {
477 struct vm_struct *area;
478 area = alloc_vm_area(PAGE_SIZE * max_nr_grant_frames());
479 BUG_ON(area == NULL);
480 shared = area->addr;
481 }
482 rc = apply_to_page_range(&init_mm, (unsigned long)shared,
483 PAGE_SIZE * nr_gframes,
484 map_pte_fn, &frames);
485 BUG_ON(rc);
486 frames -= nr_gframes; /* adjust after map_pte_fn() */
487 #else
488 shared = __va(frames[0] << PAGE_SHIFT);
489 #endif
491 kfree(frames);
493 return 0;
494 }
496 int gnttab_resume(void)
497 {
498 if (max_nr_grant_frames() < nr_grant_frames)
499 return -ENOSYS;
500 return gnttab_map(0, nr_grant_frames - 1);
501 }
503 int gnttab_suspend(void)
504 {
505 #ifndef __ia64__
506 apply_to_page_range(&init_mm, (unsigned long)shared,
507 PAGE_SIZE * nr_grant_frames,
508 unmap_pte_fn, NULL);
509 #endif
510 return 0;
511 }
513 #else /* !CONFIG_XEN */
515 #include <platform-pci.h>
517 static int gnttab_map(unsigned int start_idx, unsigned int end_idx)
518 {
519 struct xen_add_to_physmap xatp;
520 unsigned int i = end_idx;
522 /* Loop backwards, so that the first hypercall has the largest index,
523 * ensuring that the table will grow only once.
524 */
525 do {
526 xatp.domid = DOMID_SELF;
527 xatp.idx = i;
528 xatp.space = XENMAPSPACE_grant_table;
529 xatp.gpfn = (resume_frames >> PAGE_SHIFT) + i;
530 if (HYPERVISOR_memory_op(XENMEM_add_to_physmap, &xatp))
531 BUG();
532 } while (i-- > start_idx);
534 return 0;
535 }
537 int gnttab_resume(void)
538 {
539 unsigned int max_nr_gframes, nr_gframes;
541 nr_gframes = nr_grant_frames;
542 max_nr_gframes = max_nr_grant_frames();
543 if (max_nr_gframes < nr_gframes)
544 return -ENOSYS;
546 resume_frames = alloc_xen_mmio(PAGE_SIZE * max_nr_gframes);
548 gnttab_map(0, nr_gframes - 1);
550 shared = ioremap(resume_frames, PAGE_SIZE * max_nr_gframes);
551 if (shared == NULL) {
552 printk("error to ioremap gnttab share frames\n");
553 return -1;
554 }
556 return 0;
557 }
559 int gnttab_suspend(void)
560 {
561 iounmap(shared);
562 resume_frames = 0;
563 return 0;
564 }
566 #endif /* !CONFIG_XEN */
568 static int gnttab_expand(unsigned int req_entries)
569 {
570 int rc;
571 unsigned int cur, extra;
573 cur = nr_grant_frames;
574 extra = ((req_entries + (GREFS_PER_GRANT_FRAME-1)) /
575 GREFS_PER_GRANT_FRAME);
576 if (cur + extra > max_nr_grant_frames())
577 return -ENOSPC;
579 if ((rc = gnttab_map(cur, cur + extra - 1)) == 0)
580 rc = grow_gnttab_list(extra);
582 return rc;
583 }
585 int __devinit gnttab_init(void)
586 {
587 int i;
588 unsigned int max_nr_glist_frames;
589 unsigned int nr_init_grefs;
591 if (!is_running_on_xen())
592 return -ENODEV;
594 nr_grant_frames = 1;
595 boot_max_nr_grant_frames = __max_nr_grant_frames();
597 /* Determine the maximum number of frames required for the
598 * grant reference free list on the current hypervisor.
599 */
600 max_nr_glist_frames = (boot_max_nr_grant_frames *
601 GREFS_PER_GRANT_FRAME /
602 (PAGE_SIZE / sizeof(grant_ref_t)));
604 gnttab_list = kmalloc(max_nr_glist_frames * sizeof(grant_ref_t *),
605 GFP_KERNEL);
606 if (gnttab_list == NULL)
607 return -ENOMEM;
609 for (i = 0; i < nr_grant_frames; i++) {
610 gnttab_list[i] = (grant_ref_t *)__get_free_page(GFP_KERNEL);
611 if (gnttab_list[i] == NULL)
612 goto ini_nomem;
613 }
615 if (gnttab_resume() < 0)
616 return -ENODEV;
618 nr_init_grefs = nr_grant_frames * GREFS_PER_GRANT_FRAME;
620 for (i = NR_RESERVED_ENTRIES; i < nr_init_grefs - 1; i++)
621 gnttab_entry(i) = i + 1;
623 gnttab_entry(nr_init_grefs - 1) = GNTTAB_LIST_END;
624 gnttab_free_count = nr_init_grefs - NR_RESERVED_ENTRIES;
625 gnttab_free_head = NR_RESERVED_ENTRIES;
627 printk("Grant table initialized\n");
628 return 0;
630 ini_nomem:
631 for (i--; i >= 0; i--)
632 free_page((unsigned long)gnttab_list[i]);
633 kfree(gnttab_list);
634 return -ENOMEM;
635 }
637 #ifdef CONFIG_XEN
638 core_initcall(gnttab_init);
639 #endif