diff options
author | Roland Dreier <rolandd@cisco.com> | 2007-04-18 23:20:28 -0400 |
---|---|---|
committer | Roland Dreier <rolandd@cisco.com> | 2007-05-08 21:00:37 -0400 |
commit | 1bf66a30421ca772820f489d88c16d0c430d6a67 (patch) | |
tree | b1ab223e6908d772bcad7f9bc3382c33ad5a4490 | |
parent | f7c6a7b5d59980b076abbf2ceeb8735591290285 (diff) |
IB: Put rlimit accounting struct in struct ib_umem
When memory pinned with ib_umem_get() is released, ib_umem_release()
needs to subtract the amount of memory being unpinned from
mm->locked_vm. However, ib_umem_release() may be called with
mm->mmap_sem already held for writing if the memory is being released
as part of an munmap() call, so it is sometimes necessary to defer
this accounting into a workqueue.
However, the work struct used to defer this accounting is dynamically
allocated before it is queued, so there is the possibility of failing
that allocation. If the allocation fails, then ib_umem_release has no
choice except to bail out and leave the process with a permanently
elevated locked_vm.
Fix this by allocating the structure to defer accounting as part of
the original struct ib_umem, so there's no possibility of failing a
later allocation if creating the struct ib_umem and pinning memory
succeeds.
Signed-off-by: Roland Dreier <rolandd@cisco.com>
-rw-r--r-- | drivers/infiniband/core/umem.c | 41 | ||||
-rw-r--r-- | include/rdma/ib_umem.h | 3 |
2 files changed, 16 insertions, 28 deletions
diff --git a/drivers/infiniband/core/umem.c b/drivers/infiniband/core/umem.c index 48e854cf416f..f32ca5fbb26b 100644 --- a/drivers/infiniband/core/umem.c +++ b/drivers/infiniband/core/umem.c | |||
@@ -39,13 +39,6 @@ | |||
39 | 39 | ||
40 | #include "uverbs.h" | 40 | #include "uverbs.h" |
41 | 41 | ||
42 | struct ib_umem_account_work { | ||
43 | struct work_struct work; | ||
44 | struct mm_struct *mm; | ||
45 | unsigned long diff; | ||
46 | }; | ||
47 | |||
48 | |||
49 | static void __ib_umem_release(struct ib_device *dev, struct ib_umem *umem, int dirty) | 42 | static void __ib_umem_release(struct ib_device *dev, struct ib_umem *umem, int dirty) |
50 | { | 43 | { |
51 | struct ib_umem_chunk *chunk, *tmp; | 44 | struct ib_umem_chunk *chunk, *tmp; |
@@ -192,16 +185,15 @@ out: | |||
192 | } | 185 | } |
193 | EXPORT_SYMBOL(ib_umem_get); | 186 | EXPORT_SYMBOL(ib_umem_get); |
194 | 187 | ||
195 | static void ib_umem_account(struct work_struct *_work) | 188 | static void ib_umem_account(struct work_struct *work) |
196 | { | 189 | { |
197 | struct ib_umem_account_work *work = | 190 | struct ib_umem *umem = container_of(work, struct ib_umem, work); |
198 | container_of(_work, struct ib_umem_account_work, work); | 191 | |
199 | 192 | down_write(&umem->mm->mmap_sem); | |
200 | down_write(&work->mm->mmap_sem); | 193 | umem->mm->locked_vm -= umem->diff; |
201 | work->mm->locked_vm -= work->diff; | 194 | up_write(&umem->mm->mmap_sem); |
202 | up_write(&work->mm->mmap_sem); | 195 | mmput(umem->mm); |
203 | mmput(work->mm); | 196 | kfree(umem); |
204 | kfree(work); | ||
205 | } | 197 | } |
206 | 198 | ||
207 | /** | 199 | /** |
@@ -210,7 +202,6 @@ static void ib_umem_account(struct work_struct *_work) | |||
210 | */ | 202 | */ |
211 | void ib_umem_release(struct ib_umem *umem) | 203 | void ib_umem_release(struct ib_umem *umem) |
212 | { | 204 | { |
213 | struct ib_umem_account_work *work; | ||
214 | struct ib_ucontext *context = umem->context; | 205 | struct ib_ucontext *context = umem->context; |
215 | struct mm_struct *mm; | 206 | struct mm_struct *mm; |
216 | unsigned long diff; | 207 | unsigned long diff; |
@@ -222,7 +213,6 @@ void ib_umem_release(struct ib_umem *umem) | |||
222 | return; | 213 | return; |
223 | 214 | ||
224 | diff = PAGE_ALIGN(umem->length + umem->offset) >> PAGE_SHIFT; | 215 | diff = PAGE_ALIGN(umem->length + umem->offset) >> PAGE_SHIFT; |
225 | kfree(umem); | ||
226 | 216 | ||
227 | /* | 217 | /* |
228 | * We may be called with the mm's mmap_sem already held. This | 218 | * We may be called with the mm's mmap_sem already held. This |
@@ -233,17 +223,11 @@ void ib_umem_release(struct ib_umem *umem) | |||
233 | * we defer the vm_locked accounting to the system workqueue. | 223 | * we defer the vm_locked accounting to the system workqueue. |
234 | */ | 224 | */ |
235 | if (context->closing && !down_write_trylock(&mm->mmap_sem)) { | 225 | if (context->closing && !down_write_trylock(&mm->mmap_sem)) { |
236 | work = kmalloc(sizeof *work, GFP_KERNEL); | 226 | INIT_WORK(&umem->work, ib_umem_account); |
237 | if (!work) { | 227 | umem->mm = mm; |
238 | mmput(mm); | 228 | umem->diff = diff; |
239 | return; | ||
240 | } | ||
241 | 229 | ||
242 | INIT_WORK(&work->work, ib_umem_account); | 230 | schedule_work(&umem->work); |
243 | work->mm = mm; | ||
244 | work->diff = diff; | ||
245 | |||
246 | schedule_work(&work->work); | ||
247 | return; | 231 | return; |
248 | } else | 232 | } else |
249 | down_write(&mm->mmap_sem); | 233 | down_write(&mm->mmap_sem); |
@@ -251,6 +235,7 @@ void ib_umem_release(struct ib_umem *umem) | |||
251 | current->mm->locked_vm -= diff; | 235 | current->mm->locked_vm -= diff; |
252 | up_write(&mm->mmap_sem); | 236 | up_write(&mm->mmap_sem); |
253 | mmput(mm); | 237 | mmput(mm); |
238 | kfree(umem); | ||
254 | } | 239 | } |
255 | EXPORT_SYMBOL(ib_umem_release); | 240 | EXPORT_SYMBOL(ib_umem_release); |
256 | 241 | ||
diff --git a/include/rdma/ib_umem.h b/include/rdma/ib_umem.h index 06307f7e43e0..b3a36f7d79e5 100644 --- a/include/rdma/ib_umem.h +++ b/include/rdma/ib_umem.h | |||
@@ -45,6 +45,9 @@ struct ib_umem { | |||
45 | int page_size; | 45 | int page_size; |
46 | int writable; | 46 | int writable; |
47 | struct list_head chunk_list; | 47 | struct list_head chunk_list; |
48 | struct work_struct work; | ||
49 | struct mm_struct *mm; | ||
50 | unsigned long diff; | ||
48 | }; | 51 | }; |
49 | 52 | ||
50 | struct ib_umem_chunk { | 53 | struct ib_umem_chunk { |