blob: 7e34beed9ea9062e5b9c3ece1a29823d258425fa [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
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -070056
57static grant_ref_t **gnttab_list;
58static unsigned int nr_grant_frames;
59static unsigned int boot_max_nr_grant_frames;
60static int gnttab_free_count;
61static grant_ref_t gnttab_free_head;
62static DEFINE_SPINLOCK(gnttab_list_lock);
Stefano Stabellini183d03c2010-05-17 17:08:21 +010063unsigned long xen_hvm_resume_frames;
64EXPORT_SYMBOL_GPL(xen_hvm_resume_frames);
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -070065
Annie Li0f9f5a92011-11-22 09:58:06 +080066static union {
67 struct grant_entry_v1 *v1;
Annie Li85ff6ac2011-11-22 09:59:21 +080068 union grant_entry_v2 *v2;
Annie Li0f9f5a92011-11-22 09:58:06 +080069 void *addr;
70} gnttab_shared;
71
72/*This is a structure of function pointers for grant table*/
73struct gnttab_ops {
74 /*
Annie Li9dbc71d2011-12-12 18:13:57 +080075 * Mapping a list of frames for storing grant entries. Frames parameter
76 * is used to store grant table address when grant table being setup,
77 * nr_gframes is the number of frames to map grant table. Returning
78 * GNTST_okay means success and negative value means failure.
Annie Li0f9f5a92011-11-22 09:58:06 +080079 */
Annie Li9dbc71d2011-12-12 18:13:57 +080080 int (*map_frames)(unsigned long *frames, unsigned int nr_gframes);
Annie Li0f9f5a92011-11-22 09:58:06 +080081 /*
82 * Release a list of frames which are mapped in map_frames for grant
83 * entry status.
84 */
85 void (*unmap_frames)(void);
86 /*
Annie Li9dbc71d2011-12-12 18:13:57 +080087 * Introducing a valid entry into the grant table, granting the frame of
88 * this grant entry to domain for accessing or transfering. Ref
89 * parameter is reference of this introduced grant entry, domid is id of
90 * granted domain, frame is the page frame to be granted, and flags is
91 * status of the grant entry to be updated.
Annie Li0f9f5a92011-11-22 09:58:06 +080092 */
Annie Li9dbc71d2011-12-12 18:13:57 +080093 void (*update_entry)(grant_ref_t ref, domid_t domid,
94 unsigned long frame, unsigned flags);
Annie Li0f9f5a92011-11-22 09:58:06 +080095 /*
Annie Li9dbc71d2011-12-12 18:13:57 +080096 * Stop granting a grant entry to domain for accessing. Ref parameter is
97 * reference of a grant entry whose grant access will be stopped,
98 * readonly is not in use in this function. If the grant entry is
Annie Li0f9f5a92011-11-22 09:58:06 +080099 * currently mapped for reading or writing, just return failure(==0)
100 * directly and don't tear down the grant access. Otherwise, stop grant
101 * access for this entry and return success(==1).
102 */
Annie Li9dbc71d2011-12-12 18:13:57 +0800103 int (*end_foreign_access_ref)(grant_ref_t ref, int readonly);
Annie Li0f9f5a92011-11-22 09:58:06 +0800104 /*
Annie Li9dbc71d2011-12-12 18:13:57 +0800105 * Stop granting a grant entry to domain for transfer. Ref parameter is
106 * reference of a grant entry whose grant transfer will be stopped. If
107 * tranfer has not started, just reclaim the grant entry and return
108 * failure(==0). Otherwise, wait for the transfer to complete and then
109 * return the frame.
Annie Li0f9f5a92011-11-22 09:58:06 +0800110 */
Annie Li9dbc71d2011-12-12 18:13:57 +0800111 unsigned long (*end_foreign_transfer_ref)(grant_ref_t ref);
Annie Li0f9f5a92011-11-22 09:58:06 +0800112 /*
Annie Li9dbc71d2011-12-12 18:13:57 +0800113 * Query the status of a grant entry. Ref parameter is reference of
Annie Li0f9f5a92011-11-22 09:58:06 +0800114 * queried grant entry, return value is the status of queried entry.
115 * Detailed status(writing/reading) can be gotten from the return value
116 * by bit operations.
117 */
Annie Li9dbc71d2011-12-12 18:13:57 +0800118 int (*query_foreign_access)(grant_ref_t ref);
Annie Li66667542011-12-12 18:14:42 +0800119 /*
120 * Grant a domain to access a range of bytes within the page referred by
121 * an available grant entry. Ref parameter is reference of a grant entry
122 * which will be sub-page accessed, domid is id of grantee domain, frame
123 * is frame address of subpage grant, flags is grant type and flag
124 * information, page_off is offset of the range of bytes, and length is
125 * length of bytes to be accessed.
126 */
127 void (*update_subpage_entry)(grant_ref_t ref, domid_t domid,
128 unsigned long frame, int flags,
129 unsigned page_off, unsigned length);
Annie Li9438ce92011-12-12 18:15:07 +0800130 /*
131 * Redirect an available grant entry on domain A to another grant
132 * reference of domain B, then allow domain C to use grant reference
133 * of domain B transitively. Ref parameter is an available grant entry
134 * reference on domain A, domid is id of domain C which accesses grant
135 * entry transitively, flags is grant type and flag information,
136 * trans_domid is id of domain B whose grant entry is finally accessed
137 * transitively, trans_gref is grant entry transitive reference of
138 * domain B.
139 */
140 void (*update_trans_entry)(grant_ref_t ref, domid_t domid, int flags,
141 domid_t trans_domid, grant_ref_t trans_gref);
Annie Li0f9f5a92011-11-22 09:58:06 +0800142};
143
144static struct gnttab_ops *gnttab_interface;
145
Annie Li85ff6ac2011-11-22 09:59:21 +0800146/*This reflects status of grant entries, so act as a global value*/
147static grant_status_t *grstatus;
148
Annie Li0f9f5a92011-11-22 09:58:06 +0800149static int grant_table_version;
Matt Wilsonffe28822013-01-15 13:21:27 +0000150static int grefs_per_grant_frame;
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700151
152static struct gnttab_free_callback *gnttab_free_callback_list;
153
154static int gnttab_expand(unsigned int req_entries);
155
156#define RPP (PAGE_SIZE / sizeof(grant_ref_t))
Annie Li85ff6ac2011-11-22 09:59:21 +0800157#define SPP (PAGE_SIZE / sizeof(grant_status_t))
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700158
159static inline grant_ref_t *__gnttab_entry(grant_ref_t entry)
160{
161 return &gnttab_list[(entry) / RPP][(entry) % RPP];
162}
163/* This can be used as an l-value */
164#define gnttab_entry(entry) (*__gnttab_entry(entry))
165
166static int get_free_entries(unsigned count)
167{
168 unsigned long flags;
Konrad Rzeszutek Wilk272800d2011-07-22 14:00:06 -0400169 int ref, rc = 0;
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700170 grant_ref_t head;
171
172 spin_lock_irqsave(&gnttab_list_lock, flags);
173
174 if ((gnttab_free_count < count) &&
175 ((rc = gnttab_expand(count - gnttab_free_count)) < 0)) {
176 spin_unlock_irqrestore(&gnttab_list_lock, flags);
177 return rc;
178 }
179
180 ref = head = gnttab_free_head;
181 gnttab_free_count -= count;
182 while (count-- > 1)
183 head = gnttab_entry(head);
184 gnttab_free_head = gnttab_entry(head);
185 gnttab_entry(head) = GNTTAB_LIST_END;
186
187 spin_unlock_irqrestore(&gnttab_list_lock, flags);
188
189 return ref;
190}
191
192static void do_free_callbacks(void)
193{
194 struct gnttab_free_callback *callback, *next;
195
196 callback = gnttab_free_callback_list;
197 gnttab_free_callback_list = NULL;
198
199 while (callback != NULL) {
200 next = callback->next;
201 if (gnttab_free_count >= callback->count) {
202 callback->next = NULL;
203 callback->fn(callback->arg);
204 } else {
205 callback->next = gnttab_free_callback_list;
206 gnttab_free_callback_list = callback;
207 }
208 callback = next;
209 }
210}
211
212static inline void check_free_callbacks(void)
213{
214 if (unlikely(gnttab_free_callback_list))
215 do_free_callbacks();
216}
217
218static void put_free_entry(grant_ref_t ref)
219{
220 unsigned long flags;
221 spin_lock_irqsave(&gnttab_list_lock, flags);
222 gnttab_entry(ref) = gnttab_free_head;
223 gnttab_free_head = ref;
224 gnttab_free_count++;
225 check_free_callbacks();
226 spin_unlock_irqrestore(&gnttab_list_lock, flags);
227}
228
Annie Li0f9f5a92011-11-22 09:58:06 +0800229/*
Annie Li85ff6ac2011-11-22 09:59:21 +0800230 * Following applies to gnttab_update_entry_v1 and gnttab_update_entry_v2.
Annie Li0f9f5a92011-11-22 09:58:06 +0800231 * Introducing a valid entry into the grant table:
232 * 1. Write ent->domid.
233 * 2. Write ent->frame:
234 * GTF_permit_access: Frame to which access is permitted.
235 * GTF_accept_transfer: Pseudo-phys frame slot being filled by new
236 * frame, or zero if none.
237 * 3. Write memory barrier (WMB).
238 * 4. Write ent->flags, inc. valid type.
239 */
240static void gnttab_update_entry_v1(grant_ref_t ref, domid_t domid,
241 unsigned long frame, unsigned flags)
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700242{
Annie Li0f9f5a92011-11-22 09:58:06 +0800243 gnttab_shared.v1[ref].domid = domid;
244 gnttab_shared.v1[ref].frame = frame;
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700245 wmb();
Annie Li0f9f5a92011-11-22 09:58:06 +0800246 gnttab_shared.v1[ref].flags = flags;
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700247}
248
Annie Li85ff6ac2011-11-22 09:59:21 +0800249static void gnttab_update_entry_v2(grant_ref_t ref, domid_t domid,
250 unsigned long frame, unsigned flags)
251{
252 gnttab_shared.v2[ref].hdr.domid = domid;
253 gnttab_shared.v2[ref].full_page.frame = frame;
254 wmb();
255 gnttab_shared.v2[ref].hdr.flags = GTF_permit_access | flags;
256}
257
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700258/*
259 * Public grant-issuing interface functions
260 */
261void gnttab_grant_foreign_access_ref(grant_ref_t ref, domid_t domid,
262 unsigned long frame, int readonly)
263{
Annie Li0f9f5a92011-11-22 09:58:06 +0800264 gnttab_interface->update_entry(ref, domid, frame,
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700265 GTF_permit_access | (readonly ? GTF_readonly : 0));
266}
267EXPORT_SYMBOL_GPL(gnttab_grant_foreign_access_ref);
268
269int gnttab_grant_foreign_access(domid_t domid, unsigned long frame,
270 int readonly)
271{
272 int ref;
273
274 ref = get_free_entries(1);
275 if (unlikely(ref < 0))
276 return -ENOSPC;
277
278 gnttab_grant_foreign_access_ref(ref, domid, frame, readonly);
279
280 return ref;
281}
282EXPORT_SYMBOL_GPL(gnttab_grant_foreign_access);
283
Annie Li66667542011-12-12 18:14:42 +0800284void gnttab_update_subpage_entry_v2(grant_ref_t ref, domid_t domid,
285 unsigned long frame, int flags,
286 unsigned page_off,
287 unsigned length)
288{
289 gnttab_shared.v2[ref].sub_page.frame = frame;
290 gnttab_shared.v2[ref].sub_page.page_off = page_off;
291 gnttab_shared.v2[ref].sub_page.length = length;
292 gnttab_shared.v2[ref].hdr.domid = domid;
293 wmb();
294 gnttab_shared.v2[ref].hdr.flags =
295 GTF_permit_access | GTF_sub_page | flags;
296}
297
298int gnttab_grant_foreign_access_subpage_ref(grant_ref_t ref, domid_t domid,
299 unsigned long frame, int flags,
300 unsigned page_off,
301 unsigned length)
302{
303 if (flags & (GTF_accept_transfer | GTF_reading |
304 GTF_writing | GTF_transitive))
305 return -EPERM;
306
307 if (gnttab_interface->update_subpage_entry == NULL)
308 return -ENOSYS;
309
310 gnttab_interface->update_subpage_entry(ref, domid, frame, flags,
311 page_off, length);
312
313 return 0;
314}
315EXPORT_SYMBOL_GPL(gnttab_grant_foreign_access_subpage_ref);
316
317int gnttab_grant_foreign_access_subpage(domid_t domid, unsigned long frame,
318 int flags, unsigned page_off,
319 unsigned length)
320{
321 int ref, rc;
322
323 ref = get_free_entries(1);
324 if (unlikely(ref < 0))
325 return -ENOSPC;
326
327 rc = gnttab_grant_foreign_access_subpage_ref(ref, domid, frame, flags,
328 page_off, length);
329 if (rc < 0) {
330 put_free_entry(ref);
331 return rc;
332 }
333
334 return ref;
335}
336EXPORT_SYMBOL_GPL(gnttab_grant_foreign_access_subpage);
337
338bool gnttab_subpage_grants_available(void)
339{
340 return gnttab_interface->update_subpage_entry != NULL;
341}
342EXPORT_SYMBOL_GPL(gnttab_subpage_grants_available);
343
Annie Li9438ce92011-12-12 18:15:07 +0800344void gnttab_update_trans_entry_v2(grant_ref_t ref, domid_t domid,
345 int flags, domid_t trans_domid,
346 grant_ref_t trans_gref)
347{
348 gnttab_shared.v2[ref].transitive.trans_domid = trans_domid;
349 gnttab_shared.v2[ref].transitive.gref = trans_gref;
350 gnttab_shared.v2[ref].hdr.domid = domid;
351 wmb();
352 gnttab_shared.v2[ref].hdr.flags =
353 GTF_permit_access | GTF_transitive | flags;
354}
355
356int gnttab_grant_foreign_access_trans_ref(grant_ref_t ref, domid_t domid,
357 int flags, domid_t trans_domid,
358 grant_ref_t trans_gref)
359{
360 if (flags & (GTF_accept_transfer | GTF_reading |
361 GTF_writing | GTF_sub_page))
362 return -EPERM;
363
364 if (gnttab_interface->update_trans_entry == NULL)
365 return -ENOSYS;
366
367 gnttab_interface->update_trans_entry(ref, domid, flags, trans_domid,
368 trans_gref);
369
370 return 0;
371}
372EXPORT_SYMBOL_GPL(gnttab_grant_foreign_access_trans_ref);
373
374int gnttab_grant_foreign_access_trans(domid_t domid, int flags,
375 domid_t trans_domid,
376 grant_ref_t trans_gref)
377{
378 int ref, rc;
379
380 ref = get_free_entries(1);
381 if (unlikely(ref < 0))
382 return -ENOSPC;
383
384 rc = gnttab_grant_foreign_access_trans_ref(ref, domid, flags,
385 trans_domid, trans_gref);
386 if (rc < 0) {
387 put_free_entry(ref);
388 return rc;
389 }
390
391 return ref;
392}
393EXPORT_SYMBOL_GPL(gnttab_grant_foreign_access_trans);
394
395bool gnttab_trans_grants_available(void)
396{
397 return gnttab_interface->update_trans_entry != NULL;
398}
399EXPORT_SYMBOL_GPL(gnttab_trans_grants_available);
400
Annie Li0f9f5a92011-11-22 09:58:06 +0800401static int gnttab_query_foreign_access_v1(grant_ref_t ref)
402{
403 return gnttab_shared.v1[ref].flags & (GTF_reading|GTF_writing);
404}
405
Annie Li85ff6ac2011-11-22 09:59:21 +0800406static int gnttab_query_foreign_access_v2(grant_ref_t ref)
407{
408 return grstatus[ref] & (GTF_reading|GTF_writing);
409}
410
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700411int gnttab_query_foreign_access(grant_ref_t ref)
412{
Annie Li0f9f5a92011-11-22 09:58:06 +0800413 return gnttab_interface->query_foreign_access(ref);
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700414}
415EXPORT_SYMBOL_GPL(gnttab_query_foreign_access);
416
Annie Li0f9f5a92011-11-22 09:58:06 +0800417static int gnttab_end_foreign_access_ref_v1(grant_ref_t ref, int readonly)
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700418{
419 u16 flags, nflags;
Annie Lib1e495b2011-11-22 09:58:47 +0800420 u16 *pflags;
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700421
Annie Lib1e495b2011-11-22 09:58:47 +0800422 pflags = &gnttab_shared.v1[ref].flags;
423 nflags = *pflags;
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700424 do {
425 flags = nflags;
426 if (flags & (GTF_reading|GTF_writing)) {
427 printk(KERN_ALERT "WARNING: g.e. still in use!\n");
428 return 0;
429 }
Annie Lib1e495b2011-11-22 09:58:47 +0800430 } while ((nflags = sync_cmpxchg(pflags, flags, 0)) != flags);
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700431
432 return 1;
433}
Annie Li0f9f5a92011-11-22 09:58:06 +0800434
Annie Li85ff6ac2011-11-22 09:59:21 +0800435static int gnttab_end_foreign_access_ref_v2(grant_ref_t ref, int readonly)
436{
437 gnttab_shared.v2[ref].hdr.flags = 0;
438 mb();
439 if (grstatus[ref] & (GTF_reading|GTF_writing)) {
440 return 0;
441 } else {
442 /* The read of grstatus needs to have acquire
443 semantics. On x86, reads already have
444 that, and we just need to protect against
445 compiler reorderings. On other
446 architectures we may need a full
447 barrier. */
448#ifdef CONFIG_X86
449 barrier();
450#else
451 mb();
452#endif
453 }
454
455 return 1;
456}
457
Annie Li0f9f5a92011-11-22 09:58:06 +0800458int gnttab_end_foreign_access_ref(grant_ref_t ref, int readonly)
459{
460 return gnttab_interface->end_foreign_access_ref(ref, readonly);
461}
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700462EXPORT_SYMBOL_GPL(gnttab_end_foreign_access_ref);
463
464void gnttab_end_foreign_access(grant_ref_t ref, int readonly,
465 unsigned long page)
466{
467 if (gnttab_end_foreign_access_ref(ref, readonly)) {
468 put_free_entry(ref);
469 if (page != 0)
470 free_page(page);
471 } else {
472 /* XXX This needs to be fixed so that the ref and page are
473 placed on a list to be freed up later. */
474 printk(KERN_WARNING
475 "WARNING: leaking g.e. and page still in use!\n");
476 }
477}
478EXPORT_SYMBOL_GPL(gnttab_end_foreign_access);
479
480int gnttab_grant_foreign_transfer(domid_t domid, unsigned long pfn)
481{
482 int ref;
483
484 ref = get_free_entries(1);
485 if (unlikely(ref < 0))
486 return -ENOSPC;
487 gnttab_grant_foreign_transfer_ref(ref, domid, pfn);
488
489 return ref;
490}
491EXPORT_SYMBOL_GPL(gnttab_grant_foreign_transfer);
492
493void gnttab_grant_foreign_transfer_ref(grant_ref_t ref, domid_t domid,
494 unsigned long pfn)
495{
Annie Li0f9f5a92011-11-22 09:58:06 +0800496 gnttab_interface->update_entry(ref, domid, pfn, GTF_accept_transfer);
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700497}
498EXPORT_SYMBOL_GPL(gnttab_grant_foreign_transfer_ref);
499
Annie Li0f9f5a92011-11-22 09:58:06 +0800500static unsigned long gnttab_end_foreign_transfer_ref_v1(grant_ref_t ref)
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700501{
502 unsigned long frame;
503 u16 flags;
Annie Lib1e495b2011-11-22 09:58:47 +0800504 u16 *pflags;
505
506 pflags = &gnttab_shared.v1[ref].flags;
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700507
508 /*
509 * If a transfer is not even yet started, try to reclaim the grant
510 * reference and return failure (== 0).
511 */
Annie Lib1e495b2011-11-22 09:58:47 +0800512 while (!((flags = *pflags) & GTF_transfer_committed)) {
513 if (sync_cmpxchg(pflags, flags, 0) == flags)
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700514 return 0;
515 cpu_relax();
516 }
517
518 /* If a transfer is in progress then wait until it is completed. */
519 while (!(flags & GTF_transfer_completed)) {
Annie Lib1e495b2011-11-22 09:58:47 +0800520 flags = *pflags;
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700521 cpu_relax();
522 }
523
524 rmb(); /* Read the frame number /after/ reading completion status. */
Annie Li0f9f5a92011-11-22 09:58:06 +0800525 frame = gnttab_shared.v1[ref].frame;
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700526 BUG_ON(frame == 0);
527
528 return frame;
529}
Annie Li0f9f5a92011-11-22 09:58:06 +0800530
Annie Li85ff6ac2011-11-22 09:59:21 +0800531static unsigned long gnttab_end_foreign_transfer_ref_v2(grant_ref_t ref)
532{
533 unsigned long frame;
534 u16 flags;
535 u16 *pflags;
536
537 pflags = &gnttab_shared.v2[ref].hdr.flags;
538
539 /*
540 * If a transfer is not even yet started, try to reclaim the grant
541 * reference and return failure (== 0).
542 */
543 while (!((flags = *pflags) & GTF_transfer_committed)) {
544 if (sync_cmpxchg(pflags, flags, 0) == flags)
545 return 0;
546 cpu_relax();
547 }
548
549 /* If a transfer is in progress then wait until it is completed. */
550 while (!(flags & GTF_transfer_completed)) {
551 flags = *pflags;
552 cpu_relax();
553 }
554
555 rmb(); /* Read the frame number /after/ reading completion status. */
556 frame = gnttab_shared.v2[ref].full_page.frame;
557 BUG_ON(frame == 0);
558
559 return frame;
560}
561
Annie Li0f9f5a92011-11-22 09:58:06 +0800562unsigned long gnttab_end_foreign_transfer_ref(grant_ref_t ref)
563{
564 return gnttab_interface->end_foreign_transfer_ref(ref);
565}
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700566EXPORT_SYMBOL_GPL(gnttab_end_foreign_transfer_ref);
567
568unsigned long gnttab_end_foreign_transfer(grant_ref_t ref)
569{
570 unsigned long frame = gnttab_end_foreign_transfer_ref(ref);
571 put_free_entry(ref);
572 return frame;
573}
574EXPORT_SYMBOL_GPL(gnttab_end_foreign_transfer);
575
576void gnttab_free_grant_reference(grant_ref_t ref)
577{
578 put_free_entry(ref);
579}
580EXPORT_SYMBOL_GPL(gnttab_free_grant_reference);
581
582void gnttab_free_grant_references(grant_ref_t head)
583{
584 grant_ref_t ref;
585 unsigned long flags;
586 int count = 1;
587 if (head == GNTTAB_LIST_END)
588 return;
589 spin_lock_irqsave(&gnttab_list_lock, flags);
590 ref = head;
591 while (gnttab_entry(ref) != GNTTAB_LIST_END) {
592 ref = gnttab_entry(ref);
593 count++;
594 }
595 gnttab_entry(ref) = gnttab_free_head;
596 gnttab_free_head = head;
597 gnttab_free_count += count;
598 check_free_callbacks();
599 spin_unlock_irqrestore(&gnttab_list_lock, flags);
600}
601EXPORT_SYMBOL_GPL(gnttab_free_grant_references);
602
603int gnttab_alloc_grant_references(u16 count, grant_ref_t *head)
604{
605 int h = get_free_entries(count);
606
607 if (h < 0)
608 return -ENOSPC;
609
610 *head = h;
611
612 return 0;
613}
614EXPORT_SYMBOL_GPL(gnttab_alloc_grant_references);
615
616int gnttab_empty_grant_references(const grant_ref_t *private_head)
617{
618 return (*private_head == GNTTAB_LIST_END);
619}
620EXPORT_SYMBOL_GPL(gnttab_empty_grant_references);
621
622int gnttab_claim_grant_reference(grant_ref_t *private_head)
623{
624 grant_ref_t g = *private_head;
625 if (unlikely(g == GNTTAB_LIST_END))
626 return -ENOSPC;
627 *private_head = gnttab_entry(g);
628 return g;
629}
630EXPORT_SYMBOL_GPL(gnttab_claim_grant_reference);
631
632void gnttab_release_grant_reference(grant_ref_t *private_head,
633 grant_ref_t release)
634{
635 gnttab_entry(release) = *private_head;
636 *private_head = release;
637}
638EXPORT_SYMBOL_GPL(gnttab_release_grant_reference);
639
640void gnttab_request_free_callback(struct gnttab_free_callback *callback,
641 void (*fn)(void *), void *arg, u16 count)
642{
643 unsigned long flags;
644 spin_lock_irqsave(&gnttab_list_lock, flags);
645 if (callback->next)
646 goto out;
647 callback->fn = fn;
648 callback->arg = arg;
649 callback->count = count;
650 callback->next = gnttab_free_callback_list;
651 gnttab_free_callback_list = callback;
652 check_free_callbacks();
653out:
654 spin_unlock_irqrestore(&gnttab_list_lock, flags);
655}
656EXPORT_SYMBOL_GPL(gnttab_request_free_callback);
657
658void gnttab_cancel_free_callback(struct gnttab_free_callback *callback)
659{
660 struct gnttab_free_callback **pcb;
661 unsigned long flags;
662
663 spin_lock_irqsave(&gnttab_list_lock, flags);
664 for (pcb = &gnttab_free_callback_list; *pcb; pcb = &(*pcb)->next) {
665 if (*pcb == callback) {
666 *pcb = callback->next;
667 break;
668 }
669 }
670 spin_unlock_irqrestore(&gnttab_list_lock, flags);
671}
672EXPORT_SYMBOL_GPL(gnttab_cancel_free_callback);
673
674static int grow_gnttab_list(unsigned int more_frames)
675{
676 unsigned int new_nr_grant_frames, extra_entries, i;
Michael Abd-El-Malekbbc60c12008-04-04 02:33:48 -0700677 unsigned int nr_glist_frames, new_nr_glist_frames;
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700678
Matt Wilsonffe28822013-01-15 13:21:27 +0000679 BUG_ON(grefs_per_grant_frame == 0);
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700680
Matt Wilsonffe28822013-01-15 13:21:27 +0000681 new_nr_grant_frames = nr_grant_frames + more_frames;
682 extra_entries = more_frames * grefs_per_grant_frame;
683
684 nr_glist_frames = (nr_grant_frames * grefs_per_grant_frame + RPP - 1) / RPP;
Michael Abd-El-Malekbbc60c12008-04-04 02:33:48 -0700685 new_nr_glist_frames =
Matt Wilsonffe28822013-01-15 13:21:27 +0000686 (new_nr_grant_frames * grefs_per_grant_frame + RPP - 1) / RPP;
Michael Abd-El-Malekbbc60c12008-04-04 02:33:48 -0700687 for (i = nr_glist_frames; i < new_nr_glist_frames; i++) {
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700688 gnttab_list[i] = (grant_ref_t *)__get_free_page(GFP_ATOMIC);
689 if (!gnttab_list[i])
690 goto grow_nomem;
691 }
692
693
Matt Wilsonffe28822013-01-15 13:21:27 +0000694 for (i = grefs_per_grant_frame * nr_grant_frames;
695 i < grefs_per_grant_frame * new_nr_grant_frames - 1; i++)
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700696 gnttab_entry(i) = i + 1;
697
698 gnttab_entry(i) = gnttab_free_head;
Matt Wilsonffe28822013-01-15 13:21:27 +0000699 gnttab_free_head = grefs_per_grant_frame * nr_grant_frames;
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700700 gnttab_free_count += extra_entries;
701
702 nr_grant_frames = new_nr_grant_frames;
703
704 check_free_callbacks();
705
706 return 0;
707
708grow_nomem:
Michael Abd-El-Malekbbc60c12008-04-04 02:33:48 -0700709 for ( ; i >= nr_glist_frames; i--)
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700710 free_page((unsigned long) gnttab_list[i]);
711 return -ENOMEM;
712}
713
714static unsigned int __max_nr_grant_frames(void)
715{
716 struct gnttab_query_size query;
717 int rc;
718
719 query.dom = DOMID_SELF;
720
721 rc = HYPERVISOR_grant_table_op(GNTTABOP_query_size, &query, 1);
722 if ((rc < 0) || (query.status != GNTST_okay))
723 return 4; /* Legacy max supported number of frames */
724
725 return query.max_nr_frames;
726}
727
Stefano Stabellini183d03c2010-05-17 17:08:21 +0100728unsigned int gnttab_max_grant_frames(void)
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700729{
730 unsigned int xen_max = __max_nr_grant_frames();
731
732 if (xen_max > boot_max_nr_grant_frames)
733 return boot_max_nr_grant_frames;
734 return xen_max;
735}
Stefano Stabellini183d03c2010-05-17 17:08:21 +0100736EXPORT_SYMBOL_GPL(gnttab_max_grant_frames);
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700737
Stefano Stabellini289b7772010-12-10 14:54:44 +0000738int gnttab_map_refs(struct gnttab_map_grant_ref *map_ops,
Annie Lic1237992011-11-22 09:59:56 +0800739 struct gnttab_map_grant_ref *kmap_ops,
740 struct page **pages, unsigned int count)
Stefano Stabellini289b7772010-12-10 14:54:44 +0000741{
742 int i, ret;
743 pte_t *pte;
744 unsigned long mfn;
745
746 ret = HYPERVISOR_grant_table_op(GNTTABOP_map_grant_ref, map_ops, count);
Jeremy Fitzhardinge87f1d402010-12-13 14:42:30 +0000747 if (ret)
748 return ret;
Stefano Stabellini289b7772010-12-10 14:54:44 +0000749
Daniel De Graafaab8f112011-02-03 12:19:02 -0500750 if (xen_feature(XENFEAT_auto_translated_physmap))
751 return ret;
752
Stefano Stabellini289b7772010-12-10 14:54:44 +0000753 for (i = 0; i < count; i++) {
Ian Campbelldc4972a2011-03-04 17:38:21 +0000754 /* Do not add to override if the map failed. */
755 if (map_ops[i].status)
756 continue;
757
Konrad Rzeszutek Wilkcf8d9162011-02-28 17:58:48 -0500758 if (map_ops[i].flags & GNTMAP_contains_pte) {
759 pte = (pte_t *) (mfn_to_virt(PFN_DOWN(map_ops[i].host_addr)) +
Stefano Stabellini289b7772010-12-10 14:54:44 +0000760 (map_ops[i].host_addr & ~PAGE_MASK));
Konrad Rzeszutek Wilkcf8d9162011-02-28 17:58:48 -0500761 mfn = pte_mfn(*pte);
762 } else {
Daniel De Graaf7d17e842011-12-14 15:12:11 -0500763 mfn = PFN_DOWN(map_ops[i].dev_bus_addr);
Konrad Rzeszutek Wilkcf8d9162011-02-28 17:58:48 -0500764 }
Daniel De Graaf7d17e842011-12-14 15:12:11 -0500765 ret = m2p_add_override(mfn, pages[i], kmap_ops ?
766 &kmap_ops[i] : NULL);
Jeremy Fitzhardinge87f1d402010-12-13 14:42:30 +0000767 if (ret)
768 return ret;
Stefano Stabellini289b7772010-12-10 14:54:44 +0000769 }
770
771 return ret;
772}
773EXPORT_SYMBOL_GPL(gnttab_map_refs);
774
775int gnttab_unmap_refs(struct gnttab_unmap_grant_ref *unmap_ops,
Stefano Stabellini449efd12012-09-12 12:44:30 +0100776 struct gnttab_map_grant_ref *kmap_ops,
777 struct page **pages, unsigned int count)
Stefano Stabellini289b7772010-12-10 14:54:44 +0000778{
779 int i, ret;
780
781 ret = HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref, unmap_ops, count);
Jeremy Fitzhardinge87f1d402010-12-13 14:42:30 +0000782 if (ret)
783 return ret;
784
Daniel De Graafaab8f112011-02-03 12:19:02 -0500785 if (xen_feature(XENFEAT_auto_translated_physmap))
786 return ret;
787
Jeremy Fitzhardinge87f1d402010-12-13 14:42:30 +0000788 for (i = 0; i < count; i++) {
Stefano Stabellini449efd12012-09-12 12:44:30 +0100789 ret = m2p_remove_override(pages[i], kmap_ops ?
790 &kmap_ops[i] : NULL);
Jeremy Fitzhardinge87f1d402010-12-13 14:42:30 +0000791 if (ret)
792 return ret;
793 }
Stefano Stabellini289b7772010-12-10 14:54:44 +0000794
795 return ret;
796}
797EXPORT_SYMBOL_GPL(gnttab_unmap_refs);
798
Annie Li85ff6ac2011-11-22 09:59:21 +0800799static unsigned nr_status_frames(unsigned nr_grant_frames)
800{
Matt Wilsonffe28822013-01-15 13:21:27 +0000801 BUG_ON(grefs_per_grant_frame == 0);
802 return (nr_grant_frames * grefs_per_grant_frame + SPP - 1) / SPP;
Annie Li85ff6ac2011-11-22 09:59:21 +0800803}
804
Annie Li0f9f5a92011-11-22 09:58:06 +0800805static int gnttab_map_frames_v1(unsigned long *frames, unsigned int nr_gframes)
806{
807 int rc;
808
809 rc = arch_gnttab_map_shared(frames, nr_gframes,
810 gnttab_max_grant_frames(),
811 &gnttab_shared.addr);
812 BUG_ON(rc);
813
814 return 0;
815}
816
817static void gnttab_unmap_frames_v1(void)
818{
Annie Li85ff6ac2011-11-22 09:59:21 +0800819 arch_gnttab_unmap(gnttab_shared.addr, nr_grant_frames);
820}
821
822static int gnttab_map_frames_v2(unsigned long *frames, unsigned int nr_gframes)
823{
824 uint64_t *sframes;
825 unsigned int nr_sframes;
826 struct gnttab_get_status_frames getframes;
827 int rc;
828
829 nr_sframes = nr_status_frames(nr_gframes);
830
831 /* No need for kzalloc as it is initialized in following hypercall
832 * GNTTABOP_get_status_frames.
833 */
834 sframes = kmalloc(nr_sframes * sizeof(uint64_t), GFP_ATOMIC);
835 if (!sframes)
836 return -ENOMEM;
837
838 getframes.dom = DOMID_SELF;
839 getframes.nr_frames = nr_sframes;
840 set_xen_guest_handle(getframes.frame_list, sframes);
841
842 rc = HYPERVISOR_grant_table_op(GNTTABOP_get_status_frames,
843 &getframes, 1);
844 if (rc == -ENOSYS) {
845 kfree(sframes);
846 return -ENOSYS;
847 }
848
849 BUG_ON(rc || getframes.status);
850
851 rc = arch_gnttab_map_status(sframes, nr_sframes,
852 nr_status_frames(gnttab_max_grant_frames()),
853 &grstatus);
854 BUG_ON(rc);
855 kfree(sframes);
856
857 rc = arch_gnttab_map_shared(frames, nr_gframes,
858 gnttab_max_grant_frames(),
859 &gnttab_shared.addr);
860 BUG_ON(rc);
861
862 return 0;
863}
864
865static void gnttab_unmap_frames_v2(void)
866{
867 arch_gnttab_unmap(gnttab_shared.addr, nr_grant_frames);
868 arch_gnttab_unmap(grstatus, nr_status_frames(nr_grant_frames));
Annie Li0f9f5a92011-11-22 09:58:06 +0800869}
870
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700871static int gnttab_map(unsigned int start_idx, unsigned int end_idx)
872{
873 struct gnttab_setup_table setup;
874 unsigned long *frames;
875 unsigned int nr_gframes = end_idx + 1;
876 int rc;
877
Stefano Stabellini183d03c2010-05-17 17:08:21 +0100878 if (xen_hvm_domain()) {
879 struct xen_add_to_physmap xatp;
880 unsigned int i = end_idx;
881 rc = 0;
882 /*
883 * Loop backwards, so that the first hypercall has the largest
884 * index, ensuring that the table will grow only once.
885 */
886 do {
887 xatp.domid = DOMID_SELF;
888 xatp.idx = i;
889 xatp.space = XENMAPSPACE_grant_table;
890 xatp.gpfn = (xen_hvm_resume_frames >> PAGE_SHIFT) + i;
891 rc = HYPERVISOR_memory_op(XENMEM_add_to_physmap, &xatp);
892 if (rc != 0) {
893 printk(KERN_WARNING
894 "grant table add_to_physmap failed, err=%d\n", rc);
895 break;
896 }
897 } while (i-- > start_idx);
898
899 return rc;
900 }
901
Annie Li85ff6ac2011-11-22 09:59:21 +0800902 /* No need for kzalloc as it is initialized in following hypercall
903 * GNTTABOP_setup_table.
904 */
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700905 frames = kmalloc(nr_gframes * sizeof(unsigned long), GFP_ATOMIC);
906 if (!frames)
907 return -ENOMEM;
908
909 setup.dom = DOMID_SELF;
910 setup.nr_frames = nr_gframes;
Isaku Yamahata87e27cf2008-04-02 10:53:52 -0700911 set_xen_guest_handle(setup.frame_list, frames);
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700912
913 rc = HYPERVISOR_grant_table_op(GNTTABOP_setup_table, &setup, 1);
914 if (rc == -ENOSYS) {
915 kfree(frames);
916 return -ENOSYS;
917 }
918
919 BUG_ON(rc || setup.status);
920
Annie Li0f9f5a92011-11-22 09:58:06 +0800921 rc = gnttab_interface->map_frames(frames, nr_gframes);
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700922
923 kfree(frames);
924
Annie Li0f9f5a92011-11-22 09:58:06 +0800925 return rc;
926}
927
928static struct gnttab_ops gnttab_v1_ops = {
929 .map_frames = gnttab_map_frames_v1,
930 .unmap_frames = gnttab_unmap_frames_v1,
931 .update_entry = gnttab_update_entry_v1,
932 .end_foreign_access_ref = gnttab_end_foreign_access_ref_v1,
933 .end_foreign_transfer_ref = gnttab_end_foreign_transfer_ref_v1,
934 .query_foreign_access = gnttab_query_foreign_access_v1,
935};
936
Annie Li85ff6ac2011-11-22 09:59:21 +0800937static struct gnttab_ops gnttab_v2_ops = {
938 .map_frames = gnttab_map_frames_v2,
939 .unmap_frames = gnttab_unmap_frames_v2,
940 .update_entry = gnttab_update_entry_v2,
941 .end_foreign_access_ref = gnttab_end_foreign_access_ref_v2,
942 .end_foreign_transfer_ref = gnttab_end_foreign_transfer_ref_v2,
943 .query_foreign_access = gnttab_query_foreign_access_v2,
Annie Li66667542011-12-12 18:14:42 +0800944 .update_subpage_entry = gnttab_update_subpage_entry_v2,
Annie Li9438ce92011-12-12 18:15:07 +0800945 .update_trans_entry = gnttab_update_trans_entry_v2,
Annie Li85ff6ac2011-11-22 09:59:21 +0800946};
947
Annie Li0f9f5a92011-11-22 09:58:06 +0800948static void gnttab_request_version(void)
949{
Annie Li85ff6ac2011-11-22 09:59:21 +0800950 int rc;
951 struct gnttab_set_version gsv;
952
Konrad Rzeszutek Wilk69e8f432012-01-25 00:13:20 -0500953 if (xen_hvm_domain())
954 gsv.version = 1;
955 else
956 gsv.version = 2;
Annie Li85ff6ac2011-11-22 09:59:21 +0800957 rc = HYPERVISOR_grant_table_op(GNTTABOP_set_version, &gsv, 1);
Konrad Rzeszutek Wilk69e8f432012-01-25 00:13:20 -0500958 if (rc == 0 && gsv.version == 2) {
Annie Li85ff6ac2011-11-22 09:59:21 +0800959 grant_table_version = 2;
Matt Wilsonffe28822013-01-15 13:21:27 +0000960 grefs_per_grant_frame = PAGE_SIZE / sizeof(union grant_entry_v2);
Annie Li85ff6ac2011-11-22 09:59:21 +0800961 gnttab_interface = &gnttab_v2_ops;
962 } else if (grant_table_version == 2) {
963 /*
964 * If we've already used version 2 features,
965 * but then suddenly discover that they're not
966 * available (e.g. migrating to an older
967 * version of Xen), almost unbounded badness
968 * can happen.
969 */
970 panic("we need grant tables version 2, but only version 1 is available");
971 } else {
972 grant_table_version = 1;
Matt Wilsonffe28822013-01-15 13:21:27 +0000973 grefs_per_grant_frame = PAGE_SIZE / sizeof(struct grant_entry_v1);
Annie Li85ff6ac2011-11-22 09:59:21 +0800974 gnttab_interface = &gnttab_v1_ops;
975 }
Annie Li0f9f5a92011-11-22 09:58:06 +0800976 printk(KERN_INFO "Grant tables using version %d layout.\n",
977 grant_table_version);
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700978}
979
Matt Wilsonffe28822013-01-15 13:21:27 +0000980static int gnttab_setup(void)
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700981{
Stefano Stabellini183d03c2010-05-17 17:08:21 +0100982 unsigned int max_nr_gframes;
983
984 max_nr_gframes = gnttab_max_grant_frames();
985 if (max_nr_gframes < nr_grant_frames)
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700986 return -ENOSYS;
Stefano Stabellini183d03c2010-05-17 17:08:21 +0100987
988 if (xen_pv_domain())
989 return gnttab_map(0, nr_grant_frames - 1);
990
Annie Li0f9f5a92011-11-22 09:58:06 +0800991 if (gnttab_shared.addr == NULL) {
992 gnttab_shared.addr = ioremap(xen_hvm_resume_frames,
993 PAGE_SIZE * max_nr_gframes);
994 if (gnttab_shared.addr == NULL) {
Stefano Stabellini183d03c2010-05-17 17:08:21 +0100995 printk(KERN_WARNING
996 "Failed to ioremap gnttab share frames!");
997 return -ENOMEM;
998 }
999 }
1000
1001 gnttab_map(0, nr_grant_frames - 1);
1002
1003 return 0;
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -07001004}
1005
Matt Wilsonffe28822013-01-15 13:21:27 +00001006int gnttab_resume(void)
1007{
1008 gnttab_request_version();
1009 return gnttab_setup();
1010}
1011
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +01001012int gnttab_suspend(void)
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -07001013{
Annie Li0f9f5a92011-11-22 09:58:06 +08001014 gnttab_interface->unmap_frames();
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -07001015 return 0;
1016}
1017
1018static int gnttab_expand(unsigned int req_entries)
1019{
1020 int rc;
1021 unsigned int cur, extra;
1022
Matt Wilsonffe28822013-01-15 13:21:27 +00001023 BUG_ON(grefs_per_grant_frame == 0);
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -07001024 cur = nr_grant_frames;
Matt Wilsonffe28822013-01-15 13:21:27 +00001025 extra = ((req_entries + (grefs_per_grant_frame-1)) /
1026 grefs_per_grant_frame);
Stefano Stabellini183d03c2010-05-17 17:08:21 +01001027 if (cur + extra > gnttab_max_grant_frames())
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -07001028 return -ENOSPC;
1029
1030 rc = gnttab_map(cur, cur + extra - 1);
1031 if (rc == 0)
1032 rc = grow_gnttab_list(extra);
1033
1034 return rc;
1035}
1036
Stefano Stabellini183d03c2010-05-17 17:08:21 +01001037int gnttab_init(void)
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -07001038{
1039 int i;
Michael Abd-El-Malekbbc60c12008-04-04 02:33:48 -07001040 unsigned int max_nr_glist_frames, nr_glist_frames;
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -07001041 unsigned int nr_init_grefs;
Julia Lawall6b5e7d92012-04-15 11:27:12 +02001042 int ret;
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -07001043
Matt Wilsonffe28822013-01-15 13:21:27 +00001044 gnttab_request_version();
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -07001045 nr_grant_frames = 1;
1046 boot_max_nr_grant_frames = __max_nr_grant_frames();
1047
1048 /* Determine the maximum number of frames required for the
1049 * grant reference free list on the current hypervisor.
1050 */
Matt Wilsonffe28822013-01-15 13:21:27 +00001051 BUG_ON(grefs_per_grant_frame == 0);
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -07001052 max_nr_glist_frames = (boot_max_nr_grant_frames *
Matt Wilsonffe28822013-01-15 13:21:27 +00001053 grefs_per_grant_frame / RPP);
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -07001054
1055 gnttab_list = kmalloc(max_nr_glist_frames * sizeof(grant_ref_t *),
1056 GFP_KERNEL);
1057 if (gnttab_list == NULL)
1058 return -ENOMEM;
1059
Matt Wilsonffe28822013-01-15 13:21:27 +00001060 nr_glist_frames = (nr_grant_frames * grefs_per_grant_frame + RPP - 1) / RPP;
Michael Abd-El-Malekbbc60c12008-04-04 02:33:48 -07001061 for (i = 0; i < nr_glist_frames; i++) {
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -07001062 gnttab_list[i] = (grant_ref_t *)__get_free_page(GFP_KERNEL);
Julia Lawall6b5e7d92012-04-15 11:27:12 +02001063 if (gnttab_list[i] == NULL) {
1064 ret = -ENOMEM;
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -07001065 goto ini_nomem;
Julia Lawall6b5e7d92012-04-15 11:27:12 +02001066 }
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -07001067 }
1068
Matt Wilsonffe28822013-01-15 13:21:27 +00001069 if (gnttab_setup() < 0) {
Julia Lawall6b5e7d92012-04-15 11:27:12 +02001070 ret = -ENODEV;
1071 goto ini_nomem;
1072 }
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -07001073
Matt Wilsonffe28822013-01-15 13:21:27 +00001074 nr_init_grefs = nr_grant_frames * grefs_per_grant_frame;
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -07001075
1076 for (i = NR_RESERVED_ENTRIES; i < nr_init_grefs - 1; i++)
1077 gnttab_entry(i) = i + 1;
1078
1079 gnttab_entry(nr_init_grefs - 1) = GNTTAB_LIST_END;
1080 gnttab_free_count = nr_init_grefs - NR_RESERVED_ENTRIES;
1081 gnttab_free_head = NR_RESERVED_ENTRIES;
1082
1083 printk("Grant table initialized\n");
1084 return 0;
1085
1086 ini_nomem:
1087 for (i--; i >= 0; i--)
1088 free_page((unsigned long)gnttab_list[i]);
1089 kfree(gnttab_list);
Julia Lawall6b5e7d92012-04-15 11:27:12 +02001090 return ret;
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -07001091}
Stefano Stabellini183d03c2010-05-17 17:08:21 +01001092EXPORT_SYMBOL_GPL(gnttab_init);
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -07001093
Stefano Stabellini183d03c2010-05-17 17:08:21 +01001094static int __devinit __gnttab_init(void)
1095{
1096 /* Delay grant-table initialization in the PV on HVM case */
1097 if (xen_hvm_domain())
1098 return 0;
1099
1100 if (!xen_pv_domain())
1101 return -ENODEV;
1102
1103 return gnttab_init();
1104}
1105
1106core_initcall(__gnttab_init);