blob: 1589ea1a24450e8076c683a69a22748dff14a9e8 [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 Li0f9f5a92011-11-22 09:58:06 +0800123};
124
125static struct gnttab_ops *gnttab_interface;
126
Annie Li85ff6ac2011-11-22 09:59:21 +0800127/*This reflects status of grant entries, so act as a global value*/
128static grant_status_t *grstatus;
129
Annie Li0f9f5a92011-11-22 09:58:06 +0800130static int grant_table_version;
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700131
132static struct gnttab_free_callback *gnttab_free_callback_list;
133
134static int gnttab_expand(unsigned int req_entries);
135
136#define RPP (PAGE_SIZE / sizeof(grant_ref_t))
Annie Li85ff6ac2011-11-22 09:59:21 +0800137#define SPP (PAGE_SIZE / sizeof(grant_status_t))
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700138
139static inline grant_ref_t *__gnttab_entry(grant_ref_t entry)
140{
141 return &gnttab_list[(entry) / RPP][(entry) % RPP];
142}
143/* This can be used as an l-value */
144#define gnttab_entry(entry) (*__gnttab_entry(entry))
145
146static int get_free_entries(unsigned count)
147{
148 unsigned long flags;
Konrad Rzeszutek Wilk272800d2011-07-22 14:00:06 -0400149 int ref, rc = 0;
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700150 grant_ref_t head;
151
152 spin_lock_irqsave(&gnttab_list_lock, flags);
153
154 if ((gnttab_free_count < count) &&
155 ((rc = gnttab_expand(count - gnttab_free_count)) < 0)) {
156 spin_unlock_irqrestore(&gnttab_list_lock, flags);
157 return rc;
158 }
159
160 ref = head = gnttab_free_head;
161 gnttab_free_count -= count;
162 while (count-- > 1)
163 head = gnttab_entry(head);
164 gnttab_free_head = gnttab_entry(head);
165 gnttab_entry(head) = GNTTAB_LIST_END;
166
167 spin_unlock_irqrestore(&gnttab_list_lock, flags);
168
169 return ref;
170}
171
172static void do_free_callbacks(void)
173{
174 struct gnttab_free_callback *callback, *next;
175
176 callback = gnttab_free_callback_list;
177 gnttab_free_callback_list = NULL;
178
179 while (callback != NULL) {
180 next = callback->next;
181 if (gnttab_free_count >= callback->count) {
182 callback->next = NULL;
183 callback->fn(callback->arg);
184 } else {
185 callback->next = gnttab_free_callback_list;
186 gnttab_free_callback_list = callback;
187 }
188 callback = next;
189 }
190}
191
192static inline void check_free_callbacks(void)
193{
194 if (unlikely(gnttab_free_callback_list))
195 do_free_callbacks();
196}
197
198static void put_free_entry(grant_ref_t ref)
199{
200 unsigned long flags;
201 spin_lock_irqsave(&gnttab_list_lock, flags);
202 gnttab_entry(ref) = gnttab_free_head;
203 gnttab_free_head = ref;
204 gnttab_free_count++;
205 check_free_callbacks();
206 spin_unlock_irqrestore(&gnttab_list_lock, flags);
207}
208
Annie Li0f9f5a92011-11-22 09:58:06 +0800209/*
Annie Li85ff6ac2011-11-22 09:59:21 +0800210 * Following applies to gnttab_update_entry_v1 and gnttab_update_entry_v2.
Annie Li0f9f5a92011-11-22 09:58:06 +0800211 * Introducing a valid entry into the grant table:
212 * 1. Write ent->domid.
213 * 2. Write ent->frame:
214 * GTF_permit_access: Frame to which access is permitted.
215 * GTF_accept_transfer: Pseudo-phys frame slot being filled by new
216 * frame, or zero if none.
217 * 3. Write memory barrier (WMB).
218 * 4. Write ent->flags, inc. valid type.
219 */
220static void gnttab_update_entry_v1(grant_ref_t ref, domid_t domid,
221 unsigned long frame, unsigned flags)
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700222{
Annie Li0f9f5a92011-11-22 09:58:06 +0800223 gnttab_shared.v1[ref].domid = domid;
224 gnttab_shared.v1[ref].frame = frame;
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700225 wmb();
Annie Li0f9f5a92011-11-22 09:58:06 +0800226 gnttab_shared.v1[ref].flags = flags;
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700227}
228
Annie Li85ff6ac2011-11-22 09:59:21 +0800229static void gnttab_update_entry_v2(grant_ref_t ref, domid_t domid,
230 unsigned long frame, unsigned flags)
231{
232 gnttab_shared.v2[ref].hdr.domid = domid;
233 gnttab_shared.v2[ref].full_page.frame = frame;
234 wmb();
235 gnttab_shared.v2[ref].hdr.flags = GTF_permit_access | flags;
236}
237
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700238/*
239 * Public grant-issuing interface functions
240 */
241void gnttab_grant_foreign_access_ref(grant_ref_t ref, domid_t domid,
242 unsigned long frame, int readonly)
243{
Annie Li0f9f5a92011-11-22 09:58:06 +0800244 gnttab_interface->update_entry(ref, domid, frame,
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700245 GTF_permit_access | (readonly ? GTF_readonly : 0));
246}
247EXPORT_SYMBOL_GPL(gnttab_grant_foreign_access_ref);
248
249int gnttab_grant_foreign_access(domid_t domid, unsigned long frame,
250 int readonly)
251{
252 int ref;
253
254 ref = get_free_entries(1);
255 if (unlikely(ref < 0))
256 return -ENOSPC;
257
258 gnttab_grant_foreign_access_ref(ref, domid, frame, readonly);
259
260 return ref;
261}
262EXPORT_SYMBOL_GPL(gnttab_grant_foreign_access);
263
Annie Li0f9f5a92011-11-22 09:58:06 +0800264static int gnttab_query_foreign_access_v1(grant_ref_t ref)
265{
266 return gnttab_shared.v1[ref].flags & (GTF_reading|GTF_writing);
267}
268
Annie Li85ff6ac2011-11-22 09:59:21 +0800269static int gnttab_query_foreign_access_v2(grant_ref_t ref)
270{
271 return grstatus[ref] & (GTF_reading|GTF_writing);
272}
273
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700274int gnttab_query_foreign_access(grant_ref_t ref)
275{
Annie Li0f9f5a92011-11-22 09:58:06 +0800276 return gnttab_interface->query_foreign_access(ref);
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700277}
278EXPORT_SYMBOL_GPL(gnttab_query_foreign_access);
279
Annie Li0f9f5a92011-11-22 09:58:06 +0800280static int gnttab_end_foreign_access_ref_v1(grant_ref_t ref, int readonly)
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700281{
282 u16 flags, nflags;
Annie Lib1e495b2011-11-22 09:58:47 +0800283 u16 *pflags;
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700284
Annie Lib1e495b2011-11-22 09:58:47 +0800285 pflags = &gnttab_shared.v1[ref].flags;
286 nflags = *pflags;
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700287 do {
288 flags = nflags;
289 if (flags & (GTF_reading|GTF_writing)) {
290 printk(KERN_ALERT "WARNING: g.e. still in use!\n");
291 return 0;
292 }
Annie Lib1e495b2011-11-22 09:58:47 +0800293 } while ((nflags = sync_cmpxchg(pflags, flags, 0)) != flags);
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700294
295 return 1;
296}
Annie Li0f9f5a92011-11-22 09:58:06 +0800297
Annie Li85ff6ac2011-11-22 09:59:21 +0800298static int gnttab_end_foreign_access_ref_v2(grant_ref_t ref, int readonly)
299{
300 gnttab_shared.v2[ref].hdr.flags = 0;
301 mb();
302 if (grstatus[ref] & (GTF_reading|GTF_writing)) {
303 return 0;
304 } else {
305 /* The read of grstatus needs to have acquire
306 semantics. On x86, reads already have
307 that, and we just need to protect against
308 compiler reorderings. On other
309 architectures we may need a full
310 barrier. */
311#ifdef CONFIG_X86
312 barrier();
313#else
314 mb();
315#endif
316 }
317
318 return 1;
319}
320
Annie Li0f9f5a92011-11-22 09:58:06 +0800321int gnttab_end_foreign_access_ref(grant_ref_t ref, int readonly)
322{
323 return gnttab_interface->end_foreign_access_ref(ref, readonly);
324}
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700325EXPORT_SYMBOL_GPL(gnttab_end_foreign_access_ref);
326
327void gnttab_end_foreign_access(grant_ref_t ref, int readonly,
328 unsigned long page)
329{
330 if (gnttab_end_foreign_access_ref(ref, readonly)) {
331 put_free_entry(ref);
332 if (page != 0)
333 free_page(page);
334 } else {
335 /* XXX This needs to be fixed so that the ref and page are
336 placed on a list to be freed up later. */
337 printk(KERN_WARNING
338 "WARNING: leaking g.e. and page still in use!\n");
339 }
340}
341EXPORT_SYMBOL_GPL(gnttab_end_foreign_access);
342
343int gnttab_grant_foreign_transfer(domid_t domid, unsigned long pfn)
344{
345 int ref;
346
347 ref = get_free_entries(1);
348 if (unlikely(ref < 0))
349 return -ENOSPC;
350 gnttab_grant_foreign_transfer_ref(ref, domid, pfn);
351
352 return ref;
353}
354EXPORT_SYMBOL_GPL(gnttab_grant_foreign_transfer);
355
356void gnttab_grant_foreign_transfer_ref(grant_ref_t ref, domid_t domid,
357 unsigned long pfn)
358{
Annie Li0f9f5a92011-11-22 09:58:06 +0800359 gnttab_interface->update_entry(ref, domid, pfn, GTF_accept_transfer);
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700360}
361EXPORT_SYMBOL_GPL(gnttab_grant_foreign_transfer_ref);
362
Annie Li0f9f5a92011-11-22 09:58:06 +0800363static unsigned long gnttab_end_foreign_transfer_ref_v1(grant_ref_t ref)
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700364{
365 unsigned long frame;
366 u16 flags;
Annie Lib1e495b2011-11-22 09:58:47 +0800367 u16 *pflags;
368
369 pflags = &gnttab_shared.v1[ref].flags;
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700370
371 /*
372 * If a transfer is not even yet started, try to reclaim the grant
373 * reference and return failure (== 0).
374 */
Annie Lib1e495b2011-11-22 09:58:47 +0800375 while (!((flags = *pflags) & GTF_transfer_committed)) {
376 if (sync_cmpxchg(pflags, flags, 0) == flags)
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700377 return 0;
378 cpu_relax();
379 }
380
381 /* If a transfer is in progress then wait until it is completed. */
382 while (!(flags & GTF_transfer_completed)) {
Annie Lib1e495b2011-11-22 09:58:47 +0800383 flags = *pflags;
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700384 cpu_relax();
385 }
386
387 rmb(); /* Read the frame number /after/ reading completion status. */
Annie Li0f9f5a92011-11-22 09:58:06 +0800388 frame = gnttab_shared.v1[ref].frame;
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700389 BUG_ON(frame == 0);
390
391 return frame;
392}
Annie Li0f9f5a92011-11-22 09:58:06 +0800393
Annie Li85ff6ac2011-11-22 09:59:21 +0800394static unsigned long gnttab_end_foreign_transfer_ref_v2(grant_ref_t ref)
395{
396 unsigned long frame;
397 u16 flags;
398 u16 *pflags;
399
400 pflags = &gnttab_shared.v2[ref].hdr.flags;
401
402 /*
403 * If a transfer is not even yet started, try to reclaim the grant
404 * reference and return failure (== 0).
405 */
406 while (!((flags = *pflags) & GTF_transfer_committed)) {
407 if (sync_cmpxchg(pflags, flags, 0) == flags)
408 return 0;
409 cpu_relax();
410 }
411
412 /* If a transfer is in progress then wait until it is completed. */
413 while (!(flags & GTF_transfer_completed)) {
414 flags = *pflags;
415 cpu_relax();
416 }
417
418 rmb(); /* Read the frame number /after/ reading completion status. */
419 frame = gnttab_shared.v2[ref].full_page.frame;
420 BUG_ON(frame == 0);
421
422 return frame;
423}
424
Annie Li0f9f5a92011-11-22 09:58:06 +0800425unsigned long gnttab_end_foreign_transfer_ref(grant_ref_t ref)
426{
427 return gnttab_interface->end_foreign_transfer_ref(ref);
428}
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700429EXPORT_SYMBOL_GPL(gnttab_end_foreign_transfer_ref);
430
431unsigned long gnttab_end_foreign_transfer(grant_ref_t ref)
432{
433 unsigned long frame = gnttab_end_foreign_transfer_ref(ref);
434 put_free_entry(ref);
435 return frame;
436}
437EXPORT_SYMBOL_GPL(gnttab_end_foreign_transfer);
438
439void gnttab_free_grant_reference(grant_ref_t ref)
440{
441 put_free_entry(ref);
442}
443EXPORT_SYMBOL_GPL(gnttab_free_grant_reference);
444
445void gnttab_free_grant_references(grant_ref_t head)
446{
447 grant_ref_t ref;
448 unsigned long flags;
449 int count = 1;
450 if (head == GNTTAB_LIST_END)
451 return;
452 spin_lock_irqsave(&gnttab_list_lock, flags);
453 ref = head;
454 while (gnttab_entry(ref) != GNTTAB_LIST_END) {
455 ref = gnttab_entry(ref);
456 count++;
457 }
458 gnttab_entry(ref) = gnttab_free_head;
459 gnttab_free_head = head;
460 gnttab_free_count += count;
461 check_free_callbacks();
462 spin_unlock_irqrestore(&gnttab_list_lock, flags);
463}
464EXPORT_SYMBOL_GPL(gnttab_free_grant_references);
465
466int gnttab_alloc_grant_references(u16 count, grant_ref_t *head)
467{
468 int h = get_free_entries(count);
469
470 if (h < 0)
471 return -ENOSPC;
472
473 *head = h;
474
475 return 0;
476}
477EXPORT_SYMBOL_GPL(gnttab_alloc_grant_references);
478
479int gnttab_empty_grant_references(const grant_ref_t *private_head)
480{
481 return (*private_head == GNTTAB_LIST_END);
482}
483EXPORT_SYMBOL_GPL(gnttab_empty_grant_references);
484
485int gnttab_claim_grant_reference(grant_ref_t *private_head)
486{
487 grant_ref_t g = *private_head;
488 if (unlikely(g == GNTTAB_LIST_END))
489 return -ENOSPC;
490 *private_head = gnttab_entry(g);
491 return g;
492}
493EXPORT_SYMBOL_GPL(gnttab_claim_grant_reference);
494
495void gnttab_release_grant_reference(grant_ref_t *private_head,
496 grant_ref_t release)
497{
498 gnttab_entry(release) = *private_head;
499 *private_head = release;
500}
501EXPORT_SYMBOL_GPL(gnttab_release_grant_reference);
502
503void gnttab_request_free_callback(struct gnttab_free_callback *callback,
504 void (*fn)(void *), void *arg, u16 count)
505{
506 unsigned long flags;
507 spin_lock_irqsave(&gnttab_list_lock, flags);
508 if (callback->next)
509 goto out;
510 callback->fn = fn;
511 callback->arg = arg;
512 callback->count = count;
513 callback->next = gnttab_free_callback_list;
514 gnttab_free_callback_list = callback;
515 check_free_callbacks();
516out:
517 spin_unlock_irqrestore(&gnttab_list_lock, flags);
518}
519EXPORT_SYMBOL_GPL(gnttab_request_free_callback);
520
521void gnttab_cancel_free_callback(struct gnttab_free_callback *callback)
522{
523 struct gnttab_free_callback **pcb;
524 unsigned long flags;
525
526 spin_lock_irqsave(&gnttab_list_lock, flags);
527 for (pcb = &gnttab_free_callback_list; *pcb; pcb = &(*pcb)->next) {
528 if (*pcb == callback) {
529 *pcb = callback->next;
530 break;
531 }
532 }
533 spin_unlock_irqrestore(&gnttab_list_lock, flags);
534}
535EXPORT_SYMBOL_GPL(gnttab_cancel_free_callback);
536
537static int grow_gnttab_list(unsigned int more_frames)
538{
539 unsigned int new_nr_grant_frames, extra_entries, i;
Michael Abd-El-Malekbbc60c12008-04-04 02:33:48 -0700540 unsigned int nr_glist_frames, new_nr_glist_frames;
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700541
542 new_nr_grant_frames = nr_grant_frames + more_frames;
543 extra_entries = more_frames * GREFS_PER_GRANT_FRAME;
544
Michael Abd-El-Malekbbc60c12008-04-04 02:33:48 -0700545 nr_glist_frames = (nr_grant_frames * GREFS_PER_GRANT_FRAME + RPP - 1) / RPP;
546 new_nr_glist_frames =
547 (new_nr_grant_frames * GREFS_PER_GRANT_FRAME + RPP - 1) / RPP;
548 for (i = nr_glist_frames; i < new_nr_glist_frames; i++) {
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700549 gnttab_list[i] = (grant_ref_t *)__get_free_page(GFP_ATOMIC);
550 if (!gnttab_list[i])
551 goto grow_nomem;
552 }
553
554
555 for (i = GREFS_PER_GRANT_FRAME * nr_grant_frames;
556 i < GREFS_PER_GRANT_FRAME * new_nr_grant_frames - 1; i++)
557 gnttab_entry(i) = i + 1;
558
559 gnttab_entry(i) = gnttab_free_head;
560 gnttab_free_head = GREFS_PER_GRANT_FRAME * nr_grant_frames;
561 gnttab_free_count += extra_entries;
562
563 nr_grant_frames = new_nr_grant_frames;
564
565 check_free_callbacks();
566
567 return 0;
568
569grow_nomem:
Michael Abd-El-Malekbbc60c12008-04-04 02:33:48 -0700570 for ( ; i >= nr_glist_frames; i--)
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700571 free_page((unsigned long) gnttab_list[i]);
572 return -ENOMEM;
573}
574
575static unsigned int __max_nr_grant_frames(void)
576{
577 struct gnttab_query_size query;
578 int rc;
579
580 query.dom = DOMID_SELF;
581
582 rc = HYPERVISOR_grant_table_op(GNTTABOP_query_size, &query, 1);
583 if ((rc < 0) || (query.status != GNTST_okay))
584 return 4; /* Legacy max supported number of frames */
585
586 return query.max_nr_frames;
587}
588
Stefano Stabellini183d03c2010-05-17 17:08:21 +0100589unsigned int gnttab_max_grant_frames(void)
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700590{
591 unsigned int xen_max = __max_nr_grant_frames();
592
593 if (xen_max > boot_max_nr_grant_frames)
594 return boot_max_nr_grant_frames;
595 return xen_max;
596}
Stefano Stabellini183d03c2010-05-17 17:08:21 +0100597EXPORT_SYMBOL_GPL(gnttab_max_grant_frames);
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700598
Stefano Stabellini289b7772010-12-10 14:54:44 +0000599int gnttab_map_refs(struct gnttab_map_grant_ref *map_ops,
Annie Lic1237992011-11-22 09:59:56 +0800600 struct gnttab_map_grant_ref *kmap_ops,
601 struct page **pages, unsigned int count)
Stefano Stabellini289b7772010-12-10 14:54:44 +0000602{
603 int i, ret;
604 pte_t *pte;
605 unsigned long mfn;
606
607 ret = HYPERVISOR_grant_table_op(GNTTABOP_map_grant_ref, map_ops, count);
Jeremy Fitzhardinge87f1d402010-12-13 14:42:30 +0000608 if (ret)
609 return ret;
Stefano Stabellini289b7772010-12-10 14:54:44 +0000610
Daniel De Graafaab8f112011-02-03 12:19:02 -0500611 if (xen_feature(XENFEAT_auto_translated_physmap))
612 return ret;
613
Stefano Stabellini289b7772010-12-10 14:54:44 +0000614 for (i = 0; i < count; i++) {
Ian Campbelldc4972a2011-03-04 17:38:21 +0000615 /* Do not add to override if the map failed. */
616 if (map_ops[i].status)
617 continue;
618
Konrad Rzeszutek Wilkcf8d9162011-02-28 17:58:48 -0500619 if (map_ops[i].flags & GNTMAP_contains_pte) {
620 pte = (pte_t *) (mfn_to_virt(PFN_DOWN(map_ops[i].host_addr)) +
Stefano Stabellini289b7772010-12-10 14:54:44 +0000621 (map_ops[i].host_addr & ~PAGE_MASK));
Konrad Rzeszutek Wilkcf8d9162011-02-28 17:58:48 -0500622 mfn = pte_mfn(*pte);
623 } else {
624 /* If you really wanted to do this:
625 * mfn = PFN_DOWN(map_ops[i].dev_bus_addr);
626 *
627 * The reason we do not implement it is b/c on the
628 * unmap path (gnttab_unmap_refs) we have no means of
629 * checking whether the page is !GNTMAP_contains_pte.
630 *
631 * That is without some extra data-structure to carry
632 * the struct page, bool clear_pte, and list_head next
633 * tuples and deal with allocation/delallocation, etc.
634 *
635 * The users of this API set the GNTMAP_contains_pte
636 * flag so lets just return not supported until it
637 * becomes neccessary to implement.
638 */
639 return -EOPNOTSUPP;
640 }
Stefano Stabellini0930bba2011-09-29 11:57:56 +0100641 ret = m2p_add_override(mfn, pages[i], &kmap_ops[i]);
Jeremy Fitzhardinge87f1d402010-12-13 14:42:30 +0000642 if (ret)
643 return ret;
Stefano Stabellini289b7772010-12-10 14:54:44 +0000644 }
645
646 return ret;
647}
648EXPORT_SYMBOL_GPL(gnttab_map_refs);
649
650int gnttab_unmap_refs(struct gnttab_unmap_grant_ref *unmap_ops,
Annie Lic1237992011-11-22 09:59:56 +0800651 struct page **pages, unsigned int count)
Stefano Stabellini289b7772010-12-10 14:54:44 +0000652{
653 int i, ret;
654
655 ret = HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref, unmap_ops, count);
Jeremy Fitzhardinge87f1d402010-12-13 14:42:30 +0000656 if (ret)
657 return ret;
658
Daniel De Graafaab8f112011-02-03 12:19:02 -0500659 if (xen_feature(XENFEAT_auto_translated_physmap))
660 return ret;
661
Jeremy Fitzhardinge87f1d402010-12-13 14:42:30 +0000662 for (i = 0; i < count; i++) {
Konrad Rzeszutek Wilkcf8d9162011-02-28 17:58:48 -0500663 ret = m2p_remove_override(pages[i], true /* clear the PTE */);
Jeremy Fitzhardinge87f1d402010-12-13 14:42:30 +0000664 if (ret)
665 return ret;
666 }
Stefano Stabellini289b7772010-12-10 14:54:44 +0000667
668 return ret;
669}
670EXPORT_SYMBOL_GPL(gnttab_unmap_refs);
671
Annie Li85ff6ac2011-11-22 09:59:21 +0800672static unsigned nr_status_frames(unsigned nr_grant_frames)
673{
674 return (nr_grant_frames * GREFS_PER_GRANT_FRAME + SPP - 1) / SPP;
675}
676
Annie Li0f9f5a92011-11-22 09:58:06 +0800677static int gnttab_map_frames_v1(unsigned long *frames, unsigned int nr_gframes)
678{
679 int rc;
680
681 rc = arch_gnttab_map_shared(frames, nr_gframes,
682 gnttab_max_grant_frames(),
683 &gnttab_shared.addr);
684 BUG_ON(rc);
685
686 return 0;
687}
688
689static void gnttab_unmap_frames_v1(void)
690{
Annie Li85ff6ac2011-11-22 09:59:21 +0800691 arch_gnttab_unmap(gnttab_shared.addr, nr_grant_frames);
692}
693
694static int gnttab_map_frames_v2(unsigned long *frames, unsigned int nr_gframes)
695{
696 uint64_t *sframes;
697 unsigned int nr_sframes;
698 struct gnttab_get_status_frames getframes;
699 int rc;
700
701 nr_sframes = nr_status_frames(nr_gframes);
702
703 /* No need for kzalloc as it is initialized in following hypercall
704 * GNTTABOP_get_status_frames.
705 */
706 sframes = kmalloc(nr_sframes * sizeof(uint64_t), GFP_ATOMIC);
707 if (!sframes)
708 return -ENOMEM;
709
710 getframes.dom = DOMID_SELF;
711 getframes.nr_frames = nr_sframes;
712 set_xen_guest_handle(getframes.frame_list, sframes);
713
714 rc = HYPERVISOR_grant_table_op(GNTTABOP_get_status_frames,
715 &getframes, 1);
716 if (rc == -ENOSYS) {
717 kfree(sframes);
718 return -ENOSYS;
719 }
720
721 BUG_ON(rc || getframes.status);
722
723 rc = arch_gnttab_map_status(sframes, nr_sframes,
724 nr_status_frames(gnttab_max_grant_frames()),
725 &grstatus);
726 BUG_ON(rc);
727 kfree(sframes);
728
729 rc = arch_gnttab_map_shared(frames, nr_gframes,
730 gnttab_max_grant_frames(),
731 &gnttab_shared.addr);
732 BUG_ON(rc);
733
734 return 0;
735}
736
737static void gnttab_unmap_frames_v2(void)
738{
739 arch_gnttab_unmap(gnttab_shared.addr, nr_grant_frames);
740 arch_gnttab_unmap(grstatus, nr_status_frames(nr_grant_frames));
Annie Li0f9f5a92011-11-22 09:58:06 +0800741}
742
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700743static int gnttab_map(unsigned int start_idx, unsigned int end_idx)
744{
745 struct gnttab_setup_table setup;
746 unsigned long *frames;
747 unsigned int nr_gframes = end_idx + 1;
748 int rc;
749
Stefano Stabellini183d03c2010-05-17 17:08:21 +0100750 if (xen_hvm_domain()) {
751 struct xen_add_to_physmap xatp;
752 unsigned int i = end_idx;
753 rc = 0;
754 /*
755 * Loop backwards, so that the first hypercall has the largest
756 * index, ensuring that the table will grow only once.
757 */
758 do {
759 xatp.domid = DOMID_SELF;
760 xatp.idx = i;
761 xatp.space = XENMAPSPACE_grant_table;
762 xatp.gpfn = (xen_hvm_resume_frames >> PAGE_SHIFT) + i;
763 rc = HYPERVISOR_memory_op(XENMEM_add_to_physmap, &xatp);
764 if (rc != 0) {
765 printk(KERN_WARNING
766 "grant table add_to_physmap failed, err=%d\n", rc);
767 break;
768 }
769 } while (i-- > start_idx);
770
771 return rc;
772 }
773
Annie Li85ff6ac2011-11-22 09:59:21 +0800774 /* No need for kzalloc as it is initialized in following hypercall
775 * GNTTABOP_setup_table.
776 */
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700777 frames = kmalloc(nr_gframes * sizeof(unsigned long), GFP_ATOMIC);
778 if (!frames)
779 return -ENOMEM;
780
781 setup.dom = DOMID_SELF;
782 setup.nr_frames = nr_gframes;
Isaku Yamahata87e27cf2008-04-02 10:53:52 -0700783 set_xen_guest_handle(setup.frame_list, frames);
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700784
785 rc = HYPERVISOR_grant_table_op(GNTTABOP_setup_table, &setup, 1);
786 if (rc == -ENOSYS) {
787 kfree(frames);
788 return -ENOSYS;
789 }
790
791 BUG_ON(rc || setup.status);
792
Annie Li0f9f5a92011-11-22 09:58:06 +0800793 rc = gnttab_interface->map_frames(frames, nr_gframes);
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700794
795 kfree(frames);
796
Annie Li0f9f5a92011-11-22 09:58:06 +0800797 return rc;
798}
799
800static struct gnttab_ops gnttab_v1_ops = {
801 .map_frames = gnttab_map_frames_v1,
802 .unmap_frames = gnttab_unmap_frames_v1,
803 .update_entry = gnttab_update_entry_v1,
804 .end_foreign_access_ref = gnttab_end_foreign_access_ref_v1,
805 .end_foreign_transfer_ref = gnttab_end_foreign_transfer_ref_v1,
806 .query_foreign_access = gnttab_query_foreign_access_v1,
807};
808
Annie Li85ff6ac2011-11-22 09:59:21 +0800809static struct gnttab_ops gnttab_v2_ops = {
810 .map_frames = gnttab_map_frames_v2,
811 .unmap_frames = gnttab_unmap_frames_v2,
812 .update_entry = gnttab_update_entry_v2,
813 .end_foreign_access_ref = gnttab_end_foreign_access_ref_v2,
814 .end_foreign_transfer_ref = gnttab_end_foreign_transfer_ref_v2,
815 .query_foreign_access = gnttab_query_foreign_access_v2,
816};
817
Annie Li0f9f5a92011-11-22 09:58:06 +0800818static void gnttab_request_version(void)
819{
Annie Li85ff6ac2011-11-22 09:59:21 +0800820 int rc;
821 struct gnttab_set_version gsv;
822
823 gsv.version = 2;
824 rc = HYPERVISOR_grant_table_op(GNTTABOP_set_version, &gsv, 1);
825 if (rc == 0) {
826 grant_table_version = 2;
827 gnttab_interface = &gnttab_v2_ops;
828 } else if (grant_table_version == 2) {
829 /*
830 * If we've already used version 2 features,
831 * but then suddenly discover that they're not
832 * available (e.g. migrating to an older
833 * version of Xen), almost unbounded badness
834 * can happen.
835 */
836 panic("we need grant tables version 2, but only version 1 is available");
837 } else {
838 grant_table_version = 1;
839 gnttab_interface = &gnttab_v1_ops;
840 }
Annie Li0f9f5a92011-11-22 09:58:06 +0800841 printk(KERN_INFO "Grant tables using version %d layout.\n",
842 grant_table_version);
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700843}
844
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100845int gnttab_resume(void)
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700846{
Stefano Stabellini183d03c2010-05-17 17:08:21 +0100847 unsigned int max_nr_gframes;
848
Annie Li0f9f5a92011-11-22 09:58:06 +0800849 gnttab_request_version();
Stefano Stabellini183d03c2010-05-17 17:08:21 +0100850 max_nr_gframes = gnttab_max_grant_frames();
851 if (max_nr_gframes < nr_grant_frames)
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700852 return -ENOSYS;
Stefano Stabellini183d03c2010-05-17 17:08:21 +0100853
854 if (xen_pv_domain())
855 return gnttab_map(0, nr_grant_frames - 1);
856
Annie Li0f9f5a92011-11-22 09:58:06 +0800857 if (gnttab_shared.addr == NULL) {
858 gnttab_shared.addr = ioremap(xen_hvm_resume_frames,
859 PAGE_SIZE * max_nr_gframes);
860 if (gnttab_shared.addr == NULL) {
Stefano Stabellini183d03c2010-05-17 17:08:21 +0100861 printk(KERN_WARNING
862 "Failed to ioremap gnttab share frames!");
863 return -ENOMEM;
864 }
865 }
866
867 gnttab_map(0, nr_grant_frames - 1);
868
869 return 0;
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700870}
871
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100872int gnttab_suspend(void)
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700873{
Annie Li0f9f5a92011-11-22 09:58:06 +0800874 gnttab_interface->unmap_frames();
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700875 return 0;
876}
877
878static int gnttab_expand(unsigned int req_entries)
879{
880 int rc;
881 unsigned int cur, extra;
882
883 cur = nr_grant_frames;
884 extra = ((req_entries + (GREFS_PER_GRANT_FRAME-1)) /
885 GREFS_PER_GRANT_FRAME);
Stefano Stabellini183d03c2010-05-17 17:08:21 +0100886 if (cur + extra > gnttab_max_grant_frames())
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700887 return -ENOSPC;
888
889 rc = gnttab_map(cur, cur + extra - 1);
890 if (rc == 0)
891 rc = grow_gnttab_list(extra);
892
893 return rc;
894}
895
Stefano Stabellini183d03c2010-05-17 17:08:21 +0100896int gnttab_init(void)
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700897{
898 int i;
Michael Abd-El-Malekbbc60c12008-04-04 02:33:48 -0700899 unsigned int max_nr_glist_frames, nr_glist_frames;
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700900 unsigned int nr_init_grefs;
901
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700902 nr_grant_frames = 1;
903 boot_max_nr_grant_frames = __max_nr_grant_frames();
904
905 /* Determine the maximum number of frames required for the
906 * grant reference free list on the current hypervisor.
907 */
908 max_nr_glist_frames = (boot_max_nr_grant_frames *
Michael Abd-El-Malekbbc60c12008-04-04 02:33:48 -0700909 GREFS_PER_GRANT_FRAME / RPP);
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700910
911 gnttab_list = kmalloc(max_nr_glist_frames * sizeof(grant_ref_t *),
912 GFP_KERNEL);
913 if (gnttab_list == NULL)
914 return -ENOMEM;
915
Michael Abd-El-Malekbbc60c12008-04-04 02:33:48 -0700916 nr_glist_frames = (nr_grant_frames * GREFS_PER_GRANT_FRAME + RPP - 1) / RPP;
917 for (i = 0; i < nr_glist_frames; i++) {
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700918 gnttab_list[i] = (grant_ref_t *)__get_free_page(GFP_KERNEL);
919 if (gnttab_list[i] == NULL)
920 goto ini_nomem;
921 }
922
923 if (gnttab_resume() < 0)
924 return -ENODEV;
925
926 nr_init_grefs = nr_grant_frames * GREFS_PER_GRANT_FRAME;
927
928 for (i = NR_RESERVED_ENTRIES; i < nr_init_grefs - 1; i++)
929 gnttab_entry(i) = i + 1;
930
931 gnttab_entry(nr_init_grefs - 1) = GNTTAB_LIST_END;
932 gnttab_free_count = nr_init_grefs - NR_RESERVED_ENTRIES;
933 gnttab_free_head = NR_RESERVED_ENTRIES;
934
935 printk("Grant table initialized\n");
936 return 0;
937
938 ini_nomem:
939 for (i--; i >= 0; i--)
940 free_page((unsigned long)gnttab_list[i]);
941 kfree(gnttab_list);
942 return -ENOMEM;
943}
Stefano Stabellini183d03c2010-05-17 17:08:21 +0100944EXPORT_SYMBOL_GPL(gnttab_init);
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700945
Stefano Stabellini183d03c2010-05-17 17:08:21 +0100946static int __devinit __gnttab_init(void)
947{
948 /* Delay grant-table initialization in the PV on HVM case */
949 if (xen_hvm_domain())
950 return 0;
951
952 if (!xen_pv_domain())
953 return -ENODEV;
954
955 return gnttab_init();
956}
957
958core_initcall(__gnttab_init);