From b973c95c86e6710c913c01a67013605f68a3c2c3 Mon Sep 17 00:00:00 2001 From: "Bjoern B. Brandenburg" Date: Wed, 3 Feb 2010 19:35:20 -0500 Subject: Add virtual LITMUS^RT control device. This device only supports mmap()'ing a single page. This page is shared RW between the kernel and userspace. It is inteded to allow near-zero-overhead communication between the kernel and userspace. It's first use will be a proper implementation of user-signaled non-preemptable section support. --- include/litmus/rt_param.h | 27 +++++++++ litmus/Makefile | 1 + litmus/ctrldev.c | 150 ++++++++++++++++++++++++++++++++++++++++++++++ litmus/litmus.c | 35 ++++++++--- 4 files changed, 206 insertions(+), 7 deletions(-) create mode 100644 litmus/ctrldev.c diff --git a/include/litmus/rt_param.h b/include/litmus/rt_param.h index e20427846273..9353251fb30e 100644 --- a/include/litmus/rt_param.h +++ b/include/litmus/rt_param.h @@ -35,6 +35,30 @@ struct rt_task { task_class_t cls; }; +/* The definition of the data that is shared between the kernel and real-time + * tasks via a shared page (see litmus/ctrldev.c). + * + * WARNING: User space can write to this, so don't trust + * the correctness of the fields! + * + * This servees two purposes: to enable efficient signaling + * of non-preemptive sections (user->kernel) and + * delayed preemptions (kernel->user), and to export + * some real-time relevant statistics such as preemption and + * migration data to user space. We can't use a device to export + * statistics because we want to avoid system call overhead when + * determining preemption/migration overheads). + */ +struct control_page { + /* Is the task currently in a non-preemptive section? */ + int np_flag; + /* Should the task call into the kernel when it leaves + * its non-preemptive section? */ + int delayed_preemption; + + /* to be extended */ +}; + /* don't export internal data structures to user space (liblitmus) */ #ifdef __KERNEL__ @@ -163,6 +187,9 @@ struct rt_param { /* Used by rt_domain to queue task in release list. */ struct list_head list; + + /* Pointer to the page shared between userspace and kernel. */ + struct control_page * ctrl_page; }; /* Possible RT flags */ diff --git a/litmus/Makefile b/litmus/Makefile index 3b3f1af8d5b4..ff4eb8a7b6c4 100644 --- a/litmus/Makefile +++ b/litmus/Makefile @@ -11,6 +11,7 @@ obj-y = sched_plugin.o litmus.o \ srp.o \ fmlp.o \ bheap.o \ + ctrldev.o \ sched_gsn_edf.o \ sched_psn_edf.o \ sched_pfair.o diff --git a/litmus/ctrldev.c b/litmus/ctrldev.c new file mode 100644 index 000000000000..238da453228d --- /dev/null +++ b/litmus/ctrldev.c @@ -0,0 +1,150 @@ +#include +#include +#include +#include +#include + +#include + +/* only one page for now, but we might want to add a RO version at some point */ +#define CTRL_MINOR_COUNT 1 +#define CTRL_NAME "litmus/ctrl" + +/* allocate t->rt_param.ctrl_page*/ +static int alloc_ctrl_page(struct task_struct *t) +{ + int err = 0; + + /* only allocate if the task doesn't have one yet */ + if (!tsk_rt(t)->ctrl_page) { + tsk_rt(t)->ctrl_page = (void*) get_zeroed_page(GFP_KERNEL); + if (!tsk_rt(t)->ctrl_page) + err = -ENOMEM; + /* will get de-allocated in task teardown */ + TRACE_TASK(t, "%s ctrl_page = %p\n", __FUNCTION__, + tsk_rt(t)->ctrl_page); + } + return err; +} + +static int map_ctrl_page(struct task_struct *t, struct vm_area_struct* vma) +{ + int err; + unsigned long pfn; + + struct page* ctrl = virt_to_page(tsk_rt(t)->ctrl_page); + + /* Increase ref count. Is decreased when vma is destroyed. */ + get_page(ctrl); + + /* compute page frame number */ + pfn = page_to_pfn(ctrl); + + TRACE_CUR(CTRL_NAME + ": mapping %p (pfn:%lx, %lx) to 0x%lx (prot:%lx)\n", + tsk_rt(t)->ctrl_page, pfn, page_to_pfn(ctrl), vma->vm_start, + vma->vm_page_prot); + + /* Map it into the vma. Make sure to use PAGE_SHARED, otherwise + * userspace actually gets a copy-on-write page. */ + err = remap_pfn_range(vma, vma->vm_start, pfn, PAGE_SIZE, PAGE_SHARED); + + if (err) + TRACE_CUR(CTRL_NAME ": remap_pfn_range() failed (%d)\n", err); + + return err; +} + +static void litmus_ctrl_vm_close(struct vm_area_struct* vma) +{ + TRACE_CUR("%s flags=0x%x prot=0x%x\n", __FUNCTION__, + vma->vm_flags, vma->vm_page_prot); + + TRACE_CUR(CTRL_NAME + ": %p:%p vma:%p vma->vm_private_data:%p closed.\n", + (void*) vma->vm_start, (void*) vma->vm_end, vma, + vma->vm_private_data, current->comm, + current->pid); +} + +static int litmus_ctrl_vm_fault(struct vm_area_struct* vma, + struct vm_fault* vmf) +{ + /* This function should never be called, since + * all pages should have been mapped by mmap() + * already. */ + TRACE_CUR("%s flags=0x%x\n", __FUNCTION__, vma->vm_flags); + + /* nope, you only get one page */ + return VM_FAULT_SIGBUS; +} + +static struct vm_operations_struct litmus_ctrl_vm_ops = { + .close = litmus_ctrl_vm_close, + .fault = litmus_ctrl_vm_fault, +}; + +static int litmus_ctrl_mmap(struct file* filp, struct vm_area_struct* vma) +{ + int err = 0; + + /* first make sure mapper knows what he's doing */ + + /* you can only get one page */ + if (vma->vm_end - vma->vm_start != PAGE_SIZE) + return -EINVAL; + + /* you can only map the "first" page */ + if (vma->vm_pgoff != 0) + return -EINVAL; + + /* you can't share it with anyone */ + if (vma->vm_flags & (VM_MAYSHARE | VM_SHARED)) + return -EINVAL; + + vma->vm_ops = &litmus_ctrl_vm_ops; + /* this mapping should not be kept across forks, + * and cannot be expanded */ + vma->vm_flags |= VM_DONTCOPY | VM_DONTEXPAND; + + err = alloc_ctrl_page(current); + if (!err) + err = map_ctrl_page(current, vma); + + TRACE_CUR("%s flags=0x%x prot=0x%lx\n", + __FUNCTION__, vma->vm_flags, vma->vm_page_prot); + + return err; +} + +static struct file_operations litmus_ctrl_fops = { + .owner = THIS_MODULE, + .mmap = litmus_ctrl_mmap, +}; + +static struct miscdevice litmus_ctrl_dev = { + .name = CTRL_NAME, + .minor = MISC_DYNAMIC_MINOR, + .fops = &litmus_ctrl_fops, +}; + +static int __init init_litmus_ctrl_dev(void) +{ + int err; + + BUILD_BUG_ON(sizeof(struct control_page) > PAGE_SIZE); + + printk("Initializing LITMUS^RT control device.\n"); + err = misc_register(&litmus_ctrl_dev); + if (err) + printk("Could not allocate %s device (%d).\n", CTRL_NAME, err); + return err; +} + +static void __exit exit_litmus_ctrl_dev(void) +{ + misc_deregister(&litmus_ctrl_dev); +} + +module_init(init_litmus_ctrl_dev); +module_exit(exit_litmus_ctrl_dev); diff --git a/litmus/litmus.c b/litmus/litmus.c index d31a2ba030e4..589062f8ab87 100644 --- a/litmus/litmus.c +++ b/litmus/litmus.c @@ -268,12 +268,13 @@ asmlinkage long sys_null_call(cycles_t __user *ts) static void reinit_litmus_state(struct task_struct* p, int restore) { struct rt_task user_config = {}; - __user short *np_flag = NULL; + void* ctrl_page = NULL; if (restore) { - /* Safe user-space provided configuration data. */ + /* Safe user-space provided configuration data. + * and allocated page. */ user_config = p->rt_param.task_params; - np_flag = p->rt_param.np_flag; + ctrl_page = p->rt_param.ctrl_page; } /* We probably should not be inheriting any task's priority @@ -282,7 +283,7 @@ static void reinit_litmus_state(struct task_struct* p, int restore) WARN_ON(p->rt_param.inh_task); /* We need to restore the priority of the task. */ -// __setscheduler(p, p->rt_param.old_policy, p->rt_param.old_prio); +// __setscheduler(p, p->rt_param.old_policy, p->rt_param.old_prio); XXX why is this commented? /* Cleanup everything else. */ memset(&p->rt_param, 0, sizeof(p->rt_param)); @@ -290,7 +291,7 @@ static void reinit_litmus_state(struct task_struct* p, int restore) /* Restore preserved fields. */ if (restore) { p->rt_param.task_params = user_config; - p->rt_param.np_flag = np_flag; + p->rt_param.ctrl_page = ctrl_page; } } @@ -412,8 +413,11 @@ out: void litmus_fork(struct task_struct* p) { if (is_realtime(p)) - /* clean out any litmus related state, don't preserve anything*/ + /* clean out any litmus related state, don't preserve anything */ reinit_litmus_state(p, 0); + else + /* non-rt tasks might have ctrl_page set */ + tsk_rt(p)->ctrl_page = NULL; } /* Called upon execve(). @@ -426,12 +430,29 @@ void litmus_exec(void) if (is_realtime(p)) { WARN_ON(p->rt_param.inh_task); - p->rt_param.np_flag = NULL; + if (tsk_rt(p)->ctrl_page) { + free_page((unsigned long) tsk_rt(p)->ctrl_page); + tsk_rt(p)->ctrl_page = NULL; + } } } void exit_litmus(struct task_struct *dead_tsk) { + /* We also allow non-RT tasks to + * allocate control pages to allow + * measurements with non-RT tasks. + * So check if we need to free the page + * in any case. + */ + if (tsk_rt(dead_tsk)->ctrl_page) { + TRACE_TASK(dead_tsk, + "freeing ctrl_page %p\n", + tsk_rt(dead_tsk)->ctrl_page); + free_page((unsigned long) tsk_rt(dead_tsk)->ctrl_page); + } + + /* main cleanup only for RT tasks */ if (is_realtime(dead_tsk)) litmus_exit_task(dead_tsk); } -- cgit v1.2.2