diff options
author | Daniel Kiper <dkiper@net-space.pl> | 2011-03-08 16:48:24 -0500 |
---|---|---|
committer | Konrad Rzeszutek Wilk <konrad.wilk@oracle.com> | 2011-03-14 11:34:21 -0400 |
commit | 95d2ac4a0c904942a4fecf815781ebd4171e7a30 (patch) | |
tree | 3460ad49cea47eb51dfee5fde9b9743f20f6dd52 /drivers/xen | |
parent | 95170b2e23d4e98843b4833d27fae7bf0910e19c (diff) |
xen/balloon: Protect against CPU exhaust by event/x process
Protect against CPU exhaust by event/x process during
errors by adding some delays in scheduling next event
and retry count limit.
Signed-off-by: Daniel Kiper <dkiper@net-space.pl>
Signed-off-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
Diffstat (limited to 'drivers/xen')
-rw-r--r-- | drivers/xen/balloon.c | 107 |
1 files changed, 90 insertions, 17 deletions
diff --git a/drivers/xen/balloon.c b/drivers/xen/balloon.c index 9206ff7514e7..6cd2530f2330 100644 --- a/drivers/xen/balloon.c +++ b/drivers/xen/balloon.c | |||
@@ -66,6 +66,22 @@ | |||
66 | 66 | ||
67 | #define BALLOON_CLASS_NAME "xen_memory" | 67 | #define BALLOON_CLASS_NAME "xen_memory" |
68 | 68 | ||
69 | /* | ||
70 | * balloon_process() state: | ||
71 | * | ||
72 | * BP_DONE: done or nothing to do, | ||
73 | * BP_EAGAIN: error, go to sleep, | ||
74 | * BP_ECANCELED: error, balloon operation canceled. | ||
75 | */ | ||
76 | |||
77 | enum bp_state { | ||
78 | BP_DONE, | ||
79 | BP_EAGAIN, | ||
80 | BP_ECANCELED | ||
81 | }; | ||
82 | |||
83 | #define RETRY_UNLIMITED 0 | ||
84 | |||
69 | struct balloon_stats { | 85 | struct balloon_stats { |
70 | /* We aim for 'current allocation' == 'target allocation'. */ | 86 | /* We aim for 'current allocation' == 'target allocation'. */ |
71 | unsigned long current_pages; | 87 | unsigned long current_pages; |
@@ -73,6 +89,10 @@ struct balloon_stats { | |||
73 | /* Number of pages in high- and low-memory balloons. */ | 89 | /* Number of pages in high- and low-memory balloons. */ |
74 | unsigned long balloon_low; | 90 | unsigned long balloon_low; |
75 | unsigned long balloon_high; | 91 | unsigned long balloon_high; |
92 | unsigned long schedule_delay; | ||
93 | unsigned long max_schedule_delay; | ||
94 | unsigned long retry_count; | ||
95 | unsigned long max_retry_count; | ||
76 | }; | 96 | }; |
77 | 97 | ||
78 | static DEFINE_MUTEX(balloon_mutex); | 98 | static DEFINE_MUTEX(balloon_mutex); |
@@ -171,6 +191,36 @@ static struct page *balloon_next_page(struct page *page) | |||
171 | return list_entry(next, struct page, lru); | 191 | return list_entry(next, struct page, lru); |
172 | } | 192 | } |
173 | 193 | ||
194 | static enum bp_state update_schedule(enum bp_state state) | ||
195 | { | ||
196 | if (state == BP_DONE) { | ||
197 | balloon_stats.schedule_delay = 1; | ||
198 | balloon_stats.retry_count = 1; | ||
199 | return BP_DONE; | ||
200 | } | ||
201 | |||
202 | pr_info("xen_balloon: Retry count: %lu/%lu\n", balloon_stats.retry_count, | ||
203 | balloon_stats.max_retry_count); | ||
204 | |||
205 | ++balloon_stats.retry_count; | ||
206 | |||
207 | if (balloon_stats.max_retry_count != RETRY_UNLIMITED && | ||
208 | balloon_stats.retry_count > balloon_stats.max_retry_count) { | ||
209 | pr_info("xen_balloon: Retry count limit exceeded\n" | ||
210 | "xen_balloon: Balloon operation canceled\n"); | ||
211 | balloon_stats.schedule_delay = 1; | ||
212 | balloon_stats.retry_count = 1; | ||
213 | return BP_ECANCELED; | ||
214 | } | ||
215 | |||
216 | balloon_stats.schedule_delay <<= 1; | ||
217 | |||
218 | if (balloon_stats.schedule_delay > balloon_stats.max_schedule_delay) | ||
219 | balloon_stats.schedule_delay = balloon_stats.max_schedule_delay; | ||
220 | |||
221 | return BP_EAGAIN; | ||
222 | } | ||
223 | |||
174 | static unsigned long current_target(void) | 224 | static unsigned long current_target(void) |
175 | { | 225 | { |
176 | unsigned long target = balloon_stats.target_pages; | 226 | unsigned long target = balloon_stats.target_pages; |
@@ -183,11 +233,11 @@ static unsigned long current_target(void) | |||
183 | return target; | 233 | return target; |
184 | } | 234 | } |
185 | 235 | ||
186 | static int increase_reservation(unsigned long nr_pages) | 236 | static enum bp_state increase_reservation(unsigned long nr_pages) |
187 | { | 237 | { |
238 | int rc; | ||
188 | unsigned long pfn, i; | 239 | unsigned long pfn, i; |
189 | struct page *page; | 240 | struct page *page; |
190 | long rc; | ||
191 | struct xen_memory_reservation reservation = { | 241 | struct xen_memory_reservation reservation = { |
192 | .address_bits = 0, | 242 | .address_bits = 0, |
193 | .extent_order = 0, | 243 | .extent_order = 0, |
@@ -199,7 +249,10 @@ static int increase_reservation(unsigned long nr_pages) | |||
199 | 249 | ||
200 | page = balloon_first_page(); | 250 | page = balloon_first_page(); |
201 | for (i = 0; i < nr_pages; i++) { | 251 | for (i = 0; i < nr_pages; i++) { |
202 | BUG_ON(page == NULL); | 252 | if (!page) { |
253 | nr_pages = i; | ||
254 | break; | ||
255 | } | ||
203 | frame_list[i] = page_to_pfn(page); | 256 | frame_list[i] = page_to_pfn(page); |
204 | page = balloon_next_page(page); | 257 | page = balloon_next_page(page); |
205 | } | 258 | } |
@@ -207,8 +260,10 @@ static int increase_reservation(unsigned long nr_pages) | |||
207 | set_xen_guest_handle(reservation.extent_start, frame_list); | 260 | set_xen_guest_handle(reservation.extent_start, frame_list); |
208 | reservation.nr_extents = nr_pages; | 261 | reservation.nr_extents = nr_pages; |
209 | rc = HYPERVISOR_memory_op(XENMEM_populate_physmap, &reservation); | 262 | rc = HYPERVISOR_memory_op(XENMEM_populate_physmap, &reservation); |
210 | if (rc < 0) | 263 | if (rc <= 0) { |
211 | goto out; | 264 | pr_info("xen_balloon: %s: Cannot allocate memory\n", __func__); |
265 | return BP_EAGAIN; | ||
266 | } | ||
212 | 267 | ||
213 | for (i = 0; i < rc; i++) { | 268 | for (i = 0; i < rc; i++) { |
214 | page = balloon_retrieve(); | 269 | page = balloon_retrieve(); |
@@ -238,15 +293,14 @@ static int increase_reservation(unsigned long nr_pages) | |||
238 | 293 | ||
239 | balloon_stats.current_pages += rc; | 294 | balloon_stats.current_pages += rc; |
240 | 295 | ||
241 | out: | 296 | return BP_DONE; |
242 | return rc < 0 ? rc : rc != nr_pages; | ||
243 | } | 297 | } |
244 | 298 | ||
245 | static int decrease_reservation(unsigned long nr_pages) | 299 | static enum bp_state decrease_reservation(unsigned long nr_pages) |
246 | { | 300 | { |
301 | enum bp_state state = BP_DONE; | ||
247 | unsigned long pfn, i; | 302 | unsigned long pfn, i; |
248 | struct page *page; | 303 | struct page *page; |
249 | int need_sleep = 0; | ||
250 | int ret; | 304 | int ret; |
251 | struct xen_memory_reservation reservation = { | 305 | struct xen_memory_reservation reservation = { |
252 | .address_bits = 0, | 306 | .address_bits = 0, |
@@ -259,8 +313,9 @@ static int decrease_reservation(unsigned long nr_pages) | |||
259 | 313 | ||
260 | for (i = 0; i < nr_pages; i++) { | 314 | for (i = 0; i < nr_pages; i++) { |
261 | if ((page = alloc_page(GFP_BALLOON)) == NULL) { | 315 | if ((page = alloc_page(GFP_BALLOON)) == NULL) { |
316 | pr_info("xen_balloon: %s: Cannot allocate memory\n", __func__); | ||
262 | nr_pages = i; | 317 | nr_pages = i; |
263 | need_sleep = 1; | 318 | state = BP_EAGAIN; |
264 | break; | 319 | break; |
265 | } | 320 | } |
266 | 321 | ||
@@ -296,7 +351,7 @@ static int decrease_reservation(unsigned long nr_pages) | |||
296 | 351 | ||
297 | balloon_stats.current_pages -= nr_pages; | 352 | balloon_stats.current_pages -= nr_pages; |
298 | 353 | ||
299 | return need_sleep; | 354 | return state; |
300 | } | 355 | } |
301 | 356 | ||
302 | /* | 357 | /* |
@@ -307,27 +362,31 @@ static int decrease_reservation(unsigned long nr_pages) | |||
307 | */ | 362 | */ |
308 | static void balloon_process(struct work_struct *work) | 363 | static void balloon_process(struct work_struct *work) |
309 | { | 364 | { |
310 | int need_sleep = 0; | 365 | enum bp_state state = BP_DONE; |
311 | long credit; | 366 | long credit; |
312 | 367 | ||
313 | mutex_lock(&balloon_mutex); | 368 | mutex_lock(&balloon_mutex); |
314 | 369 | ||
315 | do { | 370 | do { |
316 | credit = current_target() - balloon_stats.current_pages; | 371 | credit = current_target() - balloon_stats.current_pages; |
372 | |||
317 | if (credit > 0) | 373 | if (credit > 0) |
318 | need_sleep = (increase_reservation(credit) != 0); | 374 | state = increase_reservation(credit); |
375 | |||
319 | if (credit < 0) | 376 | if (credit < 0) |
320 | need_sleep = (decrease_reservation(-credit) != 0); | 377 | state = decrease_reservation(-credit); |
378 | |||
379 | state = update_schedule(state); | ||
321 | 380 | ||
322 | #ifndef CONFIG_PREEMPT | 381 | #ifndef CONFIG_PREEMPT |
323 | if (need_resched()) | 382 | if (need_resched()) |
324 | schedule(); | 383 | schedule(); |
325 | #endif | 384 | #endif |
326 | } while ((credit != 0) && !need_sleep); | 385 | } while (credit && state == BP_DONE); |
327 | 386 | ||
328 | /* Schedule more work if there is some still to be done. */ | 387 | /* Schedule more work if there is some still to be done. */ |
329 | if (current_target() != balloon_stats.current_pages) | 388 | if (state == BP_EAGAIN) |
330 | schedule_delayed_work(&balloon_worker, HZ); | 389 | schedule_delayed_work(&balloon_worker, balloon_stats.schedule_delay * HZ); |
331 | 390 | ||
332 | mutex_unlock(&balloon_mutex); | 391 | mutex_unlock(&balloon_mutex); |
333 | } | 392 | } |
@@ -394,6 +453,11 @@ static int __init balloon_init(void) | |||
394 | balloon_stats.balloon_low = 0; | 453 | balloon_stats.balloon_low = 0; |
395 | balloon_stats.balloon_high = 0; | 454 | balloon_stats.balloon_high = 0; |
396 | 455 | ||
456 | balloon_stats.schedule_delay = 1; | ||
457 | balloon_stats.max_schedule_delay = 32; | ||
458 | balloon_stats.retry_count = 1; | ||
459 | balloon_stats.max_retry_count = 16; | ||
460 | |||
397 | register_balloon(&balloon_sysdev); | 461 | register_balloon(&balloon_sysdev); |
398 | 462 | ||
399 | /* | 463 | /* |
@@ -447,6 +511,11 @@ BALLOON_SHOW(current_kb, "%lu\n", PAGES2KB(balloon_stats.current_pages)); | |||
447 | BALLOON_SHOW(low_kb, "%lu\n", PAGES2KB(balloon_stats.balloon_low)); | 511 | BALLOON_SHOW(low_kb, "%lu\n", PAGES2KB(balloon_stats.balloon_low)); |
448 | BALLOON_SHOW(high_kb, "%lu\n", PAGES2KB(balloon_stats.balloon_high)); | 512 | BALLOON_SHOW(high_kb, "%lu\n", PAGES2KB(balloon_stats.balloon_high)); |
449 | 513 | ||
514 | static SYSDEV_ULONG_ATTR(schedule_delay, 0444, balloon_stats.schedule_delay); | ||
515 | static SYSDEV_ULONG_ATTR(max_schedule_delay, 0644, balloon_stats.max_schedule_delay); | ||
516 | static SYSDEV_ULONG_ATTR(retry_count, 0444, balloon_stats.retry_count); | ||
517 | static SYSDEV_ULONG_ATTR(max_retry_count, 0644, balloon_stats.max_retry_count); | ||
518 | |||
450 | static ssize_t show_target_kb(struct sys_device *dev, struct sysdev_attribute *attr, | 519 | static ssize_t show_target_kb(struct sys_device *dev, struct sysdev_attribute *attr, |
451 | char *buf) | 520 | char *buf) |
452 | { | 521 | { |
@@ -508,6 +577,10 @@ static SYSDEV_ATTR(target, S_IRUGO | S_IWUSR, | |||
508 | static struct sysdev_attribute *balloon_attrs[] = { | 577 | static struct sysdev_attribute *balloon_attrs[] = { |
509 | &attr_target_kb, | 578 | &attr_target_kb, |
510 | &attr_target, | 579 | &attr_target, |
580 | &attr_schedule_delay.attr, | ||
581 | &attr_max_schedule_delay.attr, | ||
582 | &attr_retry_count.attr, | ||
583 | &attr_max_retry_count.attr | ||
511 | }; | 584 | }; |
512 | 585 | ||
513 | static struct attribute *balloon_info_attrs[] = { | 586 | static struct attribute *balloon_info_attrs[] = { |