We had a deadlock between: - free_process_memory_range (take lock) -> ihk_mc_pt_free_range -> ... -> remote_flush_tlb_array_cpumask -> "/* Wait for all cores */" and - obj_list_lookup() under fileobj_list_lock that disabled irqs and thus never ack'd the remote flush The rework is quite big but removes the need for the big lock, although devobj and shmobj needed a new smaller lock to be introduced - the new locks are used much more locally and should not cause problems. On the bright side, refcounting being moved to memobj level means we could remove refcounting implemented separately in all object types and simplifies code a bit. Change-Id: I6bc8438a98b1d8edddc91c4ac33c11b88e097ebb
221 lines
4.7 KiB
C
221 lines
4.7 KiB
C
/**
|
|
* \file zeroobj.c
|
|
* License details are found in the file LICENSE.
|
|
* \brief
|
|
* read-only zeroed page object
|
|
* \author Gou Nakamura <go.nakamura.yw@hitachi-solutions.com> \par
|
|
* Copyright (C) 2014 RIKEN AICS
|
|
*/
|
|
/*
|
|
* HISTORY:
|
|
*/
|
|
|
|
#include <ihk/atomic.h>
|
|
#include <ihk/debug.h>
|
|
#include <ihk/lock.h>
|
|
#include <ihk/mm.h>
|
|
#include <errno.h>
|
|
#include <kmalloc.h>
|
|
#include <list.h>
|
|
#include <memobj.h>
|
|
#include <memory.h>
|
|
#include <page.h>
|
|
#include <string.h>
|
|
#include <debug.h>
|
|
|
|
struct zeroobj {
|
|
struct memobj memobj; /* must be first */
|
|
struct list_head page_list;
|
|
};
|
|
|
|
static ihk_spinlock_t the_zeroobj_lock = SPIN_LOCK_UNLOCKED;
|
|
static struct zeroobj *the_zeroobj = NULL; /* singleton */
|
|
|
|
static memobj_get_page_func_t zeroobj_get_page;
|
|
static memobj_free_func_t zeroobj_free;
|
|
|
|
static struct memobj_ops zeroobj_ops = {
|
|
.get_page = &zeroobj_get_page,
|
|
.free = &zeroobj_free,
|
|
};
|
|
|
|
static struct zeroobj *to_zeroobj(struct memobj *memobj)
|
|
{
|
|
return (struct zeroobj *)memobj;
|
|
}
|
|
|
|
static struct memobj *to_memobj(struct zeroobj *zeroobj)
|
|
{
|
|
return &zeroobj->memobj;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* page_list
|
|
*/
|
|
static void page_list_init(struct zeroobj *obj)
|
|
{
|
|
INIT_LIST_HEAD(&obj->page_list);
|
|
return;
|
|
}
|
|
|
|
static void page_list_insert(struct zeroobj *obj, struct page *page)
|
|
{
|
|
list_add(&page->list, &obj->page_list);
|
|
return;
|
|
}
|
|
|
|
static struct page *page_list_first(struct zeroobj *obj)
|
|
{
|
|
if (list_empty(&obj->page_list)) {
|
|
return NULL;
|
|
}
|
|
|
|
return list_first_entry(&obj->page_list, struct page, list);
|
|
}
|
|
|
|
/***********************************************************************
|
|
* zeroobj
|
|
*/
|
|
|
|
static void zeroobj_free(struct memobj *obj)
|
|
{
|
|
kprintf("trying to free zeroobj, this should never happen\n");
|
|
}
|
|
|
|
static int alloc_zeroobj(void)
|
|
{
|
|
int error;
|
|
struct zeroobj *obj = NULL;
|
|
void *virt = NULL;
|
|
uintptr_t phys;
|
|
struct page *page;
|
|
|
|
dkprintf("alloc_zeroobj()\n");
|
|
ihk_mc_spinlock_lock_noirq(&the_zeroobj_lock);
|
|
if (the_zeroobj) {
|
|
error = 0;
|
|
dkprintf("alloc_zeroobj():already. %d\n", error);
|
|
goto out;
|
|
}
|
|
|
|
obj = kmalloc(sizeof(*obj), IHK_MC_AP_NOWAIT);
|
|
if (!obj) {
|
|
error = -ENOMEM;
|
|
ekprintf("alloc_zeroobj():kmalloc failed. %d\n", error);
|
|
goto out;
|
|
}
|
|
|
|
memset(obj, 0, sizeof(*obj));
|
|
obj->memobj.ops = &zeroobj_ops;
|
|
obj->memobj.flags = MF_ZEROOBJ;
|
|
obj->memobj.size = 0;
|
|
ihk_atomic_set(&obj->memobj.refcnt, 2); // never reaches 0
|
|
page_list_init(obj);
|
|
|
|
virt = ihk_mc_alloc_pages(1, IHK_MC_AP_NOWAIT); /* XXX:NYI:large page */
|
|
if (!virt) {
|
|
error = -ENOMEM;
|
|
ekprintf("alloc_zeroobj():alloc pages failed. %d\n", error);
|
|
goto out;
|
|
}
|
|
phys = virt_to_phys(virt);
|
|
page = phys_to_page_insert_hash(phys);
|
|
|
|
if (page->mode != PM_NONE) {
|
|
ekprintf("alloc_zeroobj():"
|
|
"page %p %#lx %d %d %#lx\n",
|
|
page, page_to_phys(page), page->mode,
|
|
page->count, page->offset);
|
|
panic("alloc_zeroobj:dup alloc");
|
|
}
|
|
|
|
memset(virt, 0, PAGE_SIZE);
|
|
page->mode = PM_MAPPED;
|
|
page->offset = 0;
|
|
ihk_atomic_set(&page->count, 1);
|
|
ihk_atomic64_set(&page->mapped, 0);
|
|
page_list_insert(obj, page);
|
|
virt = NULL;
|
|
|
|
error = 0;
|
|
the_zeroobj = obj;
|
|
obj = NULL;
|
|
|
|
out:
|
|
ihk_mc_spinlock_unlock_noirq(&the_zeroobj_lock);
|
|
if (virt) {
|
|
ihk_mc_free_pages(virt, 1);
|
|
}
|
|
if (obj) {
|
|
kfree(obj);
|
|
}
|
|
dkprintf("alloc_zeroobj():%d %p\n", error, the_zeroobj);
|
|
return error;
|
|
}
|
|
|
|
int zeroobj_create(struct memobj **objp)
|
|
{
|
|
int error;
|
|
|
|
dkprintf("zeroobj_create(%p)\n", objp);
|
|
if (!the_zeroobj) {
|
|
error = alloc_zeroobj();
|
|
if (error) {
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
error = 0;
|
|
*objp = to_memobj(the_zeroobj);
|
|
memobj_ref(*objp);
|
|
|
|
out:
|
|
dkprintf("zeroobj_create(%p):%d %p\n", objp, error, *objp);
|
|
return error;
|
|
}
|
|
|
|
static int zeroobj_get_page(struct memobj *memobj, off_t off, int p2align,
|
|
uintptr_t *physp, unsigned long *pflag, uintptr_t virt_addr)
|
|
{
|
|
int error;
|
|
struct zeroobj *obj = to_zeroobj(memobj);
|
|
struct page *page;
|
|
|
|
/* Don't bother about zero page, page fault handler will
|
|
* allocate and clear pages */
|
|
return 0;
|
|
|
|
dkprintf("zeroobj_get_page(%p,%#lx,%d,%p)\n",
|
|
memobj, off, p2align, physp);
|
|
if (off & ~PAGE_MASK) {
|
|
error = -EINVAL;
|
|
ekprintf("zeroobj_get_page(%p,%#lx,%d,%p):invalid argument. %d\n",
|
|
memobj, off, p2align, physp, error);
|
|
goto out;
|
|
}
|
|
if (p2align != PAGE_P2ALIGN) { /* XXX:NYI:large pages */
|
|
error = -ENOMEM;
|
|
dkprintf("zeroobj_get_page(%p,%#lx,%d,%p):large page. %d\n",
|
|
memobj, off, p2align, physp, error);
|
|
goto out;
|
|
}
|
|
|
|
page = page_list_first(obj);
|
|
if (!page) {
|
|
error = -ENOMEM;
|
|
ekprintf("zeroobj_get_page(%p,%#lx,%d,%p):page not found. %d\n",
|
|
memobj, off, p2align, physp, error);
|
|
goto out;
|
|
}
|
|
|
|
ihk_atomic_inc(&page->count);
|
|
|
|
error = 0;
|
|
*physp = page_to_phys(page);
|
|
|
|
out:
|
|
dkprintf("zeroobj_get_page(%p,%#lx,%d,%p):%d\n",
|
|
memobj, off, p2align, physp, error);
|
|
return error;
|
|
}
|