diff options
author | Oleg Nesterov <oleg@redhat.com> | 2014-05-05 10:38:18 -0400 |
---|---|---|
committer | Oleg Nesterov <oleg@redhat.com> | 2014-05-14 07:57:24 -0400 |
commit | 29dedee0e693aa113164c820395ce51446a71ace (patch) | |
tree | 7dff2a1f28a6c2d300c14fbe3674f3756f4dea7a /kernel/events/uprobes.c | |
parent | 8e02ae573e6ae86930d08662790827a938203e70 (diff) |
uprobes: Add mem_cgroup_charge_anon() into uprobe_write_opcode()
Hugh says:
The one I noticed was that it forgets all about memcg (because
it was copied from KSM, and there the replacement page has already
been charged to a memcg). See how mm/memory.c do_anonymous_page()
does a mem_cgroup_charge_anon().
Hopefully not a big problem, uprobes is a system-wide thing and only
root can insert the probes. But I agree, should be fixed anyway.
Add mem_cgroup_{un,}charge_anon() into uprobe_write_opcode(). To simplify
the error handling (and avoid the new "uncharge" label) the patch also
moves anon_vma_prepare() up before we alloc/charge the new page.
While at it fix the comment about ->mmap_sem, it is held for write.
Suggested-by: Hugh Dickins <hughd@google.com>
Signed-off-by: Oleg Nesterov <oleg@redhat.com>
Diffstat (limited to 'kernel/events/uprobes.c')
-rw-r--r-- | kernel/events/uprobes.c | 23 |
1 files changed, 11 insertions, 12 deletions
diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c index 7716c40f2c50..a13251e8bfa4 100644 --- a/kernel/events/uprobes.c +++ b/kernel/events/uprobes.c | |||
@@ -279,18 +279,13 @@ static int verify_opcode(struct page *page, unsigned long vaddr, uprobe_opcode_t | |||
279 | * supported by that architecture then we need to modify is_trap_at_addr and | 279 | * supported by that architecture then we need to modify is_trap_at_addr and |
280 | * uprobe_write_opcode accordingly. This would never be a problem for archs | 280 | * uprobe_write_opcode accordingly. This would never be a problem for archs |
281 | * that have fixed length instructions. | 281 | * that have fixed length instructions. |
282 | */ | 282 | * |
283 | |||
284 | /* | ||
285 | * uprobe_write_opcode - write the opcode at a given virtual address. | 283 | * uprobe_write_opcode - write the opcode at a given virtual address. |
286 | * @mm: the probed process address space. | 284 | * @mm: the probed process address space. |
287 | * @vaddr: the virtual address to store the opcode. | 285 | * @vaddr: the virtual address to store the opcode. |
288 | * @opcode: opcode to be written at @vaddr. | 286 | * @opcode: opcode to be written at @vaddr. |
289 | * | 287 | * |
290 | * Called with mm->mmap_sem held (for read and with a reference to | 288 | * Called with mm->mmap_sem held for write. |
291 | * mm). | ||
292 | * | ||
293 | * For mm @mm, write the opcode at @vaddr. | ||
294 | * Return 0 (success) or a negative errno. | 289 | * Return 0 (success) or a negative errno. |
295 | */ | 290 | */ |
296 | int uprobe_write_opcode(struct mm_struct *mm, unsigned long vaddr, | 291 | int uprobe_write_opcode(struct mm_struct *mm, unsigned long vaddr, |
@@ -310,21 +305,25 @@ retry: | |||
310 | if (ret <= 0) | 305 | if (ret <= 0) |
311 | goto put_old; | 306 | goto put_old; |
312 | 307 | ||
308 | ret = anon_vma_prepare(vma); | ||
309 | if (ret) | ||
310 | goto put_old; | ||
311 | |||
313 | ret = -ENOMEM; | 312 | ret = -ENOMEM; |
314 | new_page = alloc_page_vma(GFP_HIGHUSER_MOVABLE, vma, vaddr); | 313 | new_page = alloc_page_vma(GFP_HIGHUSER_MOVABLE, vma, vaddr); |
315 | if (!new_page) | 314 | if (!new_page) |
316 | goto put_old; | 315 | goto put_old; |
317 | 316 | ||
318 | __SetPageUptodate(new_page); | 317 | if (mem_cgroup_charge_anon(new_page, mm, GFP_KERNEL)) |
318 | goto put_new; | ||
319 | 319 | ||
320 | __SetPageUptodate(new_page); | ||
320 | copy_highpage(new_page, old_page); | 321 | copy_highpage(new_page, old_page); |
321 | copy_to_page(new_page, vaddr, &opcode, UPROBE_SWBP_INSN_SIZE); | 322 | copy_to_page(new_page, vaddr, &opcode, UPROBE_SWBP_INSN_SIZE); |
322 | 323 | ||
323 | ret = anon_vma_prepare(vma); | ||
324 | if (ret) | ||
325 | goto put_new; | ||
326 | |||
327 | ret = __replace_page(vma, vaddr, old_page, new_page); | 324 | ret = __replace_page(vma, vaddr, old_page, new_page); |
325 | if (ret) | ||
326 | mem_cgroup_uncharge_page(new_page); | ||
328 | 327 | ||
329 | put_new: | 328 | put_new: |
330 | page_cache_release(new_page); | 329 | page_cache_release(new_page); |