aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/xen/grant-table.c
diff options
context:
space:
mode:
authorMatt Wilson <msw@amazon.com>2013-01-15 08:21:27 -0500
committerKonrad Rzeszutek Wilk <konrad.wilk@oracle.com>2013-01-15 15:56:55 -0500
commitd0b4d64aadb9f4a90669848de9ef3819050a98cd (patch)
tree11cd715120cc51834434ca04a541a7513a335379 /drivers/xen/grant-table.c
parent6337a23992826e68257fba51267cb6996439520d (diff)
xen/grant-table: correctly initialize grant table version 1
Commit 85ff6acb075a484780b3d763fdf41596d8fc0970 (xen/granttable: Grant tables V2 implementation) changed the GREFS_PER_GRANT_FRAME macro from a constant to a conditional expression. The expression depends on grant_table_version being appropriately set. Unfortunately, at init time grant_table_version will be 0. The GREFS_PER_GRANT_FRAME conditional expression checks for "grant_table_version == 1", and therefore returns the number of grant references per frame for v2. This causes gnttab_init() to allocate fewer pages for gnttab_list, as a frame can old half the number of v2 entries than v1 entries. After gnttab_resume() is called, grant_table_version is appropriately set. nr_init_grefs will then be miscalculated and gnttab_free_count will hold a value larger than the actual number of free gref entries. If a guest is heavily utilizing improperly initialized v1 grant tables, memory corruption can occur. One common manifestation is corruption of the vmalloc list, resulting in a poisoned pointer derefrence when accessing /proc/meminfo or /proc/vmallocinfo: [ 40.770064] BUG: unable to handle kernel paging request at 0000200200001407 [ 40.770083] IP: [<ffffffff811a6fb0>] get_vmalloc_info+0x70/0x110 [ 40.770102] PGD 0 [ 40.770107] Oops: 0000 [#1] SMP [ 40.770114] CPU 10 This patch introduces a static variable, grefs_per_grant_frame, to cache the calculated value. gnttab_init() now calls gnttab_request_version() early so that grant_table_version and grefs_per_grant_frame can be appropriately set. A few BUG_ON()s have been added to prevent this type of bug from reoccurring in the future. Signed-off-by: Matt Wilson <msw@amazon.com> Reviewed-and-Tested-by: Steven Noonan <snoonan@amazon.com> Acked-by: Ian Campbell <Ian.Campbell@citrix.com> Cc: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com> Cc: Annie Li <annie.li@oracle.com> Cc: xen-devel@lists.xen.org Cc: linux-kernel@vger.kernel.org Cc: stable@vger.kernel.org # v3.3 and newer Signed-off-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
Diffstat (limited to 'drivers/xen/grant-table.c')
-rw-r--r--drivers/xen/grant-table.c48
1 files changed, 29 insertions, 19 deletions
diff --git a/drivers/xen/grant-table.c b/drivers/xen/grant-table.c
index b91f14e83164..95ce9d02ceca 100644
--- a/drivers/xen/grant-table.c
+++ b/drivers/xen/grant-table.c
@@ -56,10 +56,6 @@
56/* External tools reserve first few grant table entries. */ 56/* External tools reserve first few grant table entries. */
57#define NR_RESERVED_ENTRIES 8 57#define NR_RESERVED_ENTRIES 8
58#define GNTTAB_LIST_END 0xffffffff 58#define GNTTAB_LIST_END 0xffffffff
59#define GREFS_PER_GRANT_FRAME \
60(grant_table_version == 1 ? \
61(PAGE_SIZE / sizeof(struct grant_entry_v1)) : \
62(PAGE_SIZE / sizeof(union grant_entry_v2)))
63 59
64static grant_ref_t **gnttab_list; 60static grant_ref_t **gnttab_list;
65static unsigned int nr_grant_frames; 61static unsigned int nr_grant_frames;
@@ -154,6 +150,7 @@ static struct gnttab_ops *gnttab_interface;
154static grant_status_t *grstatus; 150static grant_status_t *grstatus;
155 151
156static int grant_table_version; 152static int grant_table_version;
153static int grefs_per_grant_frame;
157 154
158static struct gnttab_free_callback *gnttab_free_callback_list; 155static struct gnttab_free_callback *gnttab_free_callback_list;
159 156
@@ -767,12 +764,14 @@ static int grow_gnttab_list(unsigned int more_frames)
767 unsigned int new_nr_grant_frames, extra_entries, i; 764 unsigned int new_nr_grant_frames, extra_entries, i;
768 unsigned int nr_glist_frames, new_nr_glist_frames; 765 unsigned int nr_glist_frames, new_nr_glist_frames;
769 766
767 BUG_ON(grefs_per_grant_frame == 0);
768
770 new_nr_grant_frames = nr_grant_frames + more_frames; 769 new_nr_grant_frames = nr_grant_frames + more_frames;
771 extra_entries = more_frames * GREFS_PER_GRANT_FRAME; 770 extra_entries = more_frames * grefs_per_grant_frame;
772 771
773 nr_glist_frames = (nr_grant_frames * GREFS_PER_GRANT_FRAME + RPP - 1) / RPP; 772 nr_glist_frames = (nr_grant_frames * grefs_per_grant_frame + RPP - 1) / RPP;
774 new_nr_glist_frames = 773 new_nr_glist_frames =
775 (new_nr_grant_frames * GREFS_PER_GRANT_FRAME + RPP - 1) / RPP; 774 (new_nr_grant_frames * grefs_per_grant_frame + RPP - 1) / RPP;
776 for (i = nr_glist_frames; i < new_nr_glist_frames; i++) { 775 for (i = nr_glist_frames; i < new_nr_glist_frames; i++) {
777 gnttab_list[i] = (grant_ref_t *)__get_free_page(GFP_ATOMIC); 776 gnttab_list[i] = (grant_ref_t *)__get_free_page(GFP_ATOMIC);
778 if (!gnttab_list[i]) 777 if (!gnttab_list[i])
@@ -780,12 +779,12 @@ static int grow_gnttab_list(unsigned int more_frames)
780 } 779 }
781 780
782 781
783 for (i = GREFS_PER_GRANT_FRAME * nr_grant_frames; 782 for (i = grefs_per_grant_frame * nr_grant_frames;
784 i < GREFS_PER_GRANT_FRAME * new_nr_grant_frames - 1; i++) 783 i < grefs_per_grant_frame * new_nr_grant_frames - 1; i++)
785 gnttab_entry(i) = i + 1; 784 gnttab_entry(i) = i + 1;
786 785
787 gnttab_entry(i) = gnttab_free_head; 786 gnttab_entry(i) = gnttab_free_head;
788 gnttab_free_head = GREFS_PER_GRANT_FRAME * nr_grant_frames; 787 gnttab_free_head = grefs_per_grant_frame * nr_grant_frames;
789 gnttab_free_count += extra_entries; 788 gnttab_free_count += extra_entries;
790 789
791 nr_grant_frames = new_nr_grant_frames; 790 nr_grant_frames = new_nr_grant_frames;
@@ -957,7 +956,8 @@ EXPORT_SYMBOL_GPL(gnttab_unmap_refs);
957 956
958static unsigned nr_status_frames(unsigned nr_grant_frames) 957static unsigned nr_status_frames(unsigned nr_grant_frames)
959{ 958{
960 return (nr_grant_frames * GREFS_PER_GRANT_FRAME + SPP - 1) / SPP; 959 BUG_ON(grefs_per_grant_frame == 0);
960 return (nr_grant_frames * grefs_per_grant_frame + SPP - 1) / SPP;
961} 961}
962 962
963static int gnttab_map_frames_v1(xen_pfn_t *frames, unsigned int nr_gframes) 963static int gnttab_map_frames_v1(xen_pfn_t *frames, unsigned int nr_gframes)
@@ -1115,6 +1115,7 @@ static void gnttab_request_version(void)
1115 rc = HYPERVISOR_grant_table_op(GNTTABOP_set_version, &gsv, 1); 1115 rc = HYPERVISOR_grant_table_op(GNTTABOP_set_version, &gsv, 1);
1116 if (rc == 0 && gsv.version == 2) { 1116 if (rc == 0 && gsv.version == 2) {
1117 grant_table_version = 2; 1117 grant_table_version = 2;
1118 grefs_per_grant_frame = PAGE_SIZE / sizeof(union grant_entry_v2);
1118 gnttab_interface = &gnttab_v2_ops; 1119 gnttab_interface = &gnttab_v2_ops;
1119 } else if (grant_table_version == 2) { 1120 } else if (grant_table_version == 2) {
1120 /* 1121 /*
@@ -1127,17 +1128,17 @@ static void gnttab_request_version(void)
1127 panic("we need grant tables version 2, but only version 1 is available"); 1128 panic("we need grant tables version 2, but only version 1 is available");
1128 } else { 1129 } else {
1129 grant_table_version = 1; 1130 grant_table_version = 1;
1131 grefs_per_grant_frame = PAGE_SIZE / sizeof(struct grant_entry_v1);
1130 gnttab_interface = &gnttab_v1_ops; 1132 gnttab_interface = &gnttab_v1_ops;
1131 } 1133 }
1132 printk(KERN_INFO "Grant tables using version %d layout.\n", 1134 printk(KERN_INFO "Grant tables using version %d layout.\n",
1133 grant_table_version); 1135 grant_table_version);
1134} 1136}
1135 1137
1136int gnttab_resume(void) 1138static int gnttab_setup(void)
1137{ 1139{
1138 unsigned int max_nr_gframes; 1140 unsigned int max_nr_gframes;
1139 1141
1140 gnttab_request_version();
1141 max_nr_gframes = gnttab_max_grant_frames(); 1142 max_nr_gframes = gnttab_max_grant_frames();
1142 if (max_nr_gframes < nr_grant_frames) 1143 if (max_nr_gframes < nr_grant_frames)
1143 return -ENOSYS; 1144 return -ENOSYS;
@@ -1160,6 +1161,12 @@ int gnttab_resume(void)
1160 return 0; 1161 return 0;
1161} 1162}
1162 1163
1164int gnttab_resume(void)
1165{
1166 gnttab_request_version();
1167 return gnttab_setup();
1168}
1169
1163int gnttab_suspend(void) 1170int gnttab_suspend(void)
1164{ 1171{
1165 gnttab_interface->unmap_frames(); 1172 gnttab_interface->unmap_frames();
@@ -1171,9 +1178,10 @@ static int gnttab_expand(unsigned int req_entries)
1171 int rc; 1178 int rc;
1172 unsigned int cur, extra; 1179 unsigned int cur, extra;
1173 1180
1181 BUG_ON(grefs_per_grant_frame == 0);
1174 cur = nr_grant_frames; 1182 cur = nr_grant_frames;
1175 extra = ((req_entries + (GREFS_PER_GRANT_FRAME-1)) / 1183 extra = ((req_entries + (grefs_per_grant_frame-1)) /
1176 GREFS_PER_GRANT_FRAME); 1184 grefs_per_grant_frame);
1177 if (cur + extra > gnttab_max_grant_frames()) 1185 if (cur + extra > gnttab_max_grant_frames())
1178 return -ENOSPC; 1186 return -ENOSPC;
1179 1187
@@ -1191,21 +1199,23 @@ int gnttab_init(void)
1191 unsigned int nr_init_grefs; 1199 unsigned int nr_init_grefs;
1192 int ret; 1200 int ret;
1193 1201
1202 gnttab_request_version();
1194 nr_grant_frames = 1; 1203 nr_grant_frames = 1;
1195 boot_max_nr_grant_frames = __max_nr_grant_frames(); 1204 boot_max_nr_grant_frames = __max_nr_grant_frames();
1196 1205
1197 /* Determine the maximum number of frames required for the 1206 /* Determine the maximum number of frames required for the
1198 * grant reference free list on the current hypervisor. 1207 * grant reference free list on the current hypervisor.
1199 */ 1208 */
1209 BUG_ON(grefs_per_grant_frame == 0);
1200 max_nr_glist_frames = (boot_max_nr_grant_frames * 1210 max_nr_glist_frames = (boot_max_nr_grant_frames *
1201 GREFS_PER_GRANT_FRAME / RPP); 1211 grefs_per_grant_frame / RPP);
1202 1212
1203 gnttab_list = kmalloc(max_nr_glist_frames * sizeof(grant_ref_t *), 1213 gnttab_list = kmalloc(max_nr_glist_frames * sizeof(grant_ref_t *),
1204 GFP_KERNEL); 1214 GFP_KERNEL);
1205 if (gnttab_list == NULL) 1215 if (gnttab_list == NULL)
1206 return -ENOMEM; 1216 return -ENOMEM;
1207 1217
1208 nr_glist_frames = (nr_grant_frames * GREFS_PER_GRANT_FRAME + RPP - 1) / RPP; 1218 nr_glist_frames = (nr_grant_frames * grefs_per_grant_frame + RPP - 1) / RPP;
1209 for (i = 0; i < nr_glist_frames; i++) { 1219 for (i = 0; i < nr_glist_frames; i++) {
1210 gnttab_list[i] = (grant_ref_t *)__get_free_page(GFP_KERNEL); 1220 gnttab_list[i] = (grant_ref_t *)__get_free_page(GFP_KERNEL);
1211 if (gnttab_list[i] == NULL) { 1221 if (gnttab_list[i] == NULL) {
@@ -1214,12 +1224,12 @@ int gnttab_init(void)
1214 } 1224 }
1215 } 1225 }
1216 1226
1217 if (gnttab_resume() < 0) { 1227 if (gnttab_setup() < 0) {
1218 ret = -ENODEV; 1228 ret = -ENODEV;
1219 goto ini_nomem; 1229 goto ini_nomem;
1220 } 1230 }
1221 1231
1222 nr_init_grefs = nr_grant_frames * GREFS_PER_GRANT_FRAME; 1232 nr_init_grefs = nr_grant_frames * grefs_per_grant_frame;
1223 1233
1224 for (i = NR_RESERVED_ENTRIES; i < nr_init_grefs - 1; i++) 1234 for (i = NR_RESERVED_ENTRIES; i < nr_init_grefs - 1; i++)
1225 gnttab_entry(i) = i + 1; 1235 gnttab_entry(i) = i + 1;