1 /* -*- C -*-
2 * mmap.c -- memory mapping for the scullv char module
3 *
4 * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet
5 * Copyright (C) 2001 O'Reilly & Associates
6 *
7 * The source code in this file can be freely used, adapted,
8 * and redistributed in source or binary form, so long as an
9 * acknowledgment appears in derived source files. The citation
10 * should list that the code comes from the book "Linux Device
11 * Drivers" by Alessandro Rubini and Jonathan Corbet, published
12 * by O'Reilly & Associates. No warranty is attached;
13 * we cannot take responsibility for errors or fitness for use.
14 *
15 * $Id: _mmap.c.in,v 1.13 2004/10/18 18:07:36 corbet Exp $
16 */
17
18 /* #include <linux/config.h> */
19 #include <linux/module.h>
20
21 #include <linux/mm.h> /* everything */
22 #include <linux/errno.h> /* error codes */
23 #include <linux/fs.h>
24 #include <asm/pgtable.h>
25
26 #include "scullv.h" /* local definitions */
27
28
29 /*
30 * open and close: just keep track of how many times the device is
31 * mapped, to avoid releasing it.
32 */
33
34 void scullv_vma_open(struct vm_area_struct *vma)
35 {
36 struct scullv_dev *dev = vma->vm_private_data;
37
38 dev->vmas++;
39 }
40
41 void scullv_vma_close(struct vm_area_struct *vma)
42 {
43 struct scullv_dev *dev = vma->vm_private_data;
44
45 dev->vmas--;
46 }
47
48 /*
49 * The nopage method: the core of the file. It retrieves the
50 * page required from the scullv device and returns it to the
51 * user. The count for the page must be incremented, because
52 * it is automatically decremented at page unmap.
53 *
54 * For this reason, "order" must be zero. Otherwise, only the first
55 * page has its count incremented, and the allocating module must
56 * release it as a whole block. Therefore, it isn't possible to map
57 * pages from a multipage block: when they are unmapped, their count
58 * is individually decreased, and would drop to 0.
59 */
60
61 struct page *scullv_vma_nopage(struct vm_area_struct *vma,
62 unsigned long address, int *type)
63 {
64 unsigned long offset;
65 struct scullv_dev *ptr, *dev = vma->vm_private_data;
66 struct page *page = NOPAGE_SIGBUS;
67 void *pageptr = NULL; /* default to "missing" */
68
69 down(&dev->sem);
70 offset = (address - vma->vm_start) + (vma->vm_pgoff << PAGE_SHIFT);
71 if (offset >= dev->size) goto out; /* out of range */
72
73 /*
74 * Now retrieve the scullv device from the list,then the page.
75 * If the device has holes, the process receives a SIGBUS when
76 * accessing the hole.
77 */
78 offset >>= PAGE_SHIFT; /* offset is a number of pages */
79 for (ptr = dev; ptr && offset >= dev->qset;) {
80 ptr = ptr->next;
81 offset -= dev->qset;
82 }
83 if (ptr && ptr->data) pageptr = ptr->data[offset];
84 if (!pageptr) goto out; /* hole or end-of-file */
85
86 /*
87 * After scullv lookup, "page" is now the address of the page
88 * needed by the current process. Since it's a vmalloc address,
89 * turn it into a struct page.
90 */
91 page = vmalloc_to_page(pageptr);
92
93 /* got it, now increment the count */
94 get_page(page);
95 if (type)
96 *type = VM_FAULT_MINOR;
97 out:
98 up(&dev->sem);
99 return page;
100 }
101
102
103
104 struct vm_operations_struct scullv_vm_ops = {
105 .open = scullv_vma_open,
106 .close = scullv_vma_close,
107 .nopage = scullv_vma_nopage,
108 };
109
110
111 int scullv_mmap(struct file *filp, struct vm_area_struct *vma)
112 {
113
114 /* don't do anything here: "nopage" will set up page table entries */
115 vma->vm_ops = &scullv_vm_ops;
116 vma->vm_flags |= VM_RESERVED;
117 vma->vm_private_data = filp->private_data;
118 scullv_vma_open(vma);
119 return 0;
120 }
121
122
|
This page was automatically generated by the
LXR engine.
|