in virtio_mem.c [1233:1290]
static void virtio_mem_online_page(struct virtio_mem *vm,
struct page *page, unsigned int order)
{
const unsigned long start = page_to_phys(page);
const unsigned long end = start + PFN_PHYS(1 << order);
unsigned long addr, next, id, sb_id, count;
bool do_online;
/*
* We can get called with any order up to MAX_ORDER - 1. If our
* subblock size is smaller than that and we have a mixture of plugged
* and unplugged subblocks within such a page, we have to process in
* smaller granularity. In that case we'll adjust the order exactly once
* within the loop.
*/
for (addr = start; addr < end; ) {
next = addr + PFN_PHYS(1 << order);
if (vm->in_sbm) {
id = virtio_mem_phys_to_mb_id(addr);
sb_id = virtio_mem_phys_to_sb_id(vm, addr);
count = virtio_mem_phys_to_sb_id(vm, next - 1) - sb_id + 1;
if (virtio_mem_sbm_test_sb_plugged(vm, id, sb_id, count)) {
/* Fully plugged. */
do_online = true;
} else if (count == 1 ||
virtio_mem_sbm_test_sb_unplugged(vm, id, sb_id, count)) {
/* Fully unplugged. */
do_online = false;
} else {
/*
* Mixture, process sub-blocks instead. This
* will be at least the size of a pageblock.
* We'll run into this case exactly once.
*/
order = ilog2(vm->sbm.sb_size) - PAGE_SHIFT;
do_online = virtio_mem_sbm_test_sb_plugged(vm, id, sb_id, 1);
continue;
}
} else {
/*
* If the whole block is marked fake offline, keep
* everything that way.
*/
id = virtio_mem_phys_to_bb_id(vm, addr);
do_online = virtio_mem_bbm_get_bb_state(vm, id) !=
VIRTIO_MEM_BBM_BB_FAKE_OFFLINE;
}
if (do_online)
generic_online_page(pfn_to_page(PFN_DOWN(addr)), order);
else
virtio_mem_set_fake_offline(PFN_DOWN(addr), 1 << order,
false);
addr = next;
}
}