aboutsummaryrefslogtreecommitdiffstats
path: root/litmus/uncachedev.c
blob: 06a6a7c179830a913c49f30353e081cbc671e132 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/highmem.h>
#include <asm/page.h>
#include <linux/miscdevice.h>
#include <linux/module.h>

#include <litmus/litmus.h>

/* device for allocating pages not cached by the CPU */

#define UNCACHE_NAME        "litmus/uncache"

void litmus_uncache_vm_open(struct vm_area_struct *vma)
{
}

void litmus_uncache_vm_close(struct vm_area_struct *vma)
{
}

int litmus_uncache_vm_fault(struct vm_area_struct* vma,
							struct vm_fault* vmf)
{
	/* modeled after SG DMA video4linux, but without DMA. */
	/* (see drivers/media/video/videobuf-dma-sg.c) */
	struct page *page;

	page = alloc_page(GFP_USER);
	if (!page)
		return VM_FAULT_OOM;

	clear_user_highpage(page, (unsigned long)vmf->virtual_address);
	vmf->page = page;

	return 0;
}

static struct vm_operations_struct litmus_uncache_vm_ops = {
	.open = litmus_uncache_vm_open,
	.close = litmus_uncache_vm_close,
	.fault = litmus_uncache_vm_fault,
};

static int litmus_uncache_mmap(struct file* filp, struct vm_area_struct* vma)
{
	/* first make sure mapper knows what he's doing */

	/* 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;

	/* cannot be expanded, and is not a "normal" page. */
	vma->vm_flags |= VM_DONTEXPAND;

	/* noncached pages are not explicitly locked in memory (for now). */
	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);

	vma->vm_ops = &litmus_uncache_vm_ops;

	return 0;
}

static struct file_operations litmus_uncache_fops = {
	.owner = THIS_MODULE,
	.mmap  = litmus_uncache_mmap,
};

static struct miscdevice litmus_uncache_dev = {
	.name  = UNCACHE_NAME,
	.minor = MISC_DYNAMIC_MINOR,
	.fops  = &litmus_uncache_fops,
	/* pages are not locked, so there is no reason why
	   anyone cannot allocate an uncache pages */
	.mode  = (S_IRUGO | S_IWUGO),
};

static int __init init_litmus_uncache_dev(void)
{
	int err;

	printk("Initializing LITMUS^RT uncache device.\n");
	err = misc_register(&litmus_uncache_dev);
	if (err)
		printk("Could not allocate %s device (%d).\n", UNCACHE_NAME, err);
	return err;
}

static void __exit exit_litmus_uncache_dev(void)
{
	misc_deregister(&litmus_uncache_dev);
}

module_init(init_litmus_uncache_dev);
module_exit(exit_litmus_uncache_dev);