This commit was manufactured by cvs2svn to create branch 'vserver'.
[linux-2.6.git] / fs / relayfs / buffers.c
1 /*
2  * RelayFS buffer management code.
3  *
4  * Copyright (C) 2002-2005 - Tom Zanussi (zanussi@us.ibm.com), IBM Corp
5  * Copyright (C) 1999-2005 - Karim Yaghmour (karim@opersys.com)
6  *
7  * This file is released under the GPL.
8  */
9
10 #include <linux/module.h>
11 #include <linux/vmalloc.h>
12 #include <linux/mm.h>
13 #include <linux/relayfs_fs.h>
14 #include "relay.h"
15 #include "buffers.h"
16
17 /*
18  * close() vm_op implementation for relayfs file mapping.
19  */
20 static void relay_file_mmap_close(struct vm_area_struct *vma)
21 {
22         struct rchan_buf *buf = vma->vm_private_data;
23         buf->chan->cb->buf_unmapped(buf, vma->vm_file);
24 }
25
26 /*
27  * nopage() vm_op implementation for relayfs file mapping.
28  */
29 static struct page *relay_buf_nopage(struct vm_area_struct *vma,
30                                      unsigned long address,
31                                      int *type)
32 {
33         struct page *page;
34         struct rchan_buf *buf = vma->vm_private_data;
35         unsigned long offset = address - vma->vm_start;
36
37         if (address > vma->vm_end)
38                 return NOPAGE_SIGBUS; /* Disallow mremap */
39         if (!buf)
40                 return NOPAGE_OOM;
41
42         page = vmalloc_to_page(buf->start + offset);
43         if (!page)
44                 return NOPAGE_OOM;
45         get_page(page);
46
47         if (type)
48                 *type = VM_FAULT_MINOR;
49
50         return page;
51 }
52
53 /*
54  * vm_ops for relay file mappings.
55  */
56 static struct vm_operations_struct relay_file_mmap_ops = {
57         .nopage = relay_buf_nopage,
58         .close = relay_file_mmap_close,
59 };
60
61 /**
62  *      relay_mmap_buf: - mmap channel buffer to process address space
63  *      @buf: relay channel buffer
64  *      @vma: vm_area_struct describing memory to be mapped
65  *
66  *      Returns 0 if ok, negative on error
67  *
68  *      Caller should already have grabbed mmap_sem.
69  */
70 int relay_mmap_buf(struct rchan_buf *buf, struct vm_area_struct *vma)
71 {
72         unsigned long length = vma->vm_end - vma->vm_start;
73         struct file *filp = vma->vm_file;
74
75         if (!buf)
76                 return -EBADF;
77
78         if (length != (unsigned long)buf->chan->alloc_size)
79                 return -EINVAL;
80
81         vma->vm_ops = &relay_file_mmap_ops;
82         vma->vm_private_data = buf;
83         buf->chan->cb->buf_mapped(buf, filp);
84
85         return 0;
86 }
87
88 /**
89  *      relay_alloc_buf - allocate a channel buffer
90  *      @buf: the buffer struct
91  *      @size: total size of the buffer
92  *
93  *      Returns a pointer to the resulting buffer, NULL if unsuccessful
94  */
95 static void *relay_alloc_buf(struct rchan_buf *buf, unsigned long size)
96 {
97         void *mem;
98         unsigned int i, j, n_pages;
99
100         size = PAGE_ALIGN(size);
101         n_pages = size >> PAGE_SHIFT;
102
103         buf->page_array = kcalloc(n_pages, sizeof(struct page *), GFP_KERNEL);
104         if (!buf->page_array)
105                 return NULL;
106
107         for (i = 0; i < n_pages; i++) {
108                 buf->page_array[i] = alloc_page(GFP_KERNEL);
109                 if (unlikely(!buf->page_array[i]))
110                         goto depopulate;
111         }
112         mem = vmap(buf->page_array, n_pages, VM_MAP, PAGE_KERNEL);
113         if (!mem)
114                 goto depopulate;
115
116         memset(mem, 0, size);
117         buf->page_count = n_pages;
118         return mem;
119
120 depopulate:
121         for (j = 0; j < i; j++)
122                 __free_page(buf->page_array[j]);
123         kfree(buf->page_array);
124         return NULL;
125 }
126
127 /**
128  *      relay_create_buf - allocate and initialize a channel buffer
129  *      @alloc_size: size of the buffer to allocate
130  *      @n_subbufs: number of sub-buffers in the channel
131  *
132  *      Returns channel buffer if successful, NULL otherwise
133  */
134 struct rchan_buf *relay_create_buf(struct rchan *chan)
135 {
136         struct rchan_buf *buf = kcalloc(1, sizeof(struct rchan_buf), GFP_KERNEL);
137         if (!buf)
138                 return NULL;
139
140         buf->padding = kmalloc(chan->n_subbufs * sizeof(size_t *), GFP_KERNEL);
141         if (!buf->padding)
142                 goto free_buf;
143
144         buf->start = relay_alloc_buf(buf, chan->alloc_size);
145         if (!buf->start)
146                 goto free_buf;
147
148         buf->chan = chan;
149         kref_get(&buf->chan->kref);
150         return buf;
151
152 free_buf:
153         kfree(buf->padding);
154         kfree(buf);
155         return NULL;
156 }
157
158 /**
159  *      relay_destroy_buf - destroy an rchan_buf struct and associated buffer
160  *      @buf: the buffer struct
161  */
162 void relay_destroy_buf(struct rchan_buf *buf)
163 {
164         struct rchan *chan = buf->chan;
165         unsigned int i;
166
167         if (likely(buf->start)) {
168                 vunmap(buf->start);
169                 for (i = 0; i < buf->page_count; i++)
170                         __free_page(buf->page_array[i]);
171                 kfree(buf->page_array);
172         }
173         kfree(buf->padding);
174         kfree(buf);
175         kref_put(&chan->kref, relay_destroy_channel);
176 }
177
178 /**
179  *      relay_remove_buf - remove a channel buffer
180  *
181  *      Removes the file from the relayfs fileystem, which also frees the
182  *      rchan_buf_struct and the channel buffer.  Should only be called from
183  *      kref_put().
184  */
185 void relay_remove_buf(struct kref *kref)
186 {
187         struct rchan_buf *buf = container_of(kref, struct rchan_buf, kref);
188         buf->chan->cb->remove_buf_file(buf->dentry);
189         relay_destroy_buf(buf);
190 }