blob: 3275bde6ad75340d6773b8b2c955e510afa5c12d [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 Wilson319205f2013-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;
Roger Pau Monne9b7e1572013-07-31 17:00:42 +0200644 struct gnttab_free_callback *cb;
645
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700646 spin_lock_irqsave(&gnttab_list_lock, flags);
Roger Pau Monne9b7e1572013-07-31 17:00:42 +0200647
648 /* Check if the callback is already on the list */
649 cb = gnttab_free_callback_list;
650 while (cb) {
651 if (cb == callback)
652 goto out;
653 cb = cb->next;
654 }
655
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700656 callback->fn = fn;
657 callback->arg = arg;
658 callback->count = count;
659 callback->next = gnttab_free_callback_list;
660 gnttab_free_callback_list = callback;
661 check_free_callbacks();
662out:
663 spin_unlock_irqrestore(&gnttab_list_lock, flags);
664}
665EXPORT_SYMBOL_GPL(gnttab_request_free_callback);
666
667void gnttab_cancel_free_callback(struct gnttab_free_callback *callback)
668{
669 struct gnttab_free_callback **pcb;
670 unsigned long flags;
671
672 spin_lock_irqsave(&gnttab_list_lock, flags);
673 for (pcb = &gnttab_free_callback_list; *pcb; pcb = &(*pcb)->next) {
674 if (*pcb == callback) {
675 *pcb = callback->next;
676 break;
677 }
678 }
679 spin_unlock_irqrestore(&gnttab_list_lock, flags);
680}
681EXPORT_SYMBOL_GPL(gnttab_cancel_free_callback);
682
683static int grow_gnttab_list(unsigned int more_frames)
684{
685 unsigned int new_nr_grant_frames, extra_entries, i;
Michael Abd-El-Malekbbc60c12008-04-04 02:33:48 -0700686 unsigned int nr_glist_frames, new_nr_glist_frames;
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700687
Matt Wilson319205f2013-01-15 13:21:27 +0000688 BUG_ON(grefs_per_grant_frame == 0);
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700689
Matt Wilson319205f2013-01-15 13:21:27 +0000690 new_nr_grant_frames = nr_grant_frames + more_frames;
691 extra_entries = more_frames * grefs_per_grant_frame;
692
693 nr_glist_frames = (nr_grant_frames * grefs_per_grant_frame + RPP - 1) / RPP;
Michael Abd-El-Malekbbc60c12008-04-04 02:33:48 -0700694 new_nr_glist_frames =
Matt Wilson319205f2013-01-15 13:21:27 +0000695 (new_nr_grant_frames * grefs_per_grant_frame + RPP - 1) / RPP;
Michael Abd-El-Malekbbc60c12008-04-04 02:33:48 -0700696 for (i = nr_glist_frames; i < new_nr_glist_frames; i++) {
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700697 gnttab_list[i] = (grant_ref_t *)__get_free_page(GFP_ATOMIC);
698 if (!gnttab_list[i])
699 goto grow_nomem;
700 }
701
702
Matt Wilson319205f2013-01-15 13:21:27 +0000703 for (i = grefs_per_grant_frame * nr_grant_frames;
704 i < grefs_per_grant_frame * new_nr_grant_frames - 1; i++)
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700705 gnttab_entry(i) = i + 1;
706
707 gnttab_entry(i) = gnttab_free_head;
Matt Wilson319205f2013-01-15 13:21:27 +0000708 gnttab_free_head = grefs_per_grant_frame * nr_grant_frames;
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700709 gnttab_free_count += extra_entries;
710
711 nr_grant_frames = new_nr_grant_frames;
712
713 check_free_callbacks();
714
715 return 0;
716
717grow_nomem:
Michael Abd-El-Malekbbc60c12008-04-04 02:33:48 -0700718 for ( ; i >= nr_glist_frames; i--)
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700719 free_page((unsigned long) gnttab_list[i]);
720 return -ENOMEM;
721}
722
723static unsigned int __max_nr_grant_frames(void)
724{
725 struct gnttab_query_size query;
726 int rc;
727
728 query.dom = DOMID_SELF;
729
730 rc = HYPERVISOR_grant_table_op(GNTTABOP_query_size, &query, 1);
731 if ((rc < 0) || (query.status != GNTST_okay))
732 return 4; /* Legacy max supported number of frames */
733
734 return query.max_nr_frames;
735}
736
Stefano Stabellini183d03c2010-05-17 17:08:21 +0100737unsigned int gnttab_max_grant_frames(void)
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700738{
739 unsigned int xen_max = __max_nr_grant_frames();
740
741 if (xen_max > boot_max_nr_grant_frames)
742 return boot_max_nr_grant_frames;
743 return xen_max;
744}
Stefano Stabellini183d03c2010-05-17 17:08:21 +0100745EXPORT_SYMBOL_GPL(gnttab_max_grant_frames);
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700746
Stefano Stabellini289b7772010-12-10 14:54:44 +0000747int gnttab_map_refs(struct gnttab_map_grant_ref *map_ops,
Annie Lic1237992011-11-22 09:59:56 +0800748 struct gnttab_map_grant_ref *kmap_ops,
749 struct page **pages, unsigned int count)
Stefano Stabellini289b7772010-12-10 14:54:44 +0000750{
751 int i, ret;
752 pte_t *pte;
753 unsigned long mfn;
754
755 ret = HYPERVISOR_grant_table_op(GNTTABOP_map_grant_ref, map_ops, count);
Jeremy Fitzhardinge87f1d402010-12-13 14:42:30 +0000756 if (ret)
757 return ret;
Stefano Stabellini289b7772010-12-10 14:54:44 +0000758
Daniel De Graafaab8f112011-02-03 12:19:02 -0500759 if (xen_feature(XENFEAT_auto_translated_physmap))
760 return ret;
761
Stefano Stabellini289b7772010-12-10 14:54:44 +0000762 for (i = 0; i < count; i++) {
Ian Campbelldc4972a2011-03-04 17:38:21 +0000763 /* Do not add to override if the map failed. */
764 if (map_ops[i].status)
765 continue;
766
Konrad Rzeszutek Wilkcf8d9162011-02-28 17:58:48 -0500767 if (map_ops[i].flags & GNTMAP_contains_pte) {
768 pte = (pte_t *) (mfn_to_virt(PFN_DOWN(map_ops[i].host_addr)) +
Stefano Stabellini289b7772010-12-10 14:54:44 +0000769 (map_ops[i].host_addr & ~PAGE_MASK));
Konrad Rzeszutek Wilkcf8d9162011-02-28 17:58:48 -0500770 mfn = pte_mfn(*pte);
771 } else {
Daniel De Graaf7d17e842011-12-14 15:12:11 -0500772 mfn = PFN_DOWN(map_ops[i].dev_bus_addr);
Konrad Rzeszutek Wilkcf8d9162011-02-28 17:58:48 -0500773 }
Daniel De Graaf7d17e842011-12-14 15:12:11 -0500774 ret = m2p_add_override(mfn, pages[i], kmap_ops ?
775 &kmap_ops[i] : NULL);
Jeremy Fitzhardinge87f1d402010-12-13 14:42:30 +0000776 if (ret)
777 return ret;
Stefano Stabellini289b7772010-12-10 14:54:44 +0000778 }
779
780 return ret;
781}
782EXPORT_SYMBOL_GPL(gnttab_map_refs);
783
784int gnttab_unmap_refs(struct gnttab_unmap_grant_ref *unmap_ops,
Stefano Stabellini2a73d182012-09-12 12:44:30 +0100785 struct gnttab_map_grant_ref *kmap_ops,
786 struct page **pages, unsigned int count)
Stefano Stabellini289b7772010-12-10 14:54:44 +0000787{
788 int i, ret;
789
790 ret = HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref, unmap_ops, count);
Jeremy Fitzhardinge87f1d402010-12-13 14:42:30 +0000791 if (ret)
792 return ret;
793
Daniel De Graafaab8f112011-02-03 12:19:02 -0500794 if (xen_feature(XENFEAT_auto_translated_physmap))
795 return ret;
796
Jeremy Fitzhardinge87f1d402010-12-13 14:42:30 +0000797 for (i = 0; i < count; i++) {
Stefano Stabellini2a73d182012-09-12 12:44:30 +0100798 ret = m2p_remove_override(pages[i], kmap_ops ?
799 &kmap_ops[i] : NULL);
Jeremy Fitzhardinge87f1d402010-12-13 14:42:30 +0000800 if (ret)
801 return ret;
802 }
Stefano Stabellini289b7772010-12-10 14:54:44 +0000803
804 return ret;
805}
806EXPORT_SYMBOL_GPL(gnttab_unmap_refs);
807
Annie Li85ff6ac2011-11-22 09:59:21 +0800808static unsigned nr_status_frames(unsigned nr_grant_frames)
809{
Matt Wilson319205f2013-01-15 13:21:27 +0000810 BUG_ON(grefs_per_grant_frame == 0);
811 return (nr_grant_frames * grefs_per_grant_frame + SPP - 1) / SPP;
Annie Li85ff6ac2011-11-22 09:59:21 +0800812}
813
Annie Li0f9f5a92011-11-22 09:58:06 +0800814static int gnttab_map_frames_v1(unsigned long *frames, unsigned int nr_gframes)
815{
816 int rc;
817
818 rc = arch_gnttab_map_shared(frames, nr_gframes,
819 gnttab_max_grant_frames(),
820 &gnttab_shared.addr);
821 BUG_ON(rc);
822
823 return 0;
824}
825
826static void gnttab_unmap_frames_v1(void)
827{
Annie Li85ff6ac2011-11-22 09:59:21 +0800828 arch_gnttab_unmap(gnttab_shared.addr, nr_grant_frames);
829}
830
831static int gnttab_map_frames_v2(unsigned long *frames, unsigned int nr_gframes)
832{
833 uint64_t *sframes;
834 unsigned int nr_sframes;
835 struct gnttab_get_status_frames getframes;
836 int rc;
837
838 nr_sframes = nr_status_frames(nr_gframes);
839
840 /* No need for kzalloc as it is initialized in following hypercall
841 * GNTTABOP_get_status_frames.
842 */
843 sframes = kmalloc(nr_sframes * sizeof(uint64_t), GFP_ATOMIC);
844 if (!sframes)
845 return -ENOMEM;
846
847 getframes.dom = DOMID_SELF;
848 getframes.nr_frames = nr_sframes;
849 set_xen_guest_handle(getframes.frame_list, sframes);
850
851 rc = HYPERVISOR_grant_table_op(GNTTABOP_get_status_frames,
852 &getframes, 1);
853 if (rc == -ENOSYS) {
854 kfree(sframes);
855 return -ENOSYS;
856 }
857
858 BUG_ON(rc || getframes.status);
859
860 rc = arch_gnttab_map_status(sframes, nr_sframes,
861 nr_status_frames(gnttab_max_grant_frames()),
862 &grstatus);
863 BUG_ON(rc);
864 kfree(sframes);
865
866 rc = arch_gnttab_map_shared(frames, nr_gframes,
867 gnttab_max_grant_frames(),
868 &gnttab_shared.addr);
869 BUG_ON(rc);
870
871 return 0;
872}
873
874static void gnttab_unmap_frames_v2(void)
875{
876 arch_gnttab_unmap(gnttab_shared.addr, nr_grant_frames);
877 arch_gnttab_unmap(grstatus, nr_status_frames(nr_grant_frames));
Annie Li0f9f5a92011-11-22 09:58:06 +0800878}
879
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700880static int gnttab_map(unsigned int start_idx, unsigned int end_idx)
881{
882 struct gnttab_setup_table setup;
883 unsigned long *frames;
884 unsigned int nr_gframes = end_idx + 1;
885 int rc;
886
Stefano Stabellini183d03c2010-05-17 17:08:21 +0100887 if (xen_hvm_domain()) {
888 struct xen_add_to_physmap xatp;
889 unsigned int i = end_idx;
890 rc = 0;
891 /*
892 * Loop backwards, so that the first hypercall has the largest
893 * index, ensuring that the table will grow only once.
894 */
895 do {
896 xatp.domid = DOMID_SELF;
897 xatp.idx = i;
898 xatp.space = XENMAPSPACE_grant_table;
899 xatp.gpfn = (xen_hvm_resume_frames >> PAGE_SHIFT) + i;
900 rc = HYPERVISOR_memory_op(XENMEM_add_to_physmap, &xatp);
901 if (rc != 0) {
902 printk(KERN_WARNING
903 "grant table add_to_physmap failed, err=%d\n", rc);
904 break;
905 }
906 } while (i-- > start_idx);
907
908 return rc;
909 }
910
Annie Li85ff6ac2011-11-22 09:59:21 +0800911 /* No need for kzalloc as it is initialized in following hypercall
912 * GNTTABOP_setup_table.
913 */
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700914 frames = kmalloc(nr_gframes * sizeof(unsigned long), GFP_ATOMIC);
915 if (!frames)
916 return -ENOMEM;
917
918 setup.dom = DOMID_SELF;
919 setup.nr_frames = nr_gframes;
Isaku Yamahata87e27cf2008-04-02 10:53:52 -0700920 set_xen_guest_handle(setup.frame_list, frames);
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700921
922 rc = HYPERVISOR_grant_table_op(GNTTABOP_setup_table, &setup, 1);
923 if (rc == -ENOSYS) {
924 kfree(frames);
925 return -ENOSYS;
926 }
927
928 BUG_ON(rc || setup.status);
929
Annie Li0f9f5a92011-11-22 09:58:06 +0800930 rc = gnttab_interface->map_frames(frames, nr_gframes);
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700931
932 kfree(frames);
933
Annie Li0f9f5a92011-11-22 09:58:06 +0800934 return rc;
935}
936
937static struct gnttab_ops gnttab_v1_ops = {
938 .map_frames = gnttab_map_frames_v1,
939 .unmap_frames = gnttab_unmap_frames_v1,
940 .update_entry = gnttab_update_entry_v1,
941 .end_foreign_access_ref = gnttab_end_foreign_access_ref_v1,
942 .end_foreign_transfer_ref = gnttab_end_foreign_transfer_ref_v1,
943 .query_foreign_access = gnttab_query_foreign_access_v1,
944};
945
Annie Li85ff6ac2011-11-22 09:59:21 +0800946static struct gnttab_ops gnttab_v2_ops = {
947 .map_frames = gnttab_map_frames_v2,
948 .unmap_frames = gnttab_unmap_frames_v2,
949 .update_entry = gnttab_update_entry_v2,
950 .end_foreign_access_ref = gnttab_end_foreign_access_ref_v2,
951 .end_foreign_transfer_ref = gnttab_end_foreign_transfer_ref_v2,
952 .query_foreign_access = gnttab_query_foreign_access_v2,
Annie Li66667542011-12-12 18:14:42 +0800953 .update_subpage_entry = gnttab_update_subpage_entry_v2,
Annie Li9438ce92011-12-12 18:15:07 +0800954 .update_trans_entry = gnttab_update_trans_entry_v2,
Annie Li85ff6ac2011-11-22 09:59:21 +0800955};
956
Annie Li0f9f5a92011-11-22 09:58:06 +0800957static void gnttab_request_version(void)
958{
Annie Li85ff6ac2011-11-22 09:59:21 +0800959 int rc;
960 struct gnttab_set_version gsv;
961
Konrad Rzeszutek Wilk69e8f432012-01-25 00:13:20 -0500962 if (xen_hvm_domain())
963 gsv.version = 1;
964 else
965 gsv.version = 2;
Annie Li85ff6ac2011-11-22 09:59:21 +0800966 rc = HYPERVISOR_grant_table_op(GNTTABOP_set_version, &gsv, 1);
Konrad Rzeszutek Wilk69e8f432012-01-25 00:13:20 -0500967 if (rc == 0 && gsv.version == 2) {
Annie Li85ff6ac2011-11-22 09:59:21 +0800968 grant_table_version = 2;
Matt Wilson319205f2013-01-15 13:21:27 +0000969 grefs_per_grant_frame = PAGE_SIZE / sizeof(union grant_entry_v2);
Annie Li85ff6ac2011-11-22 09:59:21 +0800970 gnttab_interface = &gnttab_v2_ops;
971 } else if (grant_table_version == 2) {
972 /*
973 * If we've already used version 2 features,
974 * but then suddenly discover that they're not
975 * available (e.g. migrating to an older
976 * version of Xen), almost unbounded badness
977 * can happen.
978 */
979 panic("we need grant tables version 2, but only version 1 is available");
980 } else {
981 grant_table_version = 1;
Matt Wilson319205f2013-01-15 13:21:27 +0000982 grefs_per_grant_frame = PAGE_SIZE / sizeof(struct grant_entry_v1);
Annie Li85ff6ac2011-11-22 09:59:21 +0800983 gnttab_interface = &gnttab_v1_ops;
984 }
Annie Li0f9f5a92011-11-22 09:58:06 +0800985 printk(KERN_INFO "Grant tables using version %d layout.\n",
986 grant_table_version);
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700987}
988
Matt Wilson319205f2013-01-15 13:21:27 +0000989static int gnttab_setup(void)
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700990{
Stefano Stabellini183d03c2010-05-17 17:08:21 +0100991 unsigned int max_nr_gframes;
992
993 max_nr_gframes = gnttab_max_grant_frames();
994 if (max_nr_gframes < nr_grant_frames)
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700995 return -ENOSYS;
Stefano Stabellini183d03c2010-05-17 17:08:21 +0100996
997 if (xen_pv_domain())
998 return gnttab_map(0, nr_grant_frames - 1);
999
Annie Li0f9f5a92011-11-22 09:58:06 +08001000 if (gnttab_shared.addr == NULL) {
1001 gnttab_shared.addr = ioremap(xen_hvm_resume_frames,
1002 PAGE_SIZE * max_nr_gframes);
1003 if (gnttab_shared.addr == NULL) {
Stefano Stabellini183d03c2010-05-17 17:08:21 +01001004 printk(KERN_WARNING
1005 "Failed to ioremap gnttab share frames!");
1006 return -ENOMEM;
1007 }
1008 }
1009
1010 gnttab_map(0, nr_grant_frames - 1);
1011
1012 return 0;
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -07001013}
1014
Matt Wilson319205f2013-01-15 13:21:27 +00001015int gnttab_resume(void)
1016{
1017 gnttab_request_version();
1018 return gnttab_setup();
1019}
1020
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +01001021int gnttab_suspend(void)
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -07001022{
Annie Li0f9f5a92011-11-22 09:58:06 +08001023 gnttab_interface->unmap_frames();
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -07001024 return 0;
1025}
1026
1027static int gnttab_expand(unsigned int req_entries)
1028{
1029 int rc;
1030 unsigned int cur, extra;
1031
Matt Wilson319205f2013-01-15 13:21:27 +00001032 BUG_ON(grefs_per_grant_frame == 0);
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -07001033 cur = nr_grant_frames;
Matt Wilson319205f2013-01-15 13:21:27 +00001034 extra = ((req_entries + (grefs_per_grant_frame-1)) /
1035 grefs_per_grant_frame);
Stefano Stabellini183d03c2010-05-17 17:08:21 +01001036 if (cur + extra > gnttab_max_grant_frames())
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -07001037 return -ENOSPC;
1038
1039 rc = gnttab_map(cur, cur + extra - 1);
1040 if (rc == 0)
1041 rc = grow_gnttab_list(extra);
1042
1043 return rc;
1044}
1045
Stefano Stabellini183d03c2010-05-17 17:08:21 +01001046int gnttab_init(void)
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -07001047{
1048 int i;
Michael Abd-El-Malekbbc60c12008-04-04 02:33:48 -07001049 unsigned int max_nr_glist_frames, nr_glist_frames;
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -07001050 unsigned int nr_init_grefs;
Julia Lawall6b5e7d92012-04-15 11:27:12 +02001051 int ret;
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -07001052
Matt Wilson319205f2013-01-15 13:21:27 +00001053 gnttab_request_version();
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -07001054 nr_grant_frames = 1;
1055 boot_max_nr_grant_frames = __max_nr_grant_frames();
1056
1057 /* Determine the maximum number of frames required for the
1058 * grant reference free list on the current hypervisor.
1059 */
Matt Wilson319205f2013-01-15 13:21:27 +00001060 BUG_ON(grefs_per_grant_frame == 0);
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -07001061 max_nr_glist_frames = (boot_max_nr_grant_frames *
Matt Wilson319205f2013-01-15 13:21:27 +00001062 grefs_per_grant_frame / RPP);
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -07001063
1064 gnttab_list = kmalloc(max_nr_glist_frames * sizeof(grant_ref_t *),
1065 GFP_KERNEL);
1066 if (gnttab_list == NULL)
1067 return -ENOMEM;
1068
Matt Wilson319205f2013-01-15 13:21:27 +00001069 nr_glist_frames = (nr_grant_frames * grefs_per_grant_frame + RPP - 1) / RPP;
Michael Abd-El-Malekbbc60c12008-04-04 02:33:48 -07001070 for (i = 0; i < nr_glist_frames; i++) {
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -07001071 gnttab_list[i] = (grant_ref_t *)__get_free_page(GFP_KERNEL);
Julia Lawall6b5e7d92012-04-15 11:27:12 +02001072 if (gnttab_list[i] == NULL) {
1073 ret = -ENOMEM;
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -07001074 goto ini_nomem;
Julia Lawall6b5e7d92012-04-15 11:27:12 +02001075 }
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -07001076 }
1077
Matt Wilson319205f2013-01-15 13:21:27 +00001078 if (gnttab_setup() < 0) {
Julia Lawall6b5e7d92012-04-15 11:27:12 +02001079 ret = -ENODEV;
1080 goto ini_nomem;
1081 }
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -07001082
Matt Wilson319205f2013-01-15 13:21:27 +00001083 nr_init_grefs = nr_grant_frames * grefs_per_grant_frame;
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -07001084
1085 for (i = NR_RESERVED_ENTRIES; i < nr_init_grefs - 1; i++)
1086 gnttab_entry(i) = i + 1;
1087
1088 gnttab_entry(nr_init_grefs - 1) = GNTTAB_LIST_END;
1089 gnttab_free_count = nr_init_grefs - NR_RESERVED_ENTRIES;
1090 gnttab_free_head = NR_RESERVED_ENTRIES;
1091
1092 printk("Grant table initialized\n");
1093 return 0;
1094
1095 ini_nomem:
1096 for (i--; i >= 0; i--)
1097 free_page((unsigned long)gnttab_list[i]);
1098 kfree(gnttab_list);
Julia Lawall6b5e7d92012-04-15 11:27:12 +02001099 return ret;
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -07001100}
Stefano Stabellini183d03c2010-05-17 17:08:21 +01001101EXPORT_SYMBOL_GPL(gnttab_init);
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -07001102
Stefano Stabellini183d03c2010-05-17 17:08:21 +01001103static int __devinit __gnttab_init(void)
1104{
1105 /* Delay grant-table initialization in the PV on HVM case */
1106 if (xen_hvm_domain())
1107 return 0;
1108
1109 if (!xen_pv_domain())
1110 return -ENODEV;
1111
1112 return gnttab_init();
1113}
1114
1115core_initcall(__gnttab_init);