blob: 9f514bb561af6c17c5c276ec93d0c4fc8521ae10 [file] [log] [blame]
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -07001/******************************************************************************
2 * grant_table.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 */
33
34#include <linux/module.h>
35#include <linux/sched.h>
36#include <linux/mm.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090037#include <linux/slab.h>
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -070038#include <linux/vmalloc.h>
39#include <linux/uaccess.h>
Stefano Stabellini183d03c2010-05-17 17:08:21 +010040#include <linux/io.h>
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -070041
Jeremy Fitzhardinge1ccbf532009-10-06 15:11:14 -070042#include <xen/xen.h>
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -070043#include <xen/interface/xen.h>
44#include <xen/page.h>
45#include <xen/grant_table.h>
Stefano Stabellini183d03c2010-05-17 17:08:21 +010046#include <xen/interface/memory.h>
Annie Li85ff6ac2011-11-22 09:59:21 +080047#include <xen/hvc-console.h>
Jeremy Fitzhardingeecbf29c2008-12-16 12:37:07 -080048#include <asm/xen/hypercall.h>
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -070049
50#include <asm/pgtable.h>
51#include <asm/sync_bitops.h>
52
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -070053/* External tools reserve first few grant table entries. */
54#define NR_RESERVED_ENTRIES 8
55#define GNTTAB_LIST_END 0xffffffff
Annie Li85ff6ac2011-11-22 09:59:21 +080056#define GREFS_PER_GRANT_FRAME \
57(grant_table_version == 1 ? \
58(PAGE_SIZE / sizeof(struct grant_entry_v1)) : \
59(PAGE_SIZE / sizeof(union grant_entry_v2)))
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -070060
61static grant_ref_t **gnttab_list;
62static unsigned int nr_grant_frames;
63static unsigned int boot_max_nr_grant_frames;
64static int gnttab_free_count;
65static grant_ref_t gnttab_free_head;
66static DEFINE_SPINLOCK(gnttab_list_lock);
Stefano Stabellini183d03c2010-05-17 17:08:21 +010067unsigned long xen_hvm_resume_frames;
68EXPORT_SYMBOL_GPL(xen_hvm_resume_frames);
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -070069
Annie Li0f9f5a92011-11-22 09:58:06 +080070static union {
71 struct grant_entry_v1 *v1;
Annie Li85ff6ac2011-11-22 09:59:21 +080072 union grant_entry_v2 *v2;
Annie Li0f9f5a92011-11-22 09:58:06 +080073 void *addr;
74} gnttab_shared;
75
76/*This is a structure of function pointers for grant table*/
77struct gnttab_ops {
78 /*
Annie Li9dbc71d2011-12-12 18:13:57 +080079 * Mapping a list of frames for storing grant entries. Frames parameter
80 * is used to store grant table address when grant table being setup,
81 * nr_gframes is the number of frames to map grant table. Returning
82 * GNTST_okay means success and negative value means failure.
Annie Li0f9f5a92011-11-22 09:58:06 +080083 */
Annie Li9dbc71d2011-12-12 18:13:57 +080084 int (*map_frames)(unsigned long *frames, unsigned int nr_gframes);
Annie Li0f9f5a92011-11-22 09:58:06 +080085 /*
86 * Release a list of frames which are mapped in map_frames for grant
87 * entry status.
88 */
89 void (*unmap_frames)(void);
90 /*
Annie Li9dbc71d2011-12-12 18:13:57 +080091 * Introducing a valid entry into the grant table, granting the frame of
92 * this grant entry to domain for accessing or transfering. Ref
93 * parameter is reference of this introduced grant entry, domid is id of
94 * granted domain, frame is the page frame to be granted, and flags is
95 * status of the grant entry to be updated.
Annie Li0f9f5a92011-11-22 09:58:06 +080096 */
Annie Li9dbc71d2011-12-12 18:13:57 +080097 void (*update_entry)(grant_ref_t ref, domid_t domid,
98 unsigned long frame, unsigned flags);
Annie Li0f9f5a92011-11-22 09:58:06 +080099 /*
Annie Li9dbc71d2011-12-12 18:13:57 +0800100 * Stop granting a grant entry to domain for accessing. Ref parameter is
101 * reference of a grant entry whose grant access will be stopped,
102 * readonly is not in use in this function. If the grant entry is
Annie Li0f9f5a92011-11-22 09:58:06 +0800103 * currently mapped for reading or writing, just return failure(==0)
104 * directly and don't tear down the grant access. Otherwise, stop grant
105 * access for this entry and return success(==1).
106 */
Annie Li9dbc71d2011-12-12 18:13:57 +0800107 int (*end_foreign_access_ref)(grant_ref_t ref, int readonly);
Annie Li0f9f5a92011-11-22 09:58:06 +0800108 /*
Annie Li9dbc71d2011-12-12 18:13:57 +0800109 * Stop granting a grant entry to domain for transfer. Ref parameter is
110 * reference of a grant entry whose grant transfer will be stopped. If
111 * tranfer has not started, just reclaim the grant entry and return
112 * failure(==0). Otherwise, wait for the transfer to complete and then
113 * return the frame.
Annie Li0f9f5a92011-11-22 09:58:06 +0800114 */
Annie Li9dbc71d2011-12-12 18:13:57 +0800115 unsigned long (*end_foreign_transfer_ref)(grant_ref_t ref);
Annie Li0f9f5a92011-11-22 09:58:06 +0800116 /*
Annie Li9dbc71d2011-12-12 18:13:57 +0800117 * Query the status of a grant entry. Ref parameter is reference of
Annie Li0f9f5a92011-11-22 09:58:06 +0800118 * queried grant entry, return value is the status of queried entry.
119 * Detailed status(writing/reading) can be gotten from the return value
120 * by bit operations.
121 */
Annie Li9dbc71d2011-12-12 18:13:57 +0800122 int (*query_foreign_access)(grant_ref_t ref);
Annie Li66667542011-12-12 18:14:42 +0800123 /*
124 * Grant a domain to access a range of bytes within the page referred by
125 * an available grant entry. Ref parameter is reference of a grant entry
126 * which will be sub-page accessed, domid is id of grantee domain, frame
127 * is frame address of subpage grant, flags is grant type and flag
128 * information, page_off is offset of the range of bytes, and length is
129 * length of bytes to be accessed.
130 */
131 void (*update_subpage_entry)(grant_ref_t ref, domid_t domid,
132 unsigned long frame, int flags,
133 unsigned page_off, unsigned length);
Annie Li9438ce92011-12-12 18:15:07 +0800134 /*
135 * Redirect an available grant entry on domain A to another grant
136 * reference of domain B, then allow domain C to use grant reference
137 * of domain B transitively. Ref parameter is an available grant entry
138 * reference on domain A, domid is id of domain C which accesses grant
139 * entry transitively, flags is grant type and flag information,
140 * trans_domid is id of domain B whose grant entry is finally accessed
141 * transitively, trans_gref is grant entry transitive reference of
142 * domain B.
143 */
144 void (*update_trans_entry)(grant_ref_t ref, domid_t domid, int flags,
145 domid_t trans_domid, grant_ref_t trans_gref);
Annie Li0f9f5a92011-11-22 09:58:06 +0800146};
147
148static struct gnttab_ops *gnttab_interface;
149
Annie Li85ff6ac2011-11-22 09:59:21 +0800150/*This reflects status of grant entries, so act as a global value*/
151static grant_status_t *grstatus;
152
Annie Li0f9f5a92011-11-22 09:58:06 +0800153static int grant_table_version;
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700154
155static struct gnttab_free_callback *gnttab_free_callback_list;
156
157static int gnttab_expand(unsigned int req_entries);
158
159#define RPP (PAGE_SIZE / sizeof(grant_ref_t))
Annie Li85ff6ac2011-11-22 09:59:21 +0800160#define SPP (PAGE_SIZE / sizeof(grant_status_t))
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700161
162static inline grant_ref_t *__gnttab_entry(grant_ref_t entry)
163{
164 return &gnttab_list[(entry) / RPP][(entry) % RPP];
165}
166/* This can be used as an l-value */
167#define gnttab_entry(entry) (*__gnttab_entry(entry))
168
169static int get_free_entries(unsigned count)
170{
171 unsigned long flags;
Konrad Rzeszutek Wilk272800d2011-07-22 14:00:06 -0400172 int ref, rc = 0;
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700173 grant_ref_t head;
174
175 spin_lock_irqsave(&gnttab_list_lock, flags);
176
177 if ((gnttab_free_count < count) &&
178 ((rc = gnttab_expand(count - gnttab_free_count)) < 0)) {
179 spin_unlock_irqrestore(&gnttab_list_lock, flags);
180 return rc;
181 }
182
183 ref = head = gnttab_free_head;
184 gnttab_free_count -= count;
185 while (count-- > 1)
186 head = gnttab_entry(head);
187 gnttab_free_head = gnttab_entry(head);
188 gnttab_entry(head) = GNTTAB_LIST_END;
189
190 spin_unlock_irqrestore(&gnttab_list_lock, flags);
191
192 return ref;
193}
194
195static void do_free_callbacks(void)
196{
197 struct gnttab_free_callback *callback, *next;
198
199 callback = gnttab_free_callback_list;
200 gnttab_free_callback_list = NULL;
201
202 while (callback != NULL) {
203 next = callback->next;
204 if (gnttab_free_count >= callback->count) {
205 callback->next = NULL;
206 callback->fn(callback->arg);
207 } else {
208 callback->next = gnttab_free_callback_list;
209 gnttab_free_callback_list = callback;
210 }
211 callback = next;
212 }
213}
214
215static inline void check_free_callbacks(void)
216{
217 if (unlikely(gnttab_free_callback_list))
218 do_free_callbacks();
219}
220
221static void put_free_entry(grant_ref_t ref)
222{
223 unsigned long flags;
224 spin_lock_irqsave(&gnttab_list_lock, flags);
225 gnttab_entry(ref) = gnttab_free_head;
226 gnttab_free_head = ref;
227 gnttab_free_count++;
228 check_free_callbacks();
229 spin_unlock_irqrestore(&gnttab_list_lock, flags);
230}
231
Annie Li0f9f5a92011-11-22 09:58:06 +0800232/*
Annie Li85ff6ac2011-11-22 09:59:21 +0800233 * Following applies to gnttab_update_entry_v1 and gnttab_update_entry_v2.
Annie Li0f9f5a92011-11-22 09:58:06 +0800234 * Introducing a valid entry into the grant table:
235 * 1. Write ent->domid.
236 * 2. Write ent->frame:
237 * GTF_permit_access: Frame to which access is permitted.
238 * GTF_accept_transfer: Pseudo-phys frame slot being filled by new
239 * frame, or zero if none.
240 * 3. Write memory barrier (WMB).
241 * 4. Write ent->flags, inc. valid type.
242 */
243static void gnttab_update_entry_v1(grant_ref_t ref, domid_t domid,
244 unsigned long frame, unsigned flags)
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700245{
Annie Li0f9f5a92011-11-22 09:58:06 +0800246 gnttab_shared.v1[ref].domid = domid;
247 gnttab_shared.v1[ref].frame = frame;
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700248 wmb();
Annie Li0f9f5a92011-11-22 09:58:06 +0800249 gnttab_shared.v1[ref].flags = flags;
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700250}
251
Annie Li85ff6ac2011-11-22 09:59:21 +0800252static void gnttab_update_entry_v2(grant_ref_t ref, domid_t domid,
253 unsigned long frame, unsigned flags)
254{
255 gnttab_shared.v2[ref].hdr.domid = domid;
256 gnttab_shared.v2[ref].full_page.frame = frame;
257 wmb();
258 gnttab_shared.v2[ref].hdr.flags = GTF_permit_access | flags;
259}
260
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700261/*
262 * Public grant-issuing interface functions
263 */
264void gnttab_grant_foreign_access_ref(grant_ref_t ref, domid_t domid,
265 unsigned long frame, int readonly)
266{
Annie Li0f9f5a92011-11-22 09:58:06 +0800267 gnttab_interface->update_entry(ref, domid, frame,
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700268 GTF_permit_access | (readonly ? GTF_readonly : 0));
269}
270EXPORT_SYMBOL_GPL(gnttab_grant_foreign_access_ref);
271
272int gnttab_grant_foreign_access(domid_t domid, unsigned long frame,
273 int readonly)
274{
275 int ref;
276
277 ref = get_free_entries(1);
278 if (unlikely(ref < 0))
279 return -ENOSPC;
280
281 gnttab_grant_foreign_access_ref(ref, domid, frame, readonly);
282
283 return ref;
284}
285EXPORT_SYMBOL_GPL(gnttab_grant_foreign_access);
286
Annie Li66667542011-12-12 18:14:42 +0800287void gnttab_update_subpage_entry_v2(grant_ref_t ref, domid_t domid,
288 unsigned long frame, int flags,
289 unsigned page_off,
290 unsigned length)
291{
292 gnttab_shared.v2[ref].sub_page.frame = frame;
293 gnttab_shared.v2[ref].sub_page.page_off = page_off;
294 gnttab_shared.v2[ref].sub_page.length = length;
295 gnttab_shared.v2[ref].hdr.domid = domid;
296 wmb();
297 gnttab_shared.v2[ref].hdr.flags =
298 GTF_permit_access | GTF_sub_page | flags;
299}
300
301int gnttab_grant_foreign_access_subpage_ref(grant_ref_t ref, domid_t domid,
302 unsigned long frame, int flags,
303 unsigned page_off,
304 unsigned length)
305{
306 if (flags & (GTF_accept_transfer | GTF_reading |
307 GTF_writing | GTF_transitive))
308 return -EPERM;
309
310 if (gnttab_interface->update_subpage_entry == NULL)
311 return -ENOSYS;
312
313 gnttab_interface->update_subpage_entry(ref, domid, frame, flags,
314 page_off, length);
315
316 return 0;
317}
318EXPORT_SYMBOL_GPL(gnttab_grant_foreign_access_subpage_ref);
319
320int gnttab_grant_foreign_access_subpage(domid_t domid, unsigned long frame,
321 int flags, unsigned page_off,
322 unsigned length)
323{
324 int ref, rc;
325
326 ref = get_free_entries(1);
327 if (unlikely(ref < 0))
328 return -ENOSPC;
329
330 rc = gnttab_grant_foreign_access_subpage_ref(ref, domid, frame, flags,
331 page_off, length);
332 if (rc < 0) {
333 put_free_entry(ref);
334 return rc;
335 }
336
337 return ref;
338}
339EXPORT_SYMBOL_GPL(gnttab_grant_foreign_access_subpage);
340
341bool gnttab_subpage_grants_available(void)
342{
343 return gnttab_interface->update_subpage_entry != NULL;
344}
345EXPORT_SYMBOL_GPL(gnttab_subpage_grants_available);
346
Annie Li9438ce92011-12-12 18:15:07 +0800347void gnttab_update_trans_entry_v2(grant_ref_t ref, domid_t domid,
348 int flags, domid_t trans_domid,
349 grant_ref_t trans_gref)
350{
351 gnttab_shared.v2[ref].transitive.trans_domid = trans_domid;
352 gnttab_shared.v2[ref].transitive.gref = trans_gref;
353 gnttab_shared.v2[ref].hdr.domid = domid;
354 wmb();
355 gnttab_shared.v2[ref].hdr.flags =
356 GTF_permit_access | GTF_transitive | flags;
357}
358
359int gnttab_grant_foreign_access_trans_ref(grant_ref_t ref, domid_t domid,
360 int flags, domid_t trans_domid,
361 grant_ref_t trans_gref)
362{
363 if (flags & (GTF_accept_transfer | GTF_reading |
364 GTF_writing | GTF_sub_page))
365 return -EPERM;
366
367 if (gnttab_interface->update_trans_entry == NULL)
368 return -ENOSYS;
369
370 gnttab_interface->update_trans_entry(ref, domid, flags, trans_domid,
371 trans_gref);
372
373 return 0;
374}
375EXPORT_SYMBOL_GPL(gnttab_grant_foreign_access_trans_ref);
376
377int gnttab_grant_foreign_access_trans(domid_t domid, int flags,
378 domid_t trans_domid,
379 grant_ref_t trans_gref)
380{
381 int ref, rc;
382
383 ref = get_free_entries(1);
384 if (unlikely(ref < 0))
385 return -ENOSPC;
386
387 rc = gnttab_grant_foreign_access_trans_ref(ref, domid, flags,
388 trans_domid, trans_gref);
389 if (rc < 0) {
390 put_free_entry(ref);
391 return rc;
392 }
393
394 return ref;
395}
396EXPORT_SYMBOL_GPL(gnttab_grant_foreign_access_trans);
397
398bool gnttab_trans_grants_available(void)
399{
400 return gnttab_interface->update_trans_entry != NULL;
401}
402EXPORT_SYMBOL_GPL(gnttab_trans_grants_available);
403
Annie Li0f9f5a92011-11-22 09:58:06 +0800404static int gnttab_query_foreign_access_v1(grant_ref_t ref)
405{
406 return gnttab_shared.v1[ref].flags & (GTF_reading|GTF_writing);
407}
408
Annie Li85ff6ac2011-11-22 09:59:21 +0800409static int gnttab_query_foreign_access_v2(grant_ref_t ref)
410{
411 return grstatus[ref] & (GTF_reading|GTF_writing);
412}
413
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700414int gnttab_query_foreign_access(grant_ref_t ref)
415{
Annie Li0f9f5a92011-11-22 09:58:06 +0800416 return gnttab_interface->query_foreign_access(ref);
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700417}
418EXPORT_SYMBOL_GPL(gnttab_query_foreign_access);
419
Annie Li0f9f5a92011-11-22 09:58:06 +0800420static int gnttab_end_foreign_access_ref_v1(grant_ref_t ref, int readonly)
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700421{
422 u16 flags, nflags;
Annie Lib1e495b2011-11-22 09:58:47 +0800423 u16 *pflags;
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700424
Annie Lib1e495b2011-11-22 09:58:47 +0800425 pflags = &gnttab_shared.v1[ref].flags;
426 nflags = *pflags;
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700427 do {
428 flags = nflags;
Jan Beulich569ca5b2012-04-05 16:10:07 +0100429 if (flags & (GTF_reading|GTF_writing))
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700430 return 0;
Annie Lib1e495b2011-11-22 09:58:47 +0800431 } while ((nflags = sync_cmpxchg(pflags, flags, 0)) != flags);
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700432
433 return 1;
434}
Annie Li0f9f5a92011-11-22 09:58:06 +0800435
Annie Li85ff6ac2011-11-22 09:59:21 +0800436static int gnttab_end_foreign_access_ref_v2(grant_ref_t ref, int readonly)
437{
438 gnttab_shared.v2[ref].hdr.flags = 0;
439 mb();
440 if (grstatus[ref] & (GTF_reading|GTF_writing)) {
441 return 0;
442 } else {
443 /* The read of grstatus needs to have acquire
444 semantics. On x86, reads already have
445 that, and we just need to protect against
446 compiler reorderings. On other
447 architectures we may need a full
448 barrier. */
449#ifdef CONFIG_X86
450 barrier();
451#else
452 mb();
453#endif
454 }
455
456 return 1;
457}
458
Jan Beulich569ca5b2012-04-05 16:10:07 +0100459static inline int _gnttab_end_foreign_access_ref(grant_ref_t ref, int readonly)
Annie Li0f9f5a92011-11-22 09:58:06 +0800460{
461 return gnttab_interface->end_foreign_access_ref(ref, readonly);
462}
Jan Beulich569ca5b2012-04-05 16:10:07 +0100463
464int gnttab_end_foreign_access_ref(grant_ref_t ref, int readonly)
465{
466 if (_gnttab_end_foreign_access_ref(ref, readonly))
467 return 1;
468 pr_warn("WARNING: g.e. %#x still in use!\n", ref);
469 return 0;
470}
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700471EXPORT_SYMBOL_GPL(gnttab_end_foreign_access_ref);
472
Jan Beulich569ca5b2012-04-05 16:10:07 +0100473struct deferred_entry {
474 struct list_head list;
475 grant_ref_t ref;
476 bool ro;
477 uint16_t warn_delay;
478 struct page *page;
479};
480static LIST_HEAD(deferred_list);
481static void gnttab_handle_deferred(unsigned long);
482static DEFINE_TIMER(deferred_timer, gnttab_handle_deferred, 0, 0);
483
484static void gnttab_handle_deferred(unsigned long unused)
485{
486 unsigned int nr = 10;
487 struct deferred_entry *first = NULL;
488 unsigned long flags;
489
490 spin_lock_irqsave(&gnttab_list_lock, flags);
491 while (nr--) {
492 struct deferred_entry *entry
493 = list_first_entry(&deferred_list,
494 struct deferred_entry, list);
495
496 if (entry == first)
497 break;
498 list_del(&entry->list);
499 spin_unlock_irqrestore(&gnttab_list_lock, flags);
500 if (_gnttab_end_foreign_access_ref(entry->ref, entry->ro)) {
501 put_free_entry(entry->ref);
502 if (entry->page) {
503 pr_debug("freeing g.e. %#x (pfn %#lx)\n",
504 entry->ref, page_to_pfn(entry->page));
505 __free_page(entry->page);
506 } else
507 pr_info("freeing g.e. %#x\n", entry->ref);
508 kfree(entry);
509 entry = NULL;
510 } else {
511 if (!--entry->warn_delay)
512 pr_info("g.e. %#x still pending\n",
513 entry->ref);
514 if (!first)
515 first = entry;
516 }
517 spin_lock_irqsave(&gnttab_list_lock, flags);
518 if (entry)
519 list_add_tail(&entry->list, &deferred_list);
520 else if (list_empty(&deferred_list))
521 break;
522 }
523 if (!list_empty(&deferred_list) && !timer_pending(&deferred_timer)) {
524 deferred_timer.expires = jiffies + HZ;
525 add_timer(&deferred_timer);
526 }
527 spin_unlock_irqrestore(&gnttab_list_lock, flags);
528}
529
530static void gnttab_add_deferred(grant_ref_t ref, bool readonly,
531 struct page *page)
532{
533 struct deferred_entry *entry = kmalloc(sizeof(*entry), GFP_ATOMIC);
534 const char *what = KERN_WARNING "leaking";
535
536 if (entry) {
537 unsigned long flags;
538
539 entry->ref = ref;
540 entry->ro = readonly;
541 entry->page = page;
542 entry->warn_delay = 60;
543 spin_lock_irqsave(&gnttab_list_lock, flags);
544 list_add_tail(&entry->list, &deferred_list);
545 if (!timer_pending(&deferred_timer)) {
546 deferred_timer.expires = jiffies + HZ;
547 add_timer(&deferred_timer);
548 }
549 spin_unlock_irqrestore(&gnttab_list_lock, flags);
550 what = KERN_DEBUG "deferring";
551 }
552 printk("%s g.e. %#x (pfn %#lx)\n",
553 what, ref, page ? page_to_pfn(page) : -1);
554}
555
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700556void gnttab_end_foreign_access(grant_ref_t ref, int readonly,
557 unsigned long page)
558{
559 if (gnttab_end_foreign_access_ref(ref, readonly)) {
560 put_free_entry(ref);
561 if (page != 0)
562 free_page(page);
Jan Beulich569ca5b2012-04-05 16:10:07 +0100563 } else
564 gnttab_add_deferred(ref, readonly,
565 page ? virt_to_page(page) : NULL);
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700566}
567EXPORT_SYMBOL_GPL(gnttab_end_foreign_access);
568
569int gnttab_grant_foreign_transfer(domid_t domid, unsigned long pfn)
570{
571 int ref;
572
573 ref = get_free_entries(1);
574 if (unlikely(ref < 0))
575 return -ENOSPC;
576 gnttab_grant_foreign_transfer_ref(ref, domid, pfn);
577
578 return ref;
579}
580EXPORT_SYMBOL_GPL(gnttab_grant_foreign_transfer);
581
582void gnttab_grant_foreign_transfer_ref(grant_ref_t ref, domid_t domid,
583 unsigned long pfn)
584{
Annie Li0f9f5a92011-11-22 09:58:06 +0800585 gnttab_interface->update_entry(ref, domid, pfn, GTF_accept_transfer);
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700586}
587EXPORT_SYMBOL_GPL(gnttab_grant_foreign_transfer_ref);
588
Annie Li0f9f5a92011-11-22 09:58:06 +0800589static unsigned long gnttab_end_foreign_transfer_ref_v1(grant_ref_t ref)
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700590{
591 unsigned long frame;
592 u16 flags;
Annie Lib1e495b2011-11-22 09:58:47 +0800593 u16 *pflags;
594
595 pflags = &gnttab_shared.v1[ref].flags;
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700596
597 /*
598 * If a transfer is not even yet started, try to reclaim the grant
599 * reference and return failure (== 0).
600 */
Annie Lib1e495b2011-11-22 09:58:47 +0800601 while (!((flags = *pflags) & GTF_transfer_committed)) {
602 if (sync_cmpxchg(pflags, flags, 0) == flags)
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700603 return 0;
604 cpu_relax();
605 }
606
607 /* If a transfer is in progress then wait until it is completed. */
608 while (!(flags & GTF_transfer_completed)) {
Annie Lib1e495b2011-11-22 09:58:47 +0800609 flags = *pflags;
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700610 cpu_relax();
611 }
612
613 rmb(); /* Read the frame number /after/ reading completion status. */
Annie Li0f9f5a92011-11-22 09:58:06 +0800614 frame = gnttab_shared.v1[ref].frame;
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700615 BUG_ON(frame == 0);
616
617 return frame;
618}
Annie Li0f9f5a92011-11-22 09:58:06 +0800619
Annie Li85ff6ac2011-11-22 09:59:21 +0800620static unsigned long gnttab_end_foreign_transfer_ref_v2(grant_ref_t ref)
621{
622 unsigned long frame;
623 u16 flags;
624 u16 *pflags;
625
626 pflags = &gnttab_shared.v2[ref].hdr.flags;
627
628 /*
629 * If a transfer is not even yet started, try to reclaim the grant
630 * reference and return failure (== 0).
631 */
632 while (!((flags = *pflags) & GTF_transfer_committed)) {
633 if (sync_cmpxchg(pflags, flags, 0) == flags)
634 return 0;
635 cpu_relax();
636 }
637
638 /* If a transfer is in progress then wait until it is completed. */
639 while (!(flags & GTF_transfer_completed)) {
640 flags = *pflags;
641 cpu_relax();
642 }
643
644 rmb(); /* Read the frame number /after/ reading completion status. */
645 frame = gnttab_shared.v2[ref].full_page.frame;
646 BUG_ON(frame == 0);
647
648 return frame;
649}
650
Annie Li0f9f5a92011-11-22 09:58:06 +0800651unsigned long gnttab_end_foreign_transfer_ref(grant_ref_t ref)
652{
653 return gnttab_interface->end_foreign_transfer_ref(ref);
654}
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700655EXPORT_SYMBOL_GPL(gnttab_end_foreign_transfer_ref);
656
657unsigned long gnttab_end_foreign_transfer(grant_ref_t ref)
658{
659 unsigned long frame = gnttab_end_foreign_transfer_ref(ref);
660 put_free_entry(ref);
661 return frame;
662}
663EXPORT_SYMBOL_GPL(gnttab_end_foreign_transfer);
664
665void gnttab_free_grant_reference(grant_ref_t ref)
666{
667 put_free_entry(ref);
668}
669EXPORT_SYMBOL_GPL(gnttab_free_grant_reference);
670
671void gnttab_free_grant_references(grant_ref_t head)
672{
673 grant_ref_t ref;
674 unsigned long flags;
675 int count = 1;
676 if (head == GNTTAB_LIST_END)
677 return;
678 spin_lock_irqsave(&gnttab_list_lock, flags);
679 ref = head;
680 while (gnttab_entry(ref) != GNTTAB_LIST_END) {
681 ref = gnttab_entry(ref);
682 count++;
683 }
684 gnttab_entry(ref) = gnttab_free_head;
685 gnttab_free_head = head;
686 gnttab_free_count += count;
687 check_free_callbacks();
688 spin_unlock_irqrestore(&gnttab_list_lock, flags);
689}
690EXPORT_SYMBOL_GPL(gnttab_free_grant_references);
691
692int gnttab_alloc_grant_references(u16 count, grant_ref_t *head)
693{
694 int h = get_free_entries(count);
695
696 if (h < 0)
697 return -ENOSPC;
698
699 *head = h;
700
701 return 0;
702}
703EXPORT_SYMBOL_GPL(gnttab_alloc_grant_references);
704
705int gnttab_empty_grant_references(const grant_ref_t *private_head)
706{
707 return (*private_head == GNTTAB_LIST_END);
708}
709EXPORT_SYMBOL_GPL(gnttab_empty_grant_references);
710
711int gnttab_claim_grant_reference(grant_ref_t *private_head)
712{
713 grant_ref_t g = *private_head;
714 if (unlikely(g == GNTTAB_LIST_END))
715 return -ENOSPC;
716 *private_head = gnttab_entry(g);
717 return g;
718}
719EXPORT_SYMBOL_GPL(gnttab_claim_grant_reference);
720
721void gnttab_release_grant_reference(grant_ref_t *private_head,
722 grant_ref_t release)
723{
724 gnttab_entry(release) = *private_head;
725 *private_head = release;
726}
727EXPORT_SYMBOL_GPL(gnttab_release_grant_reference);
728
729void gnttab_request_free_callback(struct gnttab_free_callback *callback,
730 void (*fn)(void *), void *arg, u16 count)
731{
732 unsigned long flags;
733 spin_lock_irqsave(&gnttab_list_lock, flags);
734 if (callback->next)
735 goto out;
736 callback->fn = fn;
737 callback->arg = arg;
738 callback->count = count;
739 callback->next = gnttab_free_callback_list;
740 gnttab_free_callback_list = callback;
741 check_free_callbacks();
742out:
743 spin_unlock_irqrestore(&gnttab_list_lock, flags);
744}
745EXPORT_SYMBOL_GPL(gnttab_request_free_callback);
746
747void gnttab_cancel_free_callback(struct gnttab_free_callback *callback)
748{
749 struct gnttab_free_callback **pcb;
750 unsigned long flags;
751
752 spin_lock_irqsave(&gnttab_list_lock, flags);
753 for (pcb = &gnttab_free_callback_list; *pcb; pcb = &(*pcb)->next) {
754 if (*pcb == callback) {
755 *pcb = callback->next;
756 break;
757 }
758 }
759 spin_unlock_irqrestore(&gnttab_list_lock, flags);
760}
761EXPORT_SYMBOL_GPL(gnttab_cancel_free_callback);
762
763static int grow_gnttab_list(unsigned int more_frames)
764{
765 unsigned int new_nr_grant_frames, extra_entries, i;
Michael Abd-El-Malekbbc60c12008-04-04 02:33:48 -0700766 unsigned int nr_glist_frames, new_nr_glist_frames;
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700767
768 new_nr_grant_frames = nr_grant_frames + more_frames;
769 extra_entries = more_frames * GREFS_PER_GRANT_FRAME;
770
Michael Abd-El-Malekbbc60c12008-04-04 02:33:48 -0700771 nr_glist_frames = (nr_grant_frames * GREFS_PER_GRANT_FRAME + RPP - 1) / RPP;
772 new_nr_glist_frames =
773 (new_nr_grant_frames * GREFS_PER_GRANT_FRAME + RPP - 1) / RPP;
774 for (i = nr_glist_frames; i < new_nr_glist_frames; i++) {
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700775 gnttab_list[i] = (grant_ref_t *)__get_free_page(GFP_ATOMIC);
776 if (!gnttab_list[i])
777 goto grow_nomem;
778 }
779
780
781 for (i = GREFS_PER_GRANT_FRAME * nr_grant_frames;
782 i < GREFS_PER_GRANT_FRAME * new_nr_grant_frames - 1; i++)
783 gnttab_entry(i) = i + 1;
784
785 gnttab_entry(i) = gnttab_free_head;
786 gnttab_free_head = GREFS_PER_GRANT_FRAME * nr_grant_frames;
787 gnttab_free_count += extra_entries;
788
789 nr_grant_frames = new_nr_grant_frames;
790
791 check_free_callbacks();
792
793 return 0;
794
795grow_nomem:
Michael Abd-El-Malekbbc60c12008-04-04 02:33:48 -0700796 for ( ; i >= nr_glist_frames; i--)
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700797 free_page((unsigned long) gnttab_list[i]);
798 return -ENOMEM;
799}
800
801static unsigned int __max_nr_grant_frames(void)
802{
803 struct gnttab_query_size query;
804 int rc;
805
806 query.dom = DOMID_SELF;
807
808 rc = HYPERVISOR_grant_table_op(GNTTABOP_query_size, &query, 1);
809 if ((rc < 0) || (query.status != GNTST_okay))
810 return 4; /* Legacy max supported number of frames */
811
812 return query.max_nr_frames;
813}
814
Stefano Stabellini183d03c2010-05-17 17:08:21 +0100815unsigned int gnttab_max_grant_frames(void)
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700816{
817 unsigned int xen_max = __max_nr_grant_frames();
818
819 if (xen_max > boot_max_nr_grant_frames)
820 return boot_max_nr_grant_frames;
821 return xen_max;
822}
Stefano Stabellini183d03c2010-05-17 17:08:21 +0100823EXPORT_SYMBOL_GPL(gnttab_max_grant_frames);
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700824
Stefano Stabellini289b7772010-12-10 14:54:44 +0000825int gnttab_map_refs(struct gnttab_map_grant_ref *map_ops,
Annie Lic1237992011-11-22 09:59:56 +0800826 struct gnttab_map_grant_ref *kmap_ops,
827 struct page **pages, unsigned int count)
Stefano Stabellini289b7772010-12-10 14:54:44 +0000828{
829 int i, ret;
830 pte_t *pte;
831 unsigned long mfn;
832
833 ret = HYPERVISOR_grant_table_op(GNTTABOP_map_grant_ref, map_ops, count);
Jeremy Fitzhardinge87f1d402010-12-13 14:42:30 +0000834 if (ret)
835 return ret;
Stefano Stabellini289b7772010-12-10 14:54:44 +0000836
Daniel De Graafaab8f112011-02-03 12:19:02 -0500837 if (xen_feature(XENFEAT_auto_translated_physmap))
838 return ret;
839
Stefano Stabellini289b7772010-12-10 14:54:44 +0000840 for (i = 0; i < count; i++) {
Ian Campbelldc4972a2011-03-04 17:38:21 +0000841 /* Do not add to override if the map failed. */
842 if (map_ops[i].status)
843 continue;
844
Konrad Rzeszutek Wilkcf8d9162011-02-28 17:58:48 -0500845 if (map_ops[i].flags & GNTMAP_contains_pte) {
846 pte = (pte_t *) (mfn_to_virt(PFN_DOWN(map_ops[i].host_addr)) +
Stefano Stabellini289b7772010-12-10 14:54:44 +0000847 (map_ops[i].host_addr & ~PAGE_MASK));
Konrad Rzeszutek Wilkcf8d9162011-02-28 17:58:48 -0500848 mfn = pte_mfn(*pte);
849 } else {
Daniel De Graaf7d17e842011-12-14 15:12:11 -0500850 mfn = PFN_DOWN(map_ops[i].dev_bus_addr);
Konrad Rzeszutek Wilkcf8d9162011-02-28 17:58:48 -0500851 }
Daniel De Graaf7d17e842011-12-14 15:12:11 -0500852 ret = m2p_add_override(mfn, pages[i], kmap_ops ?
853 &kmap_ops[i] : NULL);
Jeremy Fitzhardinge87f1d402010-12-13 14:42:30 +0000854 if (ret)
855 return ret;
Stefano Stabellini289b7772010-12-10 14:54:44 +0000856 }
857
858 return ret;
859}
860EXPORT_SYMBOL_GPL(gnttab_map_refs);
861
862int gnttab_unmap_refs(struct gnttab_unmap_grant_ref *unmap_ops,
Daniel De Graaf7d17e842011-12-14 15:12:11 -0500863 struct page **pages, unsigned int count, bool clear_pte)
Stefano Stabellini289b7772010-12-10 14:54:44 +0000864{
865 int i, ret;
866
867 ret = HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref, unmap_ops, count);
Jeremy Fitzhardinge87f1d402010-12-13 14:42:30 +0000868 if (ret)
869 return ret;
870
Daniel De Graafaab8f112011-02-03 12:19:02 -0500871 if (xen_feature(XENFEAT_auto_translated_physmap))
872 return ret;
873
Jeremy Fitzhardinge87f1d402010-12-13 14:42:30 +0000874 for (i = 0; i < count; i++) {
Daniel De Graaf7d17e842011-12-14 15:12:11 -0500875 ret = m2p_remove_override(pages[i], clear_pte);
Jeremy Fitzhardinge87f1d402010-12-13 14:42:30 +0000876 if (ret)
877 return ret;
878 }
Stefano Stabellini289b7772010-12-10 14:54:44 +0000879
880 return ret;
881}
882EXPORT_SYMBOL_GPL(gnttab_unmap_refs);
883
Annie Li85ff6ac2011-11-22 09:59:21 +0800884static unsigned nr_status_frames(unsigned nr_grant_frames)
885{
886 return (nr_grant_frames * GREFS_PER_GRANT_FRAME + SPP - 1) / SPP;
887}
888
Annie Li0f9f5a92011-11-22 09:58:06 +0800889static int gnttab_map_frames_v1(unsigned long *frames, unsigned int nr_gframes)
890{
891 int rc;
892
893 rc = arch_gnttab_map_shared(frames, nr_gframes,
894 gnttab_max_grant_frames(),
895 &gnttab_shared.addr);
896 BUG_ON(rc);
897
898 return 0;
899}
900
901static void gnttab_unmap_frames_v1(void)
902{
Annie Li85ff6ac2011-11-22 09:59:21 +0800903 arch_gnttab_unmap(gnttab_shared.addr, nr_grant_frames);
904}
905
906static int gnttab_map_frames_v2(unsigned long *frames, unsigned int nr_gframes)
907{
908 uint64_t *sframes;
909 unsigned int nr_sframes;
910 struct gnttab_get_status_frames getframes;
911 int rc;
912
913 nr_sframes = nr_status_frames(nr_gframes);
914
915 /* No need for kzalloc as it is initialized in following hypercall
916 * GNTTABOP_get_status_frames.
917 */
918 sframes = kmalloc(nr_sframes * sizeof(uint64_t), GFP_ATOMIC);
919 if (!sframes)
920 return -ENOMEM;
921
922 getframes.dom = DOMID_SELF;
923 getframes.nr_frames = nr_sframes;
924 set_xen_guest_handle(getframes.frame_list, sframes);
925
926 rc = HYPERVISOR_grant_table_op(GNTTABOP_get_status_frames,
927 &getframes, 1);
928 if (rc == -ENOSYS) {
929 kfree(sframes);
930 return -ENOSYS;
931 }
932
933 BUG_ON(rc || getframes.status);
934
935 rc = arch_gnttab_map_status(sframes, nr_sframes,
936 nr_status_frames(gnttab_max_grant_frames()),
937 &grstatus);
938 BUG_ON(rc);
939 kfree(sframes);
940
941 rc = arch_gnttab_map_shared(frames, nr_gframes,
942 gnttab_max_grant_frames(),
943 &gnttab_shared.addr);
944 BUG_ON(rc);
945
946 return 0;
947}
948
949static void gnttab_unmap_frames_v2(void)
950{
951 arch_gnttab_unmap(gnttab_shared.addr, nr_grant_frames);
952 arch_gnttab_unmap(grstatus, nr_status_frames(nr_grant_frames));
Annie Li0f9f5a92011-11-22 09:58:06 +0800953}
954
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700955static int gnttab_map(unsigned int start_idx, unsigned int end_idx)
956{
957 struct gnttab_setup_table setup;
958 unsigned long *frames;
959 unsigned int nr_gframes = end_idx + 1;
960 int rc;
961
Stefano Stabellini183d03c2010-05-17 17:08:21 +0100962 if (xen_hvm_domain()) {
963 struct xen_add_to_physmap xatp;
964 unsigned int i = end_idx;
965 rc = 0;
966 /*
967 * Loop backwards, so that the first hypercall has the largest
968 * index, ensuring that the table will grow only once.
969 */
970 do {
971 xatp.domid = DOMID_SELF;
972 xatp.idx = i;
973 xatp.space = XENMAPSPACE_grant_table;
974 xatp.gpfn = (xen_hvm_resume_frames >> PAGE_SHIFT) + i;
975 rc = HYPERVISOR_memory_op(XENMEM_add_to_physmap, &xatp);
976 if (rc != 0) {
977 printk(KERN_WARNING
978 "grant table add_to_physmap failed, err=%d\n", rc);
979 break;
980 }
981 } while (i-- > start_idx);
982
983 return rc;
984 }
985
Annie Li85ff6ac2011-11-22 09:59:21 +0800986 /* No need for kzalloc as it is initialized in following hypercall
987 * GNTTABOP_setup_table.
988 */
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700989 frames = kmalloc(nr_gframes * sizeof(unsigned long), GFP_ATOMIC);
990 if (!frames)
991 return -ENOMEM;
992
993 setup.dom = DOMID_SELF;
994 setup.nr_frames = nr_gframes;
Isaku Yamahata87e27cf2008-04-02 10:53:52 -0700995 set_xen_guest_handle(setup.frame_list, frames);
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700996
997 rc = HYPERVISOR_grant_table_op(GNTTABOP_setup_table, &setup, 1);
998 if (rc == -ENOSYS) {
999 kfree(frames);
1000 return -ENOSYS;
1001 }
1002
1003 BUG_ON(rc || setup.status);
1004
Annie Li0f9f5a92011-11-22 09:58:06 +08001005 rc = gnttab_interface->map_frames(frames, nr_gframes);
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -07001006
1007 kfree(frames);
1008
Annie Li0f9f5a92011-11-22 09:58:06 +08001009 return rc;
1010}
1011
1012static struct gnttab_ops gnttab_v1_ops = {
1013 .map_frames = gnttab_map_frames_v1,
1014 .unmap_frames = gnttab_unmap_frames_v1,
1015 .update_entry = gnttab_update_entry_v1,
1016 .end_foreign_access_ref = gnttab_end_foreign_access_ref_v1,
1017 .end_foreign_transfer_ref = gnttab_end_foreign_transfer_ref_v1,
1018 .query_foreign_access = gnttab_query_foreign_access_v1,
1019};
1020
Annie Li85ff6ac2011-11-22 09:59:21 +08001021static struct gnttab_ops gnttab_v2_ops = {
1022 .map_frames = gnttab_map_frames_v2,
1023 .unmap_frames = gnttab_unmap_frames_v2,
1024 .update_entry = gnttab_update_entry_v2,
1025 .end_foreign_access_ref = gnttab_end_foreign_access_ref_v2,
1026 .end_foreign_transfer_ref = gnttab_end_foreign_transfer_ref_v2,
1027 .query_foreign_access = gnttab_query_foreign_access_v2,
Annie Li66667542011-12-12 18:14:42 +08001028 .update_subpage_entry = gnttab_update_subpage_entry_v2,
Annie Li9438ce92011-12-12 18:15:07 +08001029 .update_trans_entry = gnttab_update_trans_entry_v2,
Annie Li85ff6ac2011-11-22 09:59:21 +08001030};
1031
Annie Li0f9f5a92011-11-22 09:58:06 +08001032static void gnttab_request_version(void)
1033{
Annie Li85ff6ac2011-11-22 09:59:21 +08001034 int rc;
1035 struct gnttab_set_version gsv;
1036
Konrad Rzeszutek Wilk69e8f432012-01-25 00:13:20 -05001037 if (xen_hvm_domain())
1038 gsv.version = 1;
1039 else
1040 gsv.version = 2;
Annie Li85ff6ac2011-11-22 09:59:21 +08001041 rc = HYPERVISOR_grant_table_op(GNTTABOP_set_version, &gsv, 1);
Konrad Rzeszutek Wilk69e8f432012-01-25 00:13:20 -05001042 if (rc == 0 && gsv.version == 2) {
Annie Li85ff6ac2011-11-22 09:59:21 +08001043 grant_table_version = 2;
1044 gnttab_interface = &gnttab_v2_ops;
1045 } else if (grant_table_version == 2) {
1046 /*
1047 * If we've already used version 2 features,
1048 * but then suddenly discover that they're not
1049 * available (e.g. migrating to an older
1050 * version of Xen), almost unbounded badness
1051 * can happen.
1052 */
1053 panic("we need grant tables version 2, but only version 1 is available");
1054 } else {
1055 grant_table_version = 1;
1056 gnttab_interface = &gnttab_v1_ops;
1057 }
Annie Li0f9f5a92011-11-22 09:58:06 +08001058 printk(KERN_INFO "Grant tables using version %d layout.\n",
1059 grant_table_version);
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -07001060}
1061
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +01001062int gnttab_resume(void)
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -07001063{
Stefano Stabellini183d03c2010-05-17 17:08:21 +01001064 unsigned int max_nr_gframes;
1065
Annie Li0f9f5a92011-11-22 09:58:06 +08001066 gnttab_request_version();
Stefano Stabellini183d03c2010-05-17 17:08:21 +01001067 max_nr_gframes = gnttab_max_grant_frames();
1068 if (max_nr_gframes < nr_grant_frames)
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -07001069 return -ENOSYS;
Stefano Stabellini183d03c2010-05-17 17:08:21 +01001070
1071 if (xen_pv_domain())
1072 return gnttab_map(0, nr_grant_frames - 1);
1073
Annie Li0f9f5a92011-11-22 09:58:06 +08001074 if (gnttab_shared.addr == NULL) {
1075 gnttab_shared.addr = ioremap(xen_hvm_resume_frames,
1076 PAGE_SIZE * max_nr_gframes);
1077 if (gnttab_shared.addr == NULL) {
Stefano Stabellini183d03c2010-05-17 17:08:21 +01001078 printk(KERN_WARNING
1079 "Failed to ioremap gnttab share frames!");
1080 return -ENOMEM;
1081 }
1082 }
1083
1084 gnttab_map(0, nr_grant_frames - 1);
1085
1086 return 0;
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -07001087}
1088
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +01001089int gnttab_suspend(void)
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -07001090{
Annie Li0f9f5a92011-11-22 09:58:06 +08001091 gnttab_interface->unmap_frames();
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -07001092 return 0;
1093}
1094
1095static int gnttab_expand(unsigned int req_entries)
1096{
1097 int rc;
1098 unsigned int cur, extra;
1099
1100 cur = nr_grant_frames;
1101 extra = ((req_entries + (GREFS_PER_GRANT_FRAME-1)) /
1102 GREFS_PER_GRANT_FRAME);
Stefano Stabellini183d03c2010-05-17 17:08:21 +01001103 if (cur + extra > gnttab_max_grant_frames())
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -07001104 return -ENOSPC;
1105
1106 rc = gnttab_map(cur, cur + extra - 1);
1107 if (rc == 0)
1108 rc = grow_gnttab_list(extra);
1109
1110 return rc;
1111}
1112
Stefano Stabellini183d03c2010-05-17 17:08:21 +01001113int gnttab_init(void)
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -07001114{
1115 int i;
Michael Abd-El-Malekbbc60c12008-04-04 02:33:48 -07001116 unsigned int max_nr_glist_frames, nr_glist_frames;
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -07001117 unsigned int nr_init_grefs;
1118
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -07001119 nr_grant_frames = 1;
1120 boot_max_nr_grant_frames = __max_nr_grant_frames();
1121
1122 /* Determine the maximum number of frames required for the
1123 * grant reference free list on the current hypervisor.
1124 */
1125 max_nr_glist_frames = (boot_max_nr_grant_frames *
Michael Abd-El-Malekbbc60c12008-04-04 02:33:48 -07001126 GREFS_PER_GRANT_FRAME / RPP);
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -07001127
1128 gnttab_list = kmalloc(max_nr_glist_frames * sizeof(grant_ref_t *),
1129 GFP_KERNEL);
1130 if (gnttab_list == NULL)
1131 return -ENOMEM;
1132
Michael Abd-El-Malekbbc60c12008-04-04 02:33:48 -07001133 nr_glist_frames = (nr_grant_frames * GREFS_PER_GRANT_FRAME + RPP - 1) / RPP;
1134 for (i = 0; i < nr_glist_frames; i++) {
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -07001135 gnttab_list[i] = (grant_ref_t *)__get_free_page(GFP_KERNEL);
1136 if (gnttab_list[i] == NULL)
1137 goto ini_nomem;
1138 }
1139
1140 if (gnttab_resume() < 0)
1141 return -ENODEV;
1142
1143 nr_init_grefs = nr_grant_frames * GREFS_PER_GRANT_FRAME;
1144
1145 for (i = NR_RESERVED_ENTRIES; i < nr_init_grefs - 1; i++)
1146 gnttab_entry(i) = i + 1;
1147
1148 gnttab_entry(nr_init_grefs - 1) = GNTTAB_LIST_END;
1149 gnttab_free_count = nr_init_grefs - NR_RESERVED_ENTRIES;
1150 gnttab_free_head = NR_RESERVED_ENTRIES;
1151
1152 printk("Grant table initialized\n");
1153 return 0;
1154
1155 ini_nomem:
1156 for (i--; i >= 0; i--)
1157 free_page((unsigned long)gnttab_list[i]);
1158 kfree(gnttab_list);
1159 return -ENOMEM;
1160}
Stefano Stabellini183d03c2010-05-17 17:08:21 +01001161EXPORT_SYMBOL_GPL(gnttab_init);
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -07001162
Stefano Stabellini183d03c2010-05-17 17:08:21 +01001163static int __devinit __gnttab_init(void)
1164{
1165 /* Delay grant-table initialization in the PV on HVM case */
1166 if (xen_hvm_domain())
1167 return 0;
1168
1169 if (!xen_pv_domain())
1170 return -ENODEV;
1171
1172 return gnttab_init();
1173}
1174
1175core_initcall(__gnttab_init);