This commit was manufactured by cvs2svn to create branch 'vserver'.
[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/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>
44
45 /* External tools reserve first few grant table entries. */
46 #define NR_RESERVED_ENTRIES 8
47
48 #define NR_GRANT_ENTRIES \
49         (NR_GRANT_FRAMES * PAGE_SIZE / sizeof(struct grant_entry))
50 #define GNTTAB_LIST_END (NR_GRANT_ENTRIES + 1)
51
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);
56
57 static struct grant_entry *shared;
58
59 static struct gnttab_free_callback *gnttab_free_callback_list;
60
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 }
80
81 #define get_free_entry() get_free_entries(1)
82
83 static void do_free_callbacks(void)
84 {
85         struct gnttab_free_callback *callback, *next;
86
87         callback = gnttab_free_callback_list;
88         gnttab_free_callback_list = NULL;
89
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 }
102
103 static inline void check_free_callbacks(void)
104 {
105         if (unlikely(gnttab_free_callback_list))
106                 do_free_callbacks();
107 }
108
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 }
119
120 /*
121  * Public grant-issuing interface functions
122  */
123
124 int gnttab_grant_foreign_access(domid_t domid, unsigned long frame,
125                                 int readonly)
126 {
127         int ref;
128
129         if (unlikely((ref = get_free_entry()) == -1))
130                 return -ENOSPC;
131
132         shared[ref].frame = frame;
133         shared[ref].domid = domid;
134         wmb();
135         shared[ref].flags = GTF_permit_access | (readonly ? GTF_readonly : 0);
136
137         return ref;
138 }
139 EXPORT_SYMBOL_GPL(gnttab_grant_foreign_access);
140
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);
150
151
152 int gnttab_query_foreign_access(grant_ref_t ref)
153 {
154         u16 nflags;
155
156         nflags = shared[ref].flags;
157
158         return (nflags & (GTF_reading|GTF_writing));
159 }
160 EXPORT_SYMBOL_GPL(gnttab_query_foreign_access);
161
162 int gnttab_end_foreign_access_ref(grant_ref_t ref, int readonly)
163 {
164         u16 flags, nflags;
165
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);
174
175         return 1;
176 }
177 EXPORT_SYMBOL_GPL(gnttab_end_foreign_access_ref);
178
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);
194
195 int gnttab_grant_foreign_transfer(domid_t domid, unsigned long pfn)
196 {
197         int ref;
198
199         if (unlikely((ref = get_free_entry()) == -1))
200                 return -ENOSPC;
201         gnttab_grant_foreign_transfer_ref(ref, domid, pfn);
202
203         return ref;
204 }
205 EXPORT_SYMBOL_GPL(gnttab_grant_foreign_transfer);
206
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);
216
217 unsigned long gnttab_end_foreign_transfer_ref(grant_ref_t ref)
218 {
219         unsigned long frame;
220         u16           flags;
221
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         }
231
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         }
237
238         /* Read the frame number /after/ reading completion status. */
239         rmb();
240         frame = shared[ref].frame;
241         BUG_ON(frame == 0);
242
243         return frame;
244 }
245 EXPORT_SYMBOL_GPL(gnttab_end_foreign_transfer_ref);
246
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);
254
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);
260
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);
281
282 int gnttab_alloc_grant_references(u16 count, grant_ref_t *head)
283 {
284         int h = get_free_entries(count);
285
286         if (h == -1)
287                 return -ENOSPC;
288
289         *head = h;
290
291         return 0;
292 }
293 EXPORT_SYMBOL_GPL(gnttab_alloc_grant_references);
294
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);
300
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);
310
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);
318
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);
336
337 void gnttab_cancel_free_callback(struct gnttab_free_callback *callback)
338 {
339         struct gnttab_free_callback **pcb;
340         unsigned long flags;
341
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);
352
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;
358
359         set_pte_at(&init_mm, addr, pte, pfn_pte_ma((*frames)[0], PAGE_KERNEL));
360         (*frames)++;
361         return 0;
362 }
363
364 static int unmap_pte_fn(pte_t *pte, struct page *pmd_page,
365                         unsigned long addr, void *data)
366 {
367
368         set_pte_at(&init_mm, addr, pte, __pte(0));
369         return 0;
370 }
371 #endif
372
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
382
383         setup.dom        = DOMID_SELF;
384         setup.nr_frames  = NR_GRANT_FRAMES;
385         set_xen_guest_handle(setup.frame_list, frames);
386
387         rc = HYPERVISOR_grant_table_op(GNTTABOP_setup_table, &setup, 1);
388         if (rc == -ENOSYS)
389                 return -ENOSYS;
390
391         BUG_ON(rc || setup.status);
392
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
407
408         return 0;
409 }
410
411 int gnttab_suspend(void)
412 {
413
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
419
420         return 0;
421 }
422
423 static int __init gnttab_init(void)
424 {
425         int i;
426
427         if (!is_running_on_xen())
428                 return -ENODEV;
429
430         if (gnttab_resume() < 0)
431                 return -ENODEV;
432
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;
437
438         printk("Grant table initialized\n");
439         return 0;
440 }
441
442 core_initcall(gnttab_init);