befcaa1a821a22033863a95b66c22fa5f346290f
[openwrt/svn-archive/archive.git] / target / linux / brcm2708 / patches-3.10 / 0108-vchiq-create_pagelist-copes-with-vmalloc-memory.patch
1 From f3978e548747dfe988016ef7c99fe50975da90df Mon Sep 17 00:00:00 2001
2 From: Vincent Sanders <vincent.sanders@collabora.co.uk>
3 Date: Mon, 2 Sep 2013 16:44:57 +0100
4 Subject: [PATCH 108/196] vchiq: create_pagelist copes with vmalloc memory
5
6 Signed-off-by: Daniel Stone <daniels@collabora.com>
7 ---
8 .../interface/vchiq_arm/vchiq_2835_arm.c | 83 ++++++++++++++--------
9 1 file changed, 53 insertions(+), 30 deletions(-)
10
11 diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c
12 index 2b5fa56..b3bdaa2 100644
13 --- a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c
14 +++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c
15 @@ -374,6 +374,7 @@ create_pagelist(char __user *buf, size_t count, unsigned short type,
16 unsigned int num_pages, offset, i;
17 char *addr, *base_addr, *next_addr;
18 int run, addridx, actual_pages;
19 + unsigned long *need_release;
20
21 offset = (unsigned int)buf & (PAGE_SIZE - 1);
22 num_pages = (count + offset + PAGE_SIZE - 1) / PAGE_SIZE;
23 @@ -384,9 +385,10 @@ create_pagelist(char __user *buf, size_t count, unsigned short type,
24 ** list
25 */
26 pagelist = kmalloc(sizeof(PAGELIST_T) +
27 - (num_pages * sizeof(unsigned long)) +
28 - (num_pages * sizeof(pages[0])),
29 - GFP_KERNEL);
30 + (num_pages * sizeof(unsigned long)) +
31 + sizeof(unsigned long) +
32 + (num_pages * sizeof(pages[0])),
33 + GFP_KERNEL);
34
35 vchiq_log_trace(vchiq_arm_log_level,
36 "create_pagelist - %x", (unsigned int)pagelist);
37 @@ -394,28 +396,44 @@ create_pagelist(char __user *buf, size_t count, unsigned short type,
38 return -ENOMEM;
39
40 addrs = pagelist->addrs;
41 - pages = (struct page **)(addrs + num_pages);
42 + need_release = (unsigned long *)(addrs + num_pages);
43 + pages = (struct page **)(addrs + num_pages + 1);
44
45 - down_read(&task->mm->mmap_sem);
46 - actual_pages = get_user_pages(task, task->mm,
47 - (unsigned long)buf & ~(PAGE_SIZE - 1), num_pages,
48 - (type == PAGELIST_READ) /*Write */ , 0 /*Force */ ,
49 - pages, NULL /*vmas */);
50 - up_read(&task->mm->mmap_sem);
51 -
52 - if (actual_pages != num_pages)
53 - {
54 - /* This is probably due to the process being killed */
55 - while (actual_pages > 0)
56 - {
57 - actual_pages--;
58 - page_cache_release(pages[actual_pages]);
59 - }
60 - kfree(pagelist);
61 - if (actual_pages == 0)
62 - actual_pages = -ENOMEM;
63 - return actual_pages;
64 - }
65 + if (is_vmalloc_addr(buf)) {
66 + for (actual_pages = 0; actual_pages < num_pages; actual_pages++) {
67 + pages[actual_pages] = vmalloc_to_page(buf + (actual_pages * PAGE_SIZE));
68 + }
69 + *need_release = 0; /* do not try and release vmalloc pages */
70 + } else {
71 + down_read(&task->mm->mmap_sem);
72 + actual_pages = get_user_pages(task, task->mm,
73 + (unsigned long)buf & ~(PAGE_SIZE - 1),
74 + num_pages,
75 + (type == PAGELIST_READ) /*Write */ ,
76 + 0 /*Force */ ,
77 + pages,
78 + NULL /*vmas */);
79 + up_read(&task->mm->mmap_sem);
80 +
81 + if (actual_pages != num_pages) {
82 + vchiq_log_info(vchiq_arm_log_level,
83 + "create_pagelist - only %d/%d pages locked",
84 + actual_pages,
85 + num_pages);
86 +
87 + /* This is probably due to the process being killed */
88 + while (actual_pages > 0)
89 + {
90 + actual_pages--;
91 + page_cache_release(pages[actual_pages]);
92 + }
93 + kfree(pagelist);
94 + if (actual_pages == 0)
95 + actual_pages = -ENOMEM;
96 + return actual_pages;
97 + }
98 + *need_release = 1; /* release user pages */
99 + }
100
101 pagelist->length = count;
102 pagelist->type = type;
103 @@ -482,6 +500,7 @@ create_pagelist(char __user *buf, size_t count, unsigned short type,
104 static void
105 free_pagelist(PAGELIST_T *pagelist, int actual)
106 {
107 + unsigned long *need_release;
108 struct page **pages;
109 unsigned int num_pages, i;
110
111 @@ -492,7 +511,8 @@ free_pagelist(PAGELIST_T *pagelist, int actual)
112 (pagelist->length + pagelist->offset + PAGE_SIZE - 1) /
113 PAGE_SIZE;
114
115 - pages = (struct page **)(pagelist->addrs + num_pages);
116 + need_release = (unsigned long *)(pagelist->addrs + num_pages);
117 + pages = (struct page **)(pagelist->addrs + num_pages + 1);
118
119 /* Deal with any partial cache lines (fragments) */
120 if (pagelist->type >= PAGELIST_READ_WITH_FRAGMENTS) {
121 @@ -528,11 +548,14 @@ free_pagelist(PAGELIST_T *pagelist, int actual)
122 up(&g_free_fragments_sema);
123 }
124
125 - for (i = 0; i < num_pages; i++) {
126 - if (pagelist->type != PAGELIST_WRITE)
127 - set_page_dirty(pages[i]);
128 - page_cache_release(pages[i]);
129 - }
130 + if (*need_release) {
131 + for (i = 0; i < num_pages; i++) {
132 + if (pagelist->type != PAGELIST_WRITE)
133 + set_page_dirty(pages[i]);
134 +
135 + page_cache_release(pages[i]);
136 + }
137 + }
138
139 kfree(pagelist);
140 }
141 --
142 1.9.1
143