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