Merge rsync://rsync.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
[linux-2.6] / drivers / infiniband / hw / ipath / ipath_user_pages.c
1 /*
2  * Copyright (c) 2003, 2004, 2005, 2006 PathScale, Inc. All rights reserved.
3  *
4  * This software is available to you under a choice of one of two
5  * licenses.  You may choose to be licensed under the terms of the GNU
6  * General Public License (GPL) Version 2, available from the file
7  * COPYING in the main directory of this source tree, or the
8  * OpenIB.org BSD license below:
9  *
10  *     Redistribution and use in source and binary forms, with or
11  *     without modification, are permitted provided that the following
12  *     conditions are met:
13  *
14  *      - Redistributions of source code must retain the above
15  *        copyright notice, this list of conditions and the following
16  *        disclaimer.
17  *
18  *      - Redistributions in binary form must reproduce the above
19  *        copyright notice, this list of conditions and the following
20  *        disclaimer in the documentation and/or other materials
21  *        provided with the distribution.
22  *
23  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
27  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
28  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
29  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30  * SOFTWARE.
31  */
32
33 #include <linux/mm.h>
34 #include <linux/device.h>
35
36 #include "ipath_kernel.h"
37
38 static void __ipath_release_user_pages(struct page **p, size_t num_pages,
39                                    int dirty)
40 {
41         size_t i;
42
43         for (i = 0; i < num_pages; i++) {
44                 ipath_cdbg(MM, "%lu/%lu put_page %p\n", (unsigned long) i,
45                            (unsigned long) num_pages, p[i]);
46                 if (dirty)
47                         set_page_dirty_lock(p[i]);
48                 put_page(p[i]);
49         }
50 }
51
52 /* call with current->mm->mmap_sem held */
53 static int __get_user_pages(unsigned long start_page, size_t num_pages,
54                         struct page **p, struct vm_area_struct **vma)
55 {
56         unsigned long lock_limit;
57         size_t got;
58         int ret;
59
60 #if 0
61         /*
62          * XXX - causes MPI programs to fail, haven't had time to check
63          * yet
64          */
65         if (!capable(CAP_IPC_LOCK)) {
66                 ret = -EPERM;
67                 goto bail;
68         }
69 #endif
70
71         lock_limit = current->signal->rlim[RLIMIT_MEMLOCK].rlim_cur >>
72                 PAGE_SHIFT;
73
74         if (num_pages > lock_limit) {
75                 ret = -ENOMEM;
76                 goto bail;
77         }
78
79         ipath_cdbg(VERBOSE, "pin %lx pages from vaddr %lx\n",
80                    (unsigned long) num_pages, start_page);
81
82         for (got = 0; got < num_pages; got += ret) {
83                 ret = get_user_pages(current, current->mm,
84                                      start_page + got * PAGE_SIZE,
85                                      num_pages - got, 1, 1,
86                                      p + got, vma);
87                 if (ret < 0)
88                         goto bail_release;
89         }
90
91         current->mm->locked_vm += num_pages;
92
93         ret = 0;
94         goto bail;
95
96 bail_release:
97         __ipath_release_user_pages(p, got, 0);
98 bail:
99         return ret;
100 }
101
102 /**
103  * ipath_get_user_pages - lock user pages into memory
104  * @start_page: the start page
105  * @num_pages: the number of pages
106  * @p: the output page structures
107  *
108  * This function takes a given start page (page aligned user virtual
109  * address) and pins it and the following specified number of pages.  For
110  * now, num_pages is always 1, but that will probably change at some point
111  * (because caller is doing expected sends on a single virtually contiguous
112  * buffer, so we can do all pages at once).
113  */
114 int ipath_get_user_pages(unsigned long start_page, size_t num_pages,
115                          struct page **p)
116 {
117         int ret;
118
119         down_write(&current->mm->mmap_sem);
120
121         ret = __get_user_pages(start_page, num_pages, p, NULL);
122
123         up_write(&current->mm->mmap_sem);
124
125         return ret;
126 }
127
128 /**
129  * ipath_get_user_pages_nocopy - lock a single page for I/O and mark shared
130  * @start_page: the page to lock
131  * @p: the output page structure
132  *
133  * This is similar to ipath_get_user_pages, but it's always one page, and we
134  * mark the page as locked for I/O, and shared.  This is used for the user
135  * process page that contains the destination address for the rcvhdrq tail
136  * update, so we need to have the vma. If we don't do this, the page can be
137  * taken away from us on fork, even if the child never touches it, and then
138  * the user process never sees the tail register updates.
139  */
140 int ipath_get_user_pages_nocopy(unsigned long page, struct page **p)
141 {
142         struct vm_area_struct *vma;
143         int ret;
144
145         down_write(&current->mm->mmap_sem);
146
147         ret = __get_user_pages(page, 1, p, &vma);
148
149         up_write(&current->mm->mmap_sem);
150
151         return ret;
152 }
153
154 void ipath_release_user_pages(struct page **p, size_t num_pages)
155 {
156         down_write(&current->mm->mmap_sem);
157
158         __ipath_release_user_pages(p, num_pages, 1);
159
160         current->mm->locked_vm -= num_pages;
161
162         up_write(&current->mm->mmap_sem);
163 }
164
165 struct ipath_user_pages_work {
166         struct work_struct work;
167         struct mm_struct *mm;
168         unsigned long num_pages;
169 };
170
171 static void user_pages_account(void *ptr)
172 {
173         struct ipath_user_pages_work *work = ptr;
174
175         down_write(&work->mm->mmap_sem);
176         work->mm->locked_vm -= work->num_pages;
177         up_write(&work->mm->mmap_sem);
178         mmput(work->mm);
179         kfree(work);
180 }
181
182 void ipath_release_user_pages_on_close(struct page **p, size_t num_pages)
183 {
184         struct ipath_user_pages_work *work;
185         struct mm_struct *mm;
186
187         __ipath_release_user_pages(p, num_pages, 1);
188
189         mm = get_task_mm(current);
190         if (!mm)
191                 goto bail;
192
193         work = kmalloc(sizeof(*work), GFP_KERNEL);
194         if (!work)
195                 goto bail_mm;
196
197         goto bail;
198
199         INIT_WORK(&work->work, user_pages_account, work);
200         work->mm = mm;
201         work->num_pages = num_pages;
202
203 bail_mm:
204         mmput(mm);
205 bail:
206         return;
207 }