Re: Allocating mock memory resources

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hello

On Sat, Apr 15, 2017 at 11:31 PM, Pavel Roskin <plroskin@xxxxxxxxx> wrote:

> I'm working on a device driver for hardware that is being developed.
> I'm coding against the specification and hoping for the best. It would
> be very handy to have a mock implementation of the hardware so I could
> test the driver against it. In the end, it would be an integration
> test for the driver, which could be useful even after the hardware
> arrives. For example, I could emulate hardware failures and see how
> the driver reacts. Moreover, a driver test framework would be useful
> for others.
>
> One issue I'm facing is creating resources for the device. Luckily,
> the driver only needs memory resources. It should be simple to
> allocate such resources in system RAM, but I could not find a good way
> to do it. Either the resource allocation fails, or the kernel panics
> right away, or it panics when I run "cat /proc/iomem"

In case anybody cares, here's my working solution.

The RAM resource is needed because request_region() cannot traverse
busy RAM region, but request_resource() doesn't check if any resources
are busy. I don't like iterating over resources in a driver, but I
don't know a better approach. I assume that all system RAM resources
are direct children of iomem_resource.

SetPageReserved() is needed to allow ioremap() on the region (by the
way, CamelCase in the kernel code looks so weird).

I'm surprised there is no universal phys_to_page() macro, so I'm using
virtual addresses to iterate over pages.

The only limitation on the driver under test is that it should not be
using request_region() on iomem_resource, as the RAM resource is busy
and cannot be traversed.


static struct resource *fff_get_ram_resource(struct resource *res)
{
    resource_size_t start = res->start;
    resource_size_t end = res->end;
    struct resource *p;

    for (p = iomem_resource.child; p && p->start <= end; p = p->sibling) {
        if (p->end >= start)
            return p;
    }

    return NULL;
}

static int __init fff_emu_alloc_resources(void)
{
    struct page *pg;
    char *pg_base, *p;
    struct resource *ram_res;
    int ret;

    pg = alloc_pages(GFP_KERNEL | __GFP_ZERO, get_order(EMU_MEM_SIZE));
    if (!pg) {
        pr_err("Cannot allocate memory for emulator resource\n");
        return -ENOMEM;
    }

    pg_base = page_to_virt(pg);

    emu_mem.start = page_to_phys(pg);
    emu_mem.end = emu_mem.start + EMU_MEM_SIZE - 1;

    ram_res = fff_get_ram_resource(&emu_mem);
    if (!ram_res) {
        pr_err("no RAM resource found for %pR\n", &emu_mem);
        ret = -ENXIO;
        goto out_mem;
    }

    ret = request_resource(ram_res, &emu_mem);
    if (ret) {
        pr_err("request_resource failed on %pR under %pR: error %d\n",
               &emu_mem, ram_res, ret);
        goto out_mem;
    }

    for (p = pg_base; p < pg_base + EMU_MEM_SIZE; p += PAGE_SIZE)
        SetPageReserved(virt_to_page(p));

    return 0;

out_mem:
    free_pages((unsigned long)pg_base, get_order(EMU_MEM_SIZE));
    return ret;
}

static void fff_emu_free_resources(void)
{
    char *pg_base, *p;

    pg_base = __va(emu_mem.start);
    release_resource(&emu_mem);

    for (p = pg_base; p < pg_base + EMU_MEM_SIZE; p += PAGE_SIZE)
        ClearPageReserved(virt_to_page(p));

    free_pages((unsigned long)pg_base, get_order(EMU_MEM_SIZE));
}



-- 
Regards,
Pavel Roskin

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@xxxxxxxxx.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@xxxxxxxxx";> email@xxxxxxxxx </a>



[Index of Archives]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [IETF Annouce]     [Bugtraq]     [Linux OMAP]     [Linux MIPS]     [eCos]     [Asterisk Internet PBX]     [Linux API]
  Powered by Linux