Merge to Fedora kernel-2.6.18-1.2224_FC5 patched with stable patch-2.6.18.1-vs2.0...
[linux-2.6.git] / drivers / xen / core / gnttab.c
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  */
33
34 #include <linux/module.h>
35 #include <linux/sched.h>
36 #include <linux/mm.h>
37 #include <linux/vmalloc.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
46 /* External tools reserve first few grant table entries. */
47 #define NR_RESERVED_ENTRIES 8
48
49 #define NR_GRANT_ENTRIES \
50         (NR_GRANT_FRAMES * PAGE_SIZE / sizeof(struct grant_entry))
51 #define GNTTAB_LIST_END (NR_GRANT_ENTRIES + 1)
52
53 static grant_ref_t gnttab_list[NR_GRANT_ENTRIES];
54 static int gnttab_free_count;
55 static grant_ref_t gnttab_free_head;
56 static DEFINE_SPINLOCK(gnttab_list_lock);
57
58 static struct grant_entry *shared;
59
60 static struct gnttab_free_callback *gnttab_free_callback_list;
61
62 static int get_free_entries(int count)
63 {
64         unsigned long flags;
65         int ref;
66         grant_ref_t head;
67         spin_lock_irqsave(&gnttab_list_lock, flags);
68         if (gnttab_free_count < count) {
69                 spin_unlock_irqrestore(&gnttab_list_lock, flags);
70                 return -1;
71         }
72         ref = head = gnttab_free_head;
73         gnttab_free_count -= count;
74         while (count-- > 1)
75                 head = gnttab_list[head];
76         gnttab_free_head = gnttab_list[head];
77         gnttab_list[head] = GNTTAB_LIST_END;
78         spin_unlock_irqrestore(&gnttab_list_lock, flags);
79         return ref;
80 }
81
82 #define get_free_entry() get_free_entries(1)
83
84 static void do_free_callbacks(void)
85 {
86         struct gnttab_free_callback *callback, *next;
87
88         callback = gnttab_free_callback_list;
89         gnttab_free_callback_list = NULL;
90
91         while (callback != NULL) {
92                 next = callback->next;
93                 if (gnttab_free_count >= callback->count) {
94                         callback->next = NULL;
95                         callback->fn(callback->arg);
96                 } else {
97                         callback->next = gnttab_free_callback_list;
98                         gnttab_free_callback_list = callback;
99                 }
100                 callback = next;
101         }
102 }
103
104 static inline void check_free_callbacks(void)
105 {
106         if (unlikely(gnttab_free_callback_list))
107                 do_free_callbacks();
108 }
109
110 static void put_free_entry(grant_ref_t ref)
111 {
112         unsigned long flags;
113         spin_lock_irqsave(&gnttab_list_lock, flags);
114         gnttab_list[ref] = gnttab_free_head;
115         gnttab_free_head = ref;
116         gnttab_free_count++;
117         check_free_callbacks();
118         spin_unlock_irqrestore(&gnttab_list_lock, flags);
119 }
120
121 /*
122  * Public grant-issuing interface functions
123  */
124
125 int gnttab_grant_foreign_access(domid_t domid, unsigned long frame,
126                                 int readonly)
127 {
128         int ref;
129
130         if (unlikely((ref = get_free_entry()) == -1))
131                 return -ENOSPC;
132
133         shared[ref].frame = frame;
134         shared[ref].domid = domid;
135         wmb();
136         shared[ref].flags = GTF_permit_access | (readonly ? GTF_readonly : 0);
137
138         return ref;
139 }
140 EXPORT_SYMBOL_GPL(gnttab_grant_foreign_access);
141
142 void gnttab_grant_foreign_access_ref(grant_ref_t ref, domid_t domid,
143                                      unsigned long frame, int readonly)
144 {
145         shared[ref].frame = frame;
146         shared[ref].domid = domid;
147         wmb();
148         shared[ref].flags = GTF_permit_access | (readonly ? GTF_readonly : 0);
149 }
150 EXPORT_SYMBOL_GPL(gnttab_grant_foreign_access_ref);
151
152
153 int gnttab_query_foreign_access(grant_ref_t ref)
154 {
155         u16 nflags;
156
157         nflags = shared[ref].flags;
158
159         return (nflags & (GTF_reading|GTF_writing));
160 }
161 EXPORT_SYMBOL_GPL(gnttab_query_foreign_access);
162
163 int gnttab_end_foreign_access_ref(grant_ref_t ref, int readonly)
164 {
165         u16 flags, nflags;
166
167         nflags = shared[ref].flags;
168         do {
169                 if ((flags = nflags) & (GTF_reading|GTF_writing)) {
170                         printk(KERN_ALERT "WARNING: g.e. still in use!\n");
171                         return 0;
172                 }
173         } while ((nflags = synch_cmpxchg_subword(&shared[ref].flags, flags, 0)) !=
174                  flags);
175
176         return 1;
177 }
178 EXPORT_SYMBOL_GPL(gnttab_end_foreign_access_ref);
179
180 void gnttab_end_foreign_access(grant_ref_t ref, int readonly,
181                                unsigned long page)
182 {
183         if (gnttab_end_foreign_access_ref(ref, readonly)) {
184                 put_free_entry(ref);
185                 if (page != 0)
186                         free_page(page);
187         } else {
188                 /* XXX This needs to be fixed so that the ref and page are
189                    placed on a list to be freed up later. */
190                 printk(KERN_WARNING
191                        "WARNING: leaking g.e. and page still in use!\n");
192         }
193 }
194 EXPORT_SYMBOL_GPL(gnttab_end_foreign_access);
195
196 int gnttab_grant_foreign_transfer(domid_t domid, unsigned long pfn)
197 {
198         int ref;
199
200         if (unlikely((ref = get_free_entry()) == -1))
201                 return -ENOSPC;
202         gnttab_grant_foreign_transfer_ref(ref, domid, pfn);
203
204         return ref;
205 }
206 EXPORT_SYMBOL_GPL(gnttab_grant_foreign_transfer);
207
208 void gnttab_grant_foreign_transfer_ref(grant_ref_t ref, domid_t domid,
209                                        unsigned long pfn)
210 {
211         shared[ref].frame = pfn;
212         shared[ref].domid = domid;
213         wmb();
214         shared[ref].flags = GTF_accept_transfer;
215 }
216 EXPORT_SYMBOL_GPL(gnttab_grant_foreign_transfer_ref);
217
218 unsigned long gnttab_end_foreign_transfer_ref(grant_ref_t ref)
219 {
220         unsigned long frame;
221         u16           flags;
222
223         /*
224          * If a transfer is not even yet started, try to reclaim the grant
225          * reference and return failure (== 0).
226          */
227         while (!((flags = shared[ref].flags) & GTF_transfer_committed)) {
228                 if (synch_cmpxchg_subword(&shared[ref].flags, flags, 0) == flags)
229                         return 0;
230                 cpu_relax();
231         }
232
233         /* If a transfer is in progress then wait until it is completed. */
234         while (!(flags & GTF_transfer_completed)) {
235                 flags = shared[ref].flags;
236                 cpu_relax();
237         }
238
239         /* Read the frame number /after/ reading completion status. */
240         rmb();
241         frame = shared[ref].frame;
242         BUG_ON(frame == 0);
243
244         return frame;
245 }
246 EXPORT_SYMBOL_GPL(gnttab_end_foreign_transfer_ref);
247
248 unsigned long gnttab_end_foreign_transfer(grant_ref_t ref)
249 {
250         unsigned long frame = gnttab_end_foreign_transfer_ref(ref);
251         put_free_entry(ref);
252         return frame;
253 }
254 EXPORT_SYMBOL_GPL(gnttab_end_foreign_transfer);
255
256 void gnttab_free_grant_reference(grant_ref_t ref)
257 {
258         put_free_entry(ref);
259 }
260 EXPORT_SYMBOL_GPL(gnttab_free_grant_reference);
261
262 void gnttab_free_grant_references(grant_ref_t head)
263 {
264         grant_ref_t ref;
265         unsigned long flags;
266         int count = 1;
267         if (head == GNTTAB_LIST_END)
268                 return;
269         spin_lock_irqsave(&gnttab_list_lock, flags);
270         ref = head;
271         while (gnttab_list[ref] != GNTTAB_LIST_END) {
272                 ref = gnttab_list[ref];
273                 count++;
274         }
275         gnttab_list[ref] = gnttab_free_head;
276         gnttab_free_head = head;
277         gnttab_free_count += count;
278         check_free_callbacks();
279         spin_unlock_irqrestore(&gnttab_list_lock, flags);
280 }
281 EXPORT_SYMBOL_GPL(gnttab_free_grant_references);
282
283 int gnttab_alloc_grant_references(u16 count, grant_ref_t *head)
284 {
285         int h = get_free_entries(count);
286
287         if (h == -1)
288                 return -ENOSPC;
289
290         *head = h;
291
292         return 0;
293 }
294 EXPORT_SYMBOL_GPL(gnttab_alloc_grant_references);
295
296 int gnttab_empty_grant_references(const grant_ref_t *private_head)
297 {
298         return (*private_head == GNTTAB_LIST_END);
299 }
300 EXPORT_SYMBOL_GPL(gnttab_empty_grant_references);
301
302 int gnttab_claim_grant_reference(grant_ref_t *private_head)
303 {
304         grant_ref_t g = *private_head;
305         if (unlikely(g == GNTTAB_LIST_END))
306                 return -ENOSPC;
307         *private_head = gnttab_list[g];
308         return g;
309 }
310 EXPORT_SYMBOL_GPL(gnttab_claim_grant_reference);
311
312 void gnttab_release_grant_reference(grant_ref_t *private_head,
313                                     grant_ref_t release)
314 {
315         gnttab_list[release] = *private_head;
316         *private_head = release;
317 }
318 EXPORT_SYMBOL_GPL(gnttab_release_grant_reference);
319
320 void gnttab_request_free_callback(struct gnttab_free_callback *callback,
321                                   void (*fn)(void *), void *arg, u16 count)
322 {
323         unsigned long flags;
324         spin_lock_irqsave(&gnttab_list_lock, flags);
325         if (callback->next)
326                 goto out;
327         callback->fn = fn;
328         callback->arg = arg;
329         callback->count = count;
330         callback->next = gnttab_free_callback_list;
331         gnttab_free_callback_list = callback;
332         check_free_callbacks();
333 out:
334         spin_unlock_irqrestore(&gnttab_list_lock, flags);
335 }
336 EXPORT_SYMBOL_GPL(gnttab_request_free_callback);
337
338 void gnttab_cancel_free_callback(struct gnttab_free_callback *callback)
339 {
340         struct gnttab_free_callback **pcb;
341         unsigned long flags;
342
343         spin_lock_irqsave(&gnttab_list_lock, flags);
344         for (pcb = &gnttab_free_callback_list; *pcb; pcb = &(*pcb)->next) {
345                 if (*pcb == callback) {
346                         *pcb = callback->next;
347                         break;
348                 }
349         }
350         spin_unlock_irqrestore(&gnttab_list_lock, flags);
351 }
352 EXPORT_SYMBOL_GPL(gnttab_cancel_free_callback);
353
354 #ifdef CONFIG_XEN
355
356 #ifndef __ia64__
357 static int map_pte_fn(pte_t *pte, struct page *pmd_page,
358                       unsigned long addr, void *data)
359 {
360         unsigned long **frames = (unsigned long **)data;
361
362         set_pte_at(&init_mm, addr, pte, pfn_pte_ma((*frames)[0], PAGE_KERNEL));
363         (*frames)++;
364         return 0;
365 }
366
367 static int unmap_pte_fn(pte_t *pte, struct page *pmd_page,
368                         unsigned long addr, void *data)
369 {
370
371         set_pte_at(&init_mm, addr, pte, __pte(0));
372         return 0;
373 }
374 #endif
375
376 int gnttab_resume(void)
377 {
378         struct gnttab_setup_table setup;
379         unsigned long frames[NR_GRANT_FRAMES];
380         int rc;
381 #ifndef __ia64__
382         void *pframes = frames;
383         struct vm_struct *area;
384 #endif
385
386         setup.dom        = DOMID_SELF;
387         setup.nr_frames  = NR_GRANT_FRAMES;
388         set_xen_guest_handle(setup.frame_list, frames);
389
390         rc = HYPERVISOR_grant_table_op(GNTTABOP_setup_table, &setup, 1);
391         if (rc == -ENOSYS)
392                 return -ENOSYS;
393
394         BUG_ON(rc || setup.status);
395
396 #ifndef __ia64__
397         if (shared == NULL) {
398                 area = get_vm_area(PAGE_SIZE * NR_GRANT_FRAMES, VM_IOREMAP);
399                 BUG_ON(area == NULL);
400                 shared = area->addr;
401         }
402         rc = apply_to_page_range(&init_mm, (unsigned long)shared,
403                                  PAGE_SIZE * NR_GRANT_FRAMES,
404                                  map_pte_fn, &pframes);
405         BUG_ON(rc);
406 #else
407         shared = __va(frames[0] << PAGE_SHIFT);
408         printk("grant table at %p\n", shared);
409 #endif
410
411         return 0;
412 }
413
414 int gnttab_suspend(void)
415 {
416 #ifndef __ia64__
417         apply_to_page_range(&init_mm, (unsigned long)shared,
418                             PAGE_SIZE * NR_GRANT_FRAMES,
419                             unmap_pte_fn, NULL);
420 #endif
421         return 0;
422 }
423
424 #else /* !CONFIG_XEN */
425
426 #include <platform-pci.h>
427
428 int gnttab_resume(void)
429 {
430         unsigned long frames;
431         struct xen_add_to_physmap xatp;
432         unsigned int i;
433
434         frames = alloc_xen_mmio(PAGE_SIZE * NR_GRANT_FRAMES);
435
436         for (i = 0; i < NR_GRANT_FRAMES; i++) {
437                 xatp.domid = DOMID_SELF;
438                 xatp.idx = i;
439                 xatp.space = XENMAPSPACE_grant_table;
440                 xatp.gpfn = (frames >> PAGE_SHIFT) + i;
441                 if (HYPERVISOR_memory_op(XENMEM_add_to_physmap, &xatp))
442                         BUG();
443         }
444
445         shared = ioremap(frames, PAGE_SIZE * NR_GRANT_FRAMES);
446         if (shared == NULL) {
447                 printk("error to ioremap gnttab share frames\n");
448                 return -1;
449         }
450
451         return 0;
452 }
453
454 int gnttab_suspend(void)
455 {
456         iounmap(shared);
457         return 0;
458 }
459
460 #endif /* !CONFIG_XEN */
461
462 int __init gnttab_init(void)
463 {
464         int i;
465
466         if (!is_running_on_xen())
467                 return -ENODEV;
468
469         if (gnttab_resume() < 0)
470                 return -ENODEV;
471
472         for (i = NR_RESERVED_ENTRIES; i < NR_GRANT_ENTRIES; i++)
473                 gnttab_list[i] = i + 1;
474         gnttab_free_count = NR_GRANT_ENTRIES - NR_RESERVED_ENTRIES;
475         gnttab_free_head  = NR_RESERVED_ENTRIES;
476
477         printk("Grant table initialized\n");
478         return 0;
479 }
480
481 #ifdef CONFIG_XEN
482 core_initcall(gnttab_init);
483 #endif