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 | |
| 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>
| -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[] = { |
