ia64/xen-unstable

view linux-2.6-xen-sparse/drivers/xen/core/gnttab.c @ 14099:77298360e365

[PV-on-HVM] Fix gnttab_map to not run off the bottom of the gnttab space.

Cleanup a few warnings: reached end of void function and unused variables.

Signed-off-by: Ian Campbell <ian.campbell@xensource.com>
author Ian Campbell <ian.campbell@xensource.com>
date Fri Feb 23 16:56:42 2007 +0000 (2007-02-23)
parents 70f05d642a2e
children e47738923a05
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/config.h>
35 #include <linux/module.h>
36 #include <linux/sched.h>
37 #include <linux/mm.h>
38 #include <xen/interface/xen.h>
39 #include <xen/gnttab.h>
40 #include <asm/pgtable.h>
41 #include <asm/uaccess.h>
42 #include <asm/synch_bitops.h>
43 #include <asm/io.h>
44 #include <xen/interface/memory.h>
45 #include <xen/driver_util.h>
47 #ifdef HAVE_XEN_PLATFORM_COMPAT_H
48 #include <xen/platform-compat.h>
49 #endif
51 /* External tools reserve first few grant table entries. */
52 #define NR_RESERVED_ENTRIES 8
53 #define GNTTAB_LIST_END 0xffffffff
54 #define GREFS_PER_GRANT_FRAME (PAGE_SIZE / sizeof(grant_entry_t))
56 static grant_ref_t **gnttab_list;
57 static unsigned int nr_grant_frames;
58 static unsigned int boot_max_nr_grant_frames;
59 static int gnttab_free_count;
60 static grant_ref_t gnttab_free_head;
61 static DEFINE_SPINLOCK(gnttab_list_lock);
63 static struct grant_entry *shared;
64 #ifndef CONFIG_XEN
65 static unsigned long resume_frames;
66 #endif
68 static struct gnttab_free_callback *gnttab_free_callback_list;
70 static int gnttab_expand(unsigned int req_entries);
72 #define RPP (PAGE_SIZE / sizeof(grant_ref_t))
73 #define gnttab_entry(entry) (gnttab_list[(entry) / RPP][(entry) % RPP])
75 static int get_free_entries(int count)
76 {
77 unsigned long flags;
78 int ref, rc;
79 grant_ref_t head;
81 spin_lock_irqsave(&gnttab_list_lock, flags);
83 if ((gnttab_free_count < count) &&
84 ((rc = gnttab_expand(count - gnttab_free_count)) < 0)) {
85 spin_unlock_irqrestore(&gnttab_list_lock, flags);
86 return rc;
87 }
89 ref = head = gnttab_free_head;
90 gnttab_free_count -= count;
91 while (count-- > 1)
92 head = gnttab_entry(head);
93 gnttab_free_head = gnttab_entry(head);
94 gnttab_entry(head) = GNTTAB_LIST_END;
96 spin_unlock_irqrestore(&gnttab_list_lock, flags);
98 return ref;
99 }
101 #define get_free_entry() get_free_entries(1)
103 static void do_free_callbacks(void)
104 {
105 struct gnttab_free_callback *callback, *next;
107 callback = gnttab_free_callback_list;
108 gnttab_free_callback_list = NULL;
110 while (callback != NULL) {
111 next = callback->next;
112 if (gnttab_free_count >= callback->count) {
113 callback->next = NULL;
114 callback->fn(callback->arg);
115 } else {
116 callback->next = gnttab_free_callback_list;
117 gnttab_free_callback_list = callback;
118 }
119 callback = next;
120 }
121 }
123 static inline void check_free_callbacks(void)
124 {
125 if (unlikely(gnttab_free_callback_list))
126 do_free_callbacks();
127 }
129 static void put_free_entry(grant_ref_t ref)
130 {
131 unsigned long flags;
132 spin_lock_irqsave(&gnttab_list_lock, flags);
133 gnttab_entry(ref) = gnttab_free_head;
134 gnttab_free_head = ref;
135 gnttab_free_count++;
136 check_free_callbacks();
137 spin_unlock_irqrestore(&gnttab_list_lock, flags);
138 }
140 /*
141 * Public grant-issuing interface functions
142 */
144 int gnttab_grant_foreign_access(domid_t domid, unsigned long frame,
145 int readonly)
146 {
147 int ref;
149 if (unlikely((ref = get_free_entry()) < 0))
150 return -ENOSPC;
152 shared[ref].frame = frame;
153 shared[ref].domid = domid;
154 wmb();
155 shared[ref].flags = GTF_permit_access | (readonly ? GTF_readonly : 0);
157 return ref;
158 }
159 EXPORT_SYMBOL_GPL(gnttab_grant_foreign_access);
161 void gnttab_grant_foreign_access_ref(grant_ref_t ref, domid_t domid,
162 unsigned long frame, int readonly)
163 {
164 shared[ref].frame = frame;
165 shared[ref].domid = domid;
166 wmb();
167 shared[ref].flags = GTF_permit_access | (readonly ? GTF_readonly : 0);
168 }
169 EXPORT_SYMBOL_GPL(gnttab_grant_foreign_access_ref);
172 int gnttab_query_foreign_access(grant_ref_t ref)
173 {
174 u16 nflags;
176 nflags = shared[ref].flags;
178 return (nflags & (GTF_reading|GTF_writing));
179 }
180 EXPORT_SYMBOL_GPL(gnttab_query_foreign_access);
182 int gnttab_end_foreign_access_ref(grant_ref_t ref, int readonly)
183 {
184 u16 flags, nflags;
186 nflags = shared[ref].flags;
187 do {
188 if ((flags = nflags) & (GTF_reading|GTF_writing)) {
189 printk(KERN_ALERT "WARNING: g.e. still in use!\n");
190 return 0;
191 }
192 } while ((nflags = synch_cmpxchg_subword(&shared[ref].flags, flags, 0)) !=
193 flags);
195 return 1;
196 }
197 EXPORT_SYMBOL_GPL(gnttab_end_foreign_access_ref);
199 void gnttab_end_foreign_access(grant_ref_t ref, int readonly,
200 unsigned long page)
201 {
202 if (gnttab_end_foreign_access_ref(ref, readonly)) {
203 put_free_entry(ref);
204 if (page != 0)
205 free_page(page);
206 } else {
207 /* XXX This needs to be fixed so that the ref and page are
208 placed on a list to be freed up later. */
209 printk(KERN_WARNING
210 "WARNING: leaking g.e. and page still in use!\n");
211 }
212 }
213 EXPORT_SYMBOL_GPL(gnttab_end_foreign_access);
215 int gnttab_grant_foreign_transfer(domid_t domid, unsigned long pfn)
216 {
217 int ref;
219 if (unlikely((ref = get_free_entry()) < 0))
220 return -ENOSPC;
221 gnttab_grant_foreign_transfer_ref(ref, domid, pfn);
223 return ref;
224 }
225 EXPORT_SYMBOL_GPL(gnttab_grant_foreign_transfer);
227 void gnttab_grant_foreign_transfer_ref(grant_ref_t ref, domid_t domid,
228 unsigned long pfn)
229 {
230 shared[ref].frame = pfn;
231 shared[ref].domid = domid;
232 wmb();
233 shared[ref].flags = GTF_accept_transfer;
234 }
235 EXPORT_SYMBOL_GPL(gnttab_grant_foreign_transfer_ref);
237 unsigned long gnttab_end_foreign_transfer_ref(grant_ref_t ref)
238 {
239 unsigned long frame;
240 u16 flags;
242 /*
243 * If a transfer is not even yet started, try to reclaim the grant
244 * reference and return failure (== 0).
245 */
246 while (!((flags = shared[ref].flags) & GTF_transfer_committed)) {
247 if (synch_cmpxchg_subword(&shared[ref].flags, flags, 0) == flags)
248 return 0;
249 cpu_relax();
250 }
252 /* If a transfer is in progress then wait until it is completed. */
253 while (!(flags & GTF_transfer_completed)) {
254 flags = shared[ref].flags;
255 cpu_relax();
256 }
258 /* Read the frame number /after/ reading completion status. */
259 rmb();
260 frame = shared[ref].frame;
261 BUG_ON(frame == 0);
263 return frame;
264 }
265 EXPORT_SYMBOL_GPL(gnttab_end_foreign_transfer_ref);
267 unsigned long gnttab_end_foreign_transfer(grant_ref_t ref)
268 {
269 unsigned long frame = gnttab_end_foreign_transfer_ref(ref);
270 put_free_entry(ref);
271 return frame;
272 }
273 EXPORT_SYMBOL_GPL(gnttab_end_foreign_transfer);
275 void gnttab_free_grant_reference(grant_ref_t ref)
276 {
277 put_free_entry(ref);
278 }
279 EXPORT_SYMBOL_GPL(gnttab_free_grant_reference);
281 void gnttab_free_grant_references(grant_ref_t head)
282 {
283 grant_ref_t ref;
284 unsigned long flags;
285 int count = 1;
286 if (head == GNTTAB_LIST_END)
287 return;
288 spin_lock_irqsave(&gnttab_list_lock, flags);
289 ref = head;
290 while (gnttab_entry(ref) != GNTTAB_LIST_END) {
291 ref = gnttab_entry(ref);
292 count++;
293 }
294 gnttab_entry(ref) = gnttab_free_head;
295 gnttab_free_head = head;
296 gnttab_free_count += count;
297 check_free_callbacks();
298 spin_unlock_irqrestore(&gnttab_list_lock, flags);
299 }
300 EXPORT_SYMBOL_GPL(gnttab_free_grant_references);
302 int gnttab_alloc_grant_references(u16 count, grant_ref_t *head)
303 {
304 int h = get_free_entries(count);
306 if (h < 0)
307 return -ENOSPC;
309 *head = h;
311 return 0;
312 }
313 EXPORT_SYMBOL_GPL(gnttab_alloc_grant_references);
315 int gnttab_empty_grant_references(const grant_ref_t *private_head)
316 {
317 return (*private_head == GNTTAB_LIST_END);
318 }
319 EXPORT_SYMBOL_GPL(gnttab_empty_grant_references);
321 int gnttab_claim_grant_reference(grant_ref_t *private_head)
322 {
323 grant_ref_t g = *private_head;
324 if (unlikely(g == GNTTAB_LIST_END))
325 return -ENOSPC;
326 *private_head = gnttab_entry(g);
327 return g;
328 }
329 EXPORT_SYMBOL_GPL(gnttab_claim_grant_reference);
331 void gnttab_release_grant_reference(grant_ref_t *private_head,
332 grant_ref_t release)
333 {
334 gnttab_entry(release) = *private_head;
335 *private_head = release;
336 }
337 EXPORT_SYMBOL_GPL(gnttab_release_grant_reference);
339 void gnttab_request_free_callback(struct gnttab_free_callback *callback,
340 void (*fn)(void *), void *arg, u16 count)
341 {
342 unsigned long flags;
343 spin_lock_irqsave(&gnttab_list_lock, flags);
344 if (callback->next)
345 goto out;
346 callback->fn = fn;
347 callback->arg = arg;
348 callback->count = count;
349 callback->next = gnttab_free_callback_list;
350 gnttab_free_callback_list = callback;
351 check_free_callbacks();
352 out:
353 spin_unlock_irqrestore(&gnttab_list_lock, flags);
354 }
355 EXPORT_SYMBOL_GPL(gnttab_request_free_callback);
357 void gnttab_cancel_free_callback(struct gnttab_free_callback *callback)
358 {
359 struct gnttab_free_callback **pcb;
360 unsigned long flags;
362 spin_lock_irqsave(&gnttab_list_lock, flags);
363 for (pcb = &gnttab_free_callback_list; *pcb; pcb = &(*pcb)->next) {
364 if (*pcb == callback) {
365 *pcb = callback->next;
366 break;
367 }
368 }
369 spin_unlock_irqrestore(&gnttab_list_lock, flags);
370 }
371 EXPORT_SYMBOL_GPL(gnttab_cancel_free_callback);
373 static int grow_gnttab_list(unsigned int more_frames)
374 {
375 unsigned int new_nr_grant_frames, extra_entries, i;
377 new_nr_grant_frames = nr_grant_frames + more_frames;
378 extra_entries = more_frames * GREFS_PER_GRANT_FRAME;
380 for (i = nr_grant_frames; i < new_nr_grant_frames; i++)
381 {
382 gnttab_list[i] = (grant_ref_t *)__get_free_page(GFP_ATOMIC);
383 if (!gnttab_list[i])
384 goto grow_nomem;
385 }
388 for (i = GREFS_PER_GRANT_FRAME * nr_grant_frames;
389 i < GREFS_PER_GRANT_FRAME * new_nr_grant_frames - 1; i++)
390 gnttab_entry(i) = i + 1;
392 gnttab_entry(i) = gnttab_free_head;
393 gnttab_free_head = GREFS_PER_GRANT_FRAME * nr_grant_frames;
394 gnttab_free_count += extra_entries;
396 nr_grant_frames = new_nr_grant_frames;
398 check_free_callbacks();
400 return 0;
402 grow_nomem:
403 for ( ; i >= nr_grant_frames; i--)
404 free_page((unsigned long) gnttab_list[i]);
405 return -ENOMEM;
406 }
408 static unsigned int __max_nr_grant_frames(void)
409 {
410 struct gnttab_query_size query;
411 int rc;
413 query.dom = DOMID_SELF;
415 rc = HYPERVISOR_grant_table_op(GNTTABOP_query_size, &query, 1);
416 if ((rc < 0) || (query.status != GNTST_okay))
417 return 4; /* Legacy max supported number of frames */
419 return query.max_nr_frames;
420 }
422 static inline unsigned int max_nr_grant_frames(void)
423 {
424 unsigned int xen_max = __max_nr_grant_frames();
426 if (xen_max > boot_max_nr_grant_frames)
427 return boot_max_nr_grant_frames;
428 return xen_max;
429 }
431 #ifdef CONFIG_XEN
433 #ifndef __ia64__
434 static int map_pte_fn(pte_t *pte, struct page *pmd_page,
435 unsigned long addr, void *data)
436 {
437 unsigned long **frames = (unsigned long **)data;
439 set_pte_at(&init_mm, addr, pte, pfn_pte_ma((*frames)[0], PAGE_KERNEL));
440 (*frames)++;
441 return 0;
442 }
444 static int unmap_pte_fn(pte_t *pte, struct page *pmd_page,
445 unsigned long addr, void *data)
446 {
448 set_pte_at(&init_mm, addr, pte, __pte(0));
449 return 0;
450 }
451 #endif
453 static int gnttab_map(unsigned int start_idx, unsigned int end_idx)
454 {
455 struct gnttab_setup_table setup;
456 unsigned long *frames;
457 unsigned int nr_gframes = end_idx + 1;
458 int rc;
460 frames = kmalloc(nr_gframes * sizeof(unsigned long), GFP_ATOMIC);
461 if (!frames)
462 return -ENOMEM;
464 setup.dom = DOMID_SELF;
465 setup.nr_frames = nr_gframes;
466 set_xen_guest_handle(setup.frame_list, frames);
468 rc = HYPERVISOR_grant_table_op(GNTTABOP_setup_table, &setup, 1);
469 if (rc == -ENOSYS) {
470 kfree(frames);
471 return -ENOSYS;
472 }
474 BUG_ON(rc || setup.status);
476 #ifndef __ia64__
477 if (shared == NULL) {
478 struct vm_struct *area;
479 area = alloc_vm_area(PAGE_SIZE * max_nr_grant_frames());
480 BUG_ON(area == NULL);
481 shared = area->addr;
482 }
483 rc = apply_to_page_range(&init_mm, (unsigned long)shared,
484 PAGE_SIZE * nr_gframes,
485 map_pte_fn, &frames);
486 BUG_ON(rc);
487 frames -= nr_gframes; /* adjust after map_pte_fn() */
488 #else
489 shared = __va(frames[0] << PAGE_SHIFT);
490 #endif
492 kfree(frames);
494 return 0;
495 }
497 int gnttab_resume(void)
498 {
499 if (max_nr_grant_frames() < nr_grant_frames)
500 return -ENOSYS;
501 return gnttab_map(0, nr_grant_frames - 1);
502 }
504 int gnttab_suspend(void)
505 {
506 #ifndef __ia64__
507 apply_to_page_range(&init_mm, (unsigned long)shared,
508 PAGE_SIZE * nr_grant_frames,
509 unmap_pte_fn, NULL);
510 #endif
511 return 0;
512 }
514 #else /* !CONFIG_XEN */
516 #include <platform-pci.h>
518 static int gnttab_map(unsigned int start_idx, unsigned int end_idx)
519 {
520 struct xen_add_to_physmap xatp;
521 unsigned int i = end_idx;
523 /* Loop backwards, so that the first hypercall has the largest index,
524 * ensuring that the table will grow only once.
525 */
526 do {
527 xatp.domid = DOMID_SELF;
528 xatp.idx = i;
529 xatp.space = XENMAPSPACE_grant_table;
530 xatp.gpfn = (resume_frames >> PAGE_SHIFT) + i;
531 if (HYPERVISOR_memory_op(XENMEM_add_to_physmap, &xatp))
532 BUG();
533 } while (i-- > start_idx);
535 return 0;
536 }
538 int gnttab_resume(void)
539 {
540 unsigned int max_nr_gframes, nr_gframes;
542 nr_gframes = nr_grant_frames;
543 max_nr_gframes = max_nr_grant_frames();
544 if (max_nr_gframes < nr_gframes)
545 return -ENOSYS;
547 resume_frames = alloc_xen_mmio(PAGE_SIZE * max_nr_gframes);
549 gnttab_map(0, nr_gframes - 1);
551 shared = ioremap(resume_frames, PAGE_SIZE * max_nr_gframes);
552 if (shared == NULL) {
553 printk("error to ioremap gnttab share frames\n");
554 return -1;
555 }
557 return 0;
558 }
560 int gnttab_suspend(void)
561 {
562 iounmap(shared);
563 resume_frames = 0;
564 return 0;
565 }
567 #endif /* !CONFIG_XEN */
569 static int gnttab_expand(unsigned int req_entries)
570 {
571 int rc;
572 unsigned int cur, extra;
574 cur = nr_grant_frames;
575 extra = ((req_entries + (GREFS_PER_GRANT_FRAME-1)) /
576 GREFS_PER_GRANT_FRAME);
577 if (cur + extra > max_nr_grant_frames())
578 return -ENOSPC;
580 if ((rc = gnttab_map(cur, cur + extra - 1)) == 0)
581 rc = grow_gnttab_list(extra);
583 return rc;
584 }
586 int __devinit gnttab_init(void)
587 {
588 int i;
589 unsigned int max_nr_glist_frames;
590 unsigned int nr_init_grefs;
592 if (!is_running_on_xen())
593 return -ENODEV;
595 nr_grant_frames = 1;
596 boot_max_nr_grant_frames = __max_nr_grant_frames();
598 /* Determine the maximum number of frames required for the
599 * grant reference free list on the current hypervisor.
600 */
601 max_nr_glist_frames = (boot_max_nr_grant_frames *
602 GREFS_PER_GRANT_FRAME /
603 (PAGE_SIZE / sizeof(grant_ref_t)));
605 gnttab_list = kmalloc(max_nr_glist_frames * sizeof(grant_ref_t *),
606 GFP_KERNEL);
607 if (gnttab_list == NULL)
608 return -ENOMEM;
610 for (i = 0; i < nr_grant_frames; i++) {
611 gnttab_list[i] = (grant_ref_t *)__get_free_page(GFP_KERNEL);
612 if (gnttab_list[i] == NULL)
613 goto ini_nomem;
614 }
616 if (gnttab_resume() < 0)
617 return -ENODEV;
619 nr_init_grefs = nr_grant_frames * GREFS_PER_GRANT_FRAME;
621 for (i = NR_RESERVED_ENTRIES; i < nr_init_grefs - 1; i++)
622 gnttab_entry(i) = i + 1;
624 gnttab_entry(nr_init_grefs - 1) = GNTTAB_LIST_END;
625 gnttab_free_count = nr_init_grefs - NR_RESERVED_ENTRIES;
626 gnttab_free_head = NR_RESERVED_ENTRIES;
628 printk("Grant table initialized\n");
629 return 0;
631 ini_nomem:
632 for (i--; i >= 0; i--)
633 free_page((unsigned long)gnttab_list[i]);
634 kfree(gnttab_list);
635 return -ENOMEM;
636 }
638 #ifdef CONFIG_XEN
639 core_initcall(gnttab_init);
640 #endif