diff options
-rw-r--r-- | arch/x86/xen/grant-table.c | 39 | ||||
-rw-r--r-- | drivers/xen/grant-table.c | 171 | ||||
-rw-r--r-- | include/xen/grant_table.h | 6 |
3 files changed, 209 insertions, 7 deletions
diff --git a/arch/x86/xen/grant-table.c b/arch/x86/xen/grant-table.c index c6ab2e7ca3a6..65160a8a0ba3 100644 --- a/arch/x86/xen/grant-table.c +++ b/arch/x86/xen/grant-table.c | |||
@@ -54,6 +54,20 @@ static int map_pte_fn(pte_t *pte, struct page *pmd_page, | |||
54 | return 0; | 54 | return 0; |
55 | } | 55 | } |
56 | 56 | ||
57 | /* | ||
58 | * This function is used to map shared frames to store grant status. It is | ||
59 | * different from map_pte_fn above, the frames type here is uint64_t. | ||
60 | */ | ||
61 | static int map_pte_fn_status(pte_t *pte, struct page *pmd_page, | ||
62 | unsigned long addr, void *data) | ||
63 | { | ||
64 | uint64_t **frames = (uint64_t **)data; | ||
65 | |||
66 | set_pte_at(&init_mm, addr, pte, mfn_pte((*frames)[0], PAGE_KERNEL)); | ||
67 | (*frames)++; | ||
68 | return 0; | ||
69 | } | ||
70 | |||
57 | static int unmap_pte_fn(pte_t *pte, struct page *pmd_page, | 71 | static int unmap_pte_fn(pte_t *pte, struct page *pmd_page, |
58 | unsigned long addr, void *data) | 72 | unsigned long addr, void *data) |
59 | { | 73 | { |
@@ -83,7 +97,30 @@ int arch_gnttab_map_shared(unsigned long *frames, unsigned long nr_gframes, | |||
83 | return rc; | 97 | return rc; |
84 | } | 98 | } |
85 | 99 | ||
86 | void arch_gnttab_unmap_shared(void *shared, unsigned long nr_gframes) | 100 | int arch_gnttab_map_status(uint64_t *frames, unsigned long nr_gframes, |
101 | unsigned long max_nr_gframes, | ||
102 | grant_status_t **__shared) | ||
103 | { | ||
104 | int rc; | ||
105 | grant_status_t *shared = *__shared; | ||
106 | |||
107 | if (shared == NULL) { | ||
108 | /* No need to pass in PTE as we are going to do it | ||
109 | * in apply_to_page_range anyhow. */ | ||
110 | struct vm_struct *area = | ||
111 | alloc_vm_area(PAGE_SIZE * max_nr_gframes, NULL); | ||
112 | BUG_ON(area == NULL); | ||
113 | shared = area->addr; | ||
114 | *__shared = shared; | ||
115 | } | ||
116 | |||
117 | rc = apply_to_page_range(&init_mm, (unsigned long)shared, | ||
118 | PAGE_SIZE * nr_gframes, | ||
119 | map_pte_fn_status, &frames); | ||
120 | return rc; | ||
121 | } | ||
122 | |||
123 | void arch_gnttab_unmap(void *shared, unsigned long nr_gframes) | ||
87 | { | 124 | { |
88 | apply_to_page_range(&init_mm, (unsigned long)shared, | 125 | apply_to_page_range(&init_mm, (unsigned long)shared, |
89 | PAGE_SIZE * nr_gframes, unmap_pte_fn, NULL); | 126 | PAGE_SIZE * nr_gframes, unmap_pte_fn, NULL); |
diff --git a/drivers/xen/grant-table.c b/drivers/xen/grant-table.c index 0518d0404942..301869f60dc7 100644 --- a/drivers/xen/grant-table.c +++ b/drivers/xen/grant-table.c | |||
@@ -44,6 +44,7 @@ | |||
44 | #include <xen/page.h> | 44 | #include <xen/page.h> |
45 | #include <xen/grant_table.h> | 45 | #include <xen/grant_table.h> |
46 | #include <xen/interface/memory.h> | 46 | #include <xen/interface/memory.h> |
47 | #include <xen/hvc-console.h> | ||
47 | #include <asm/xen/hypercall.h> | 48 | #include <asm/xen/hypercall.h> |
48 | 49 | ||
49 | #include <asm/pgtable.h> | 50 | #include <asm/pgtable.h> |
@@ -53,7 +54,10 @@ | |||
53 | /* External tools reserve first few grant table entries. */ | 54 | /* External tools reserve first few grant table entries. */ |
54 | #define NR_RESERVED_ENTRIES 8 | 55 | #define NR_RESERVED_ENTRIES 8 |
55 | #define GNTTAB_LIST_END 0xffffffff | 56 | #define GNTTAB_LIST_END 0xffffffff |
56 | #define GREFS_PER_GRANT_FRAME (PAGE_SIZE / sizeof(struct grant_entry_v1)) | 57 | #define GREFS_PER_GRANT_FRAME \ |
58 | (grant_table_version == 1 ? \ | ||
59 | (PAGE_SIZE / sizeof(struct grant_entry_v1)) : \ | ||
60 | (PAGE_SIZE / sizeof(union grant_entry_v2))) | ||
57 | 61 | ||
58 | static grant_ref_t **gnttab_list; | 62 | static grant_ref_t **gnttab_list; |
59 | static unsigned int nr_grant_frames; | 63 | static unsigned int nr_grant_frames; |
@@ -66,6 +70,7 @@ EXPORT_SYMBOL_GPL(xen_hvm_resume_frames); | |||
66 | 70 | ||
67 | static union { | 71 | static union { |
68 | struct grant_entry_v1 *v1; | 72 | struct grant_entry_v1 *v1; |
73 | union grant_entry_v2 *v2; | ||
69 | void *addr; | 74 | void *addr; |
70 | } gnttab_shared; | 75 | } gnttab_shared; |
71 | 76 | ||
@@ -120,6 +125,9 @@ struct gnttab_ops { | |||
120 | 125 | ||
121 | static struct gnttab_ops *gnttab_interface; | 126 | static struct gnttab_ops *gnttab_interface; |
122 | 127 | ||
128 | /*This reflects status of grant entries, so act as a global value*/ | ||
129 | static grant_status_t *grstatus; | ||
130 | |||
123 | static int grant_table_version; | 131 | static int grant_table_version; |
124 | 132 | ||
125 | static struct gnttab_free_callback *gnttab_free_callback_list; | 133 | static struct gnttab_free_callback *gnttab_free_callback_list; |
@@ -127,6 +135,7 @@ static struct gnttab_free_callback *gnttab_free_callback_list; | |||
127 | static int gnttab_expand(unsigned int req_entries); | 135 | static int gnttab_expand(unsigned int req_entries); |
128 | 136 | ||
129 | #define RPP (PAGE_SIZE / sizeof(grant_ref_t)) | 137 | #define RPP (PAGE_SIZE / sizeof(grant_ref_t)) |
138 | #define SPP (PAGE_SIZE / sizeof(grant_status_t)) | ||
130 | 139 | ||
131 | static inline grant_ref_t *__gnttab_entry(grant_ref_t entry) | 140 | static inline grant_ref_t *__gnttab_entry(grant_ref_t entry) |
132 | { | 141 | { |
@@ -199,6 +208,7 @@ static void put_free_entry(grant_ref_t ref) | |||
199 | } | 208 | } |
200 | 209 | ||
201 | /* | 210 | /* |
211 | * Following applies to gnttab_update_entry_v1 and gnttab_update_entry_v2. | ||
202 | * Introducing a valid entry into the grant table: | 212 | * Introducing a valid entry into the grant table: |
203 | * 1. Write ent->domid. | 213 | * 1. Write ent->domid. |
204 | * 2. Write ent->frame: | 214 | * 2. Write ent->frame: |
@@ -217,6 +227,15 @@ static void gnttab_update_entry_v1(grant_ref_t ref, domid_t domid, | |||
217 | gnttab_shared.v1[ref].flags = flags; | 227 | gnttab_shared.v1[ref].flags = flags; |
218 | } | 228 | } |
219 | 229 | ||
230 | static void gnttab_update_entry_v2(grant_ref_t ref, domid_t domid, | ||
231 | unsigned long frame, unsigned flags) | ||
232 | { | ||
233 | gnttab_shared.v2[ref].hdr.domid = domid; | ||
234 | gnttab_shared.v2[ref].full_page.frame = frame; | ||
235 | wmb(); | ||
236 | gnttab_shared.v2[ref].hdr.flags = GTF_permit_access | flags; | ||
237 | } | ||
238 | |||
220 | /* | 239 | /* |
221 | * Public grant-issuing interface functions | 240 | * Public grant-issuing interface functions |
222 | */ | 241 | */ |
@@ -248,6 +267,11 @@ static int gnttab_query_foreign_access_v1(grant_ref_t ref) | |||
248 | return gnttab_shared.v1[ref].flags & (GTF_reading|GTF_writing); | 267 | return gnttab_shared.v1[ref].flags & (GTF_reading|GTF_writing); |
249 | } | 268 | } |
250 | 269 | ||
270 | static int gnttab_query_foreign_access_v2(grant_ref_t ref) | ||
271 | { | ||
272 | return grstatus[ref] & (GTF_reading|GTF_writing); | ||
273 | } | ||
274 | |||
251 | int gnttab_query_foreign_access(grant_ref_t ref) | 275 | int gnttab_query_foreign_access(grant_ref_t ref) |
252 | { | 276 | { |
253 | return gnttab_interface->query_foreign_access(ref); | 277 | return gnttab_interface->query_foreign_access(ref); |
@@ -272,6 +296,29 @@ static int gnttab_end_foreign_access_ref_v1(grant_ref_t ref, int readonly) | |||
272 | return 1; | 296 | return 1; |
273 | } | 297 | } |
274 | 298 | ||
299 | static int gnttab_end_foreign_access_ref_v2(grant_ref_t ref, int readonly) | ||
300 | { | ||
301 | gnttab_shared.v2[ref].hdr.flags = 0; | ||
302 | mb(); | ||
303 | if (grstatus[ref] & (GTF_reading|GTF_writing)) { | ||
304 | return 0; | ||
305 | } else { | ||
306 | /* The read of grstatus needs to have acquire | ||
307 | semantics. On x86, reads already have | ||
308 | that, and we just need to protect against | ||
309 | compiler reorderings. On other | ||
310 | architectures we may need a full | ||
311 | barrier. */ | ||
312 | #ifdef CONFIG_X86 | ||
313 | barrier(); | ||
314 | #else | ||
315 | mb(); | ||
316 | #endif | ||
317 | } | ||
318 | |||
319 | return 1; | ||
320 | } | ||
321 | |||
275 | int gnttab_end_foreign_access_ref(grant_ref_t ref, int readonly) | 322 | int gnttab_end_foreign_access_ref(grant_ref_t ref, int readonly) |
276 | { | 323 | { |
277 | return gnttab_interface->end_foreign_access_ref(ref, readonly); | 324 | return gnttab_interface->end_foreign_access_ref(ref, readonly); |
@@ -345,6 +392,37 @@ static unsigned long gnttab_end_foreign_transfer_ref_v1(grant_ref_t ref) | |||
345 | return frame; | 392 | return frame; |
346 | } | 393 | } |
347 | 394 | ||
395 | static unsigned long gnttab_end_foreign_transfer_ref_v2(grant_ref_t ref) | ||
396 | { | ||
397 | unsigned long frame; | ||
398 | u16 flags; | ||
399 | u16 *pflags; | ||
400 | |||
401 | pflags = &gnttab_shared.v2[ref].hdr.flags; | ||
402 | |||
403 | /* | ||
404 | * If a transfer is not even yet started, try to reclaim the grant | ||
405 | * reference and return failure (== 0). | ||
406 | */ | ||
407 | while (!((flags = *pflags) & GTF_transfer_committed)) { | ||
408 | if (sync_cmpxchg(pflags, flags, 0) == flags) | ||
409 | return 0; | ||
410 | cpu_relax(); | ||
411 | } | ||
412 | |||
413 | /* If a transfer is in progress then wait until it is completed. */ | ||
414 | while (!(flags & GTF_transfer_completed)) { | ||
415 | flags = *pflags; | ||
416 | cpu_relax(); | ||
417 | } | ||
418 | |||
419 | rmb(); /* Read the frame number /after/ reading completion status. */ | ||
420 | frame = gnttab_shared.v2[ref].full_page.frame; | ||
421 | BUG_ON(frame == 0); | ||
422 | |||
423 | return frame; | ||
424 | } | ||
425 | |||
348 | unsigned long gnttab_end_foreign_transfer_ref(grant_ref_t ref) | 426 | unsigned long gnttab_end_foreign_transfer_ref(grant_ref_t ref) |
349 | { | 427 | { |
350 | return gnttab_interface->end_foreign_transfer_ref(ref); | 428 | return gnttab_interface->end_foreign_transfer_ref(ref); |
@@ -592,6 +670,11 @@ int gnttab_unmap_refs(struct gnttab_unmap_grant_ref *unmap_ops, | |||
592 | } | 670 | } |
593 | EXPORT_SYMBOL_GPL(gnttab_unmap_refs); | 671 | EXPORT_SYMBOL_GPL(gnttab_unmap_refs); |
594 | 672 | ||
673 | static unsigned nr_status_frames(unsigned nr_grant_frames) | ||
674 | { | ||
675 | return (nr_grant_frames * GREFS_PER_GRANT_FRAME + SPP - 1) / SPP; | ||
676 | } | ||
677 | |||
595 | static int gnttab_map_frames_v1(unsigned long *frames, unsigned int nr_gframes) | 678 | static int gnttab_map_frames_v1(unsigned long *frames, unsigned int nr_gframes) |
596 | { | 679 | { |
597 | int rc; | 680 | int rc; |
@@ -606,7 +689,56 @@ static int gnttab_map_frames_v1(unsigned long *frames, unsigned int nr_gframes) | |||
606 | 689 | ||
607 | static void gnttab_unmap_frames_v1(void) | 690 | static void gnttab_unmap_frames_v1(void) |
608 | { | 691 | { |
609 | arch_gnttab_unmap_shared(gnttab_shared.addr, nr_grant_frames); | 692 | arch_gnttab_unmap(gnttab_shared.addr, nr_grant_frames); |
693 | } | ||
694 | |||
695 | static int gnttab_map_frames_v2(unsigned long *frames, unsigned int nr_gframes) | ||
696 | { | ||
697 | uint64_t *sframes; | ||
698 | unsigned int nr_sframes; | ||
699 | struct gnttab_get_status_frames getframes; | ||
700 | int rc; | ||
701 | |||
702 | nr_sframes = nr_status_frames(nr_gframes); | ||
703 | |||
704 | /* No need for kzalloc as it is initialized in following hypercall | ||
705 | * GNTTABOP_get_status_frames. | ||
706 | */ | ||
707 | sframes = kmalloc(nr_sframes * sizeof(uint64_t), GFP_ATOMIC); | ||
708 | if (!sframes) | ||
709 | return -ENOMEM; | ||
710 | |||
711 | getframes.dom = DOMID_SELF; | ||
712 | getframes.nr_frames = nr_sframes; | ||
713 | set_xen_guest_handle(getframes.frame_list, sframes); | ||
714 | |||
715 | rc = HYPERVISOR_grant_table_op(GNTTABOP_get_status_frames, | ||
716 | &getframes, 1); | ||
717 | if (rc == -ENOSYS) { | ||
718 | kfree(sframes); | ||
719 | return -ENOSYS; | ||
720 | } | ||
721 | |||
722 | BUG_ON(rc || getframes.status); | ||
723 | |||
724 | rc = arch_gnttab_map_status(sframes, nr_sframes, | ||
725 | nr_status_frames(gnttab_max_grant_frames()), | ||
726 | &grstatus); | ||
727 | BUG_ON(rc); | ||
728 | kfree(sframes); | ||
729 | |||
730 | rc = arch_gnttab_map_shared(frames, nr_gframes, | ||
731 | gnttab_max_grant_frames(), | ||
732 | &gnttab_shared.addr); | ||
733 | BUG_ON(rc); | ||
734 | |||
735 | return 0; | ||
736 | } | ||
737 | |||
738 | static void gnttab_unmap_frames_v2(void) | ||
739 | { | ||
740 | arch_gnttab_unmap(gnttab_shared.addr, nr_grant_frames); | ||
741 | arch_gnttab_unmap(grstatus, nr_status_frames(nr_grant_frames)); | ||
610 | } | 742 | } |
611 | 743 | ||
612 | static int gnttab_map(unsigned int start_idx, unsigned int end_idx) | 744 | static int gnttab_map(unsigned int start_idx, unsigned int end_idx) |
@@ -640,6 +772,9 @@ static int gnttab_map(unsigned int start_idx, unsigned int end_idx) | |||
640 | return rc; | 772 | return rc; |
641 | } | 773 | } |
642 | 774 | ||
775 | /* No need for kzalloc as it is initialized in following hypercall | ||
776 | * GNTTABOP_setup_table. | ||
777 | */ | ||
643 | frames = kmalloc(nr_gframes * sizeof(unsigned long), GFP_ATOMIC); | 778 | frames = kmalloc(nr_gframes * sizeof(unsigned long), GFP_ATOMIC); |
644 | if (!frames) | 779 | if (!frames) |
645 | return -ENOMEM; | 780 | return -ENOMEM; |
@@ -672,10 +807,38 @@ static struct gnttab_ops gnttab_v1_ops = { | |||
672 | .query_foreign_access = gnttab_query_foreign_access_v1, | 807 | .query_foreign_access = gnttab_query_foreign_access_v1, |
673 | }; | 808 | }; |
674 | 809 | ||
810 | static struct gnttab_ops gnttab_v2_ops = { | ||
811 | .map_frames = gnttab_map_frames_v2, | ||
812 | .unmap_frames = gnttab_unmap_frames_v2, | ||
813 | .update_entry = gnttab_update_entry_v2, | ||
814 | .end_foreign_access_ref = gnttab_end_foreign_access_ref_v2, | ||
815 | .end_foreign_transfer_ref = gnttab_end_foreign_transfer_ref_v2, | ||
816 | .query_foreign_access = gnttab_query_foreign_access_v2, | ||
817 | }; | ||
818 | |||
675 | static void gnttab_request_version(void) | 819 | static void gnttab_request_version(void) |
676 | { | 820 | { |
677 | grant_table_version = 1; | 821 | int rc; |
678 | gnttab_interface = &gnttab_v1_ops; | 822 | struct gnttab_set_version gsv; |
823 | |||
824 | gsv.version = 2; | ||
825 | rc = HYPERVISOR_grant_table_op(GNTTABOP_set_version, &gsv, 1); | ||
826 | if (rc == 0) { | ||
827 | grant_table_version = 2; | ||
828 | gnttab_interface = &gnttab_v2_ops; | ||
829 | } else if (grant_table_version == 2) { | ||
830 | /* | ||
831 | * If we've already used version 2 features, | ||
832 | * but then suddenly discover that they're not | ||
833 | * available (e.g. migrating to an older | ||
834 | * version of Xen), almost unbounded badness | ||
835 | * can happen. | ||
836 | */ | ||
837 | panic("we need grant tables version 2, but only version 1 is available"); | ||
838 | } else { | ||
839 | grant_table_version = 1; | ||
840 | gnttab_interface = &gnttab_v1_ops; | ||
841 | } | ||
679 | printk(KERN_INFO "Grant tables using version %d layout.\n", | 842 | printk(KERN_INFO "Grant tables using version %d layout.\n", |
680 | grant_table_version); | 843 | grant_table_version); |
681 | } | 844 | } |
diff --git a/include/xen/grant_table.h b/include/xen/grant_table.h index c7a40f8d455a..5494c402c83a 100644 --- a/include/xen/grant_table.h +++ b/include/xen/grant_table.h | |||
@@ -146,8 +146,10 @@ gnttab_set_unmap_op(struct gnttab_unmap_grant_ref *unmap, phys_addr_t addr, | |||
146 | int arch_gnttab_map_shared(unsigned long *frames, unsigned long nr_gframes, | 146 | int arch_gnttab_map_shared(unsigned long *frames, unsigned long nr_gframes, |
147 | unsigned long max_nr_gframes, | 147 | unsigned long max_nr_gframes, |
148 | void **__shared); | 148 | void **__shared); |
149 | void arch_gnttab_unmap_shared(void *shared, | 149 | int arch_gnttab_map_status(uint64_t *frames, unsigned long nr_gframes, |
150 | unsigned long nr_gframes); | 150 | unsigned long max_nr_gframes, |
151 | grant_status_t **__shared); | ||
152 | void arch_gnttab_unmap(void *shared, unsigned long nr_gframes); | ||
151 | 153 | ||
152 | extern unsigned long xen_hvm_resume_frames; | 154 | extern unsigned long xen_hvm_resume_frames; |
153 | unsigned int gnttab_max_grant_frames(void); | 155 | unsigned int gnttab_max_grant_frames(void); |