diff options
Diffstat (limited to 'drivers/s390/char')
-rw-r--r-- | drivers/s390/char/sclp_cmd.c | 48 |
1 files changed, 46 insertions, 2 deletions
diff --git a/drivers/s390/char/sclp_cmd.c b/drivers/s390/char/sclp_cmd.c index 6e14999f9e8f..7be782116dab 100644 --- a/drivers/s390/char/sclp_cmd.c +++ b/drivers/s390/char/sclp_cmd.c | |||
@@ -315,10 +315,29 @@ static int sclp_mem_change_state(unsigned long start, unsigned long size, | |||
315 | rc |= sclp_assign_storage(incr->rn); | 315 | rc |= sclp_assign_storage(incr->rn); |
316 | else | 316 | else |
317 | sclp_unassign_storage(incr->rn); | 317 | sclp_unassign_storage(incr->rn); |
318 | if (rc == 0) | ||
319 | incr->standby = online ? 0 : 1; | ||
318 | } | 320 | } |
319 | return rc ? -EIO : 0; | 321 | return rc ? -EIO : 0; |
320 | } | 322 | } |
321 | 323 | ||
324 | static bool contains_standby_increment(unsigned long start, unsigned long end) | ||
325 | { | ||
326 | struct memory_increment *incr; | ||
327 | unsigned long istart; | ||
328 | |||
329 | list_for_each_entry(incr, &sclp_mem_list, list) { | ||
330 | istart = rn2addr(incr->rn); | ||
331 | if (end - 1 < istart) | ||
332 | continue; | ||
333 | if (start > istart + sclp_rzm - 1) | ||
334 | continue; | ||
335 | if (incr->standby) | ||
336 | return true; | ||
337 | } | ||
338 | return false; | ||
339 | } | ||
340 | |||
322 | static int sclp_mem_notifier(struct notifier_block *nb, | 341 | static int sclp_mem_notifier(struct notifier_block *nb, |
323 | unsigned long action, void *data) | 342 | unsigned long action, void *data) |
324 | { | 343 | { |
@@ -334,8 +353,16 @@ static int sclp_mem_notifier(struct notifier_block *nb, | |||
334 | for_each_clear_bit(id, sclp_storage_ids, sclp_max_storage_id + 1) | 353 | for_each_clear_bit(id, sclp_storage_ids, sclp_max_storage_id + 1) |
335 | sclp_attach_storage(id); | 354 | sclp_attach_storage(id); |
336 | switch (action) { | 355 | switch (action) { |
337 | case MEM_ONLINE: | ||
338 | case MEM_GOING_OFFLINE: | 356 | case MEM_GOING_OFFLINE: |
357 | /* | ||
358 | * We do not allow to set memory blocks offline that contain | ||
359 | * standby memory. This is done to simplify the "memory online" | ||
360 | * case. | ||
361 | */ | ||
362 | if (contains_standby_increment(start, start + size)) | ||
363 | rc = -EPERM; | ||
364 | break; | ||
365 | case MEM_ONLINE: | ||
339 | case MEM_CANCEL_OFFLINE: | 366 | case MEM_CANCEL_OFFLINE: |
340 | break; | 367 | break; |
341 | case MEM_GOING_ONLINE: | 368 | case MEM_GOING_ONLINE: |
@@ -361,6 +388,21 @@ static struct notifier_block sclp_mem_nb = { | |||
361 | .notifier_call = sclp_mem_notifier, | 388 | .notifier_call = sclp_mem_notifier, |
362 | }; | 389 | }; |
363 | 390 | ||
391 | static void __init align_to_block_size(unsigned long long *start, | ||
392 | unsigned long long *size) | ||
393 | { | ||
394 | unsigned long long start_align, size_align, alignment; | ||
395 | |||
396 | alignment = memory_block_size_bytes(); | ||
397 | start_align = roundup(*start, alignment); | ||
398 | size_align = rounddown(*start + *size, alignment) - start_align; | ||
399 | |||
400 | pr_info("Standby memory at 0x%llx (%lluM of %lluM usable)\n", | ||
401 | *start, size_align >> 20, *size >> 20); | ||
402 | *start = start_align; | ||
403 | *size = size_align; | ||
404 | } | ||
405 | |||
364 | static void __init add_memory_merged(u16 rn) | 406 | static void __init add_memory_merged(u16 rn) |
365 | { | 407 | { |
366 | static u16 first_rn, num; | 408 | static u16 first_rn, num; |
@@ -382,7 +424,9 @@ static void __init add_memory_merged(u16 rn) | |||
382 | goto skip_add; | 424 | goto skip_add; |
383 | if (memory_end_set && (start + size > memory_end)) | 425 | if (memory_end_set && (start + size > memory_end)) |
384 | size = memory_end - start; | 426 | size = memory_end - start; |
385 | add_memory(0, start, size); | 427 | align_to_block_size(&start, &size); |
428 | if (size) | ||
429 | add_memory(0, start, size); | ||
386 | skip_add: | 430 | skip_add: |
387 | first_rn = rn; | 431 | first_rn = rn; |
388 | num = 1; | 432 | num = 1; |