Skip to content

Commit 5865cf9

Browse files
Vincent Sanderspopcornmix
Vincent Sanders
authored andcommitted
vchiq: create_pagelist copes with vmalloc memory
Signed-off-by: Daniel Stone <[email protected]>
1 parent a37a457 commit 5865cf9

File tree

1 file changed

+53
-30
lines changed

1 file changed

+53
-30
lines changed

drivers/misc/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c

Lines changed: 53 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,7 @@ create_pagelist(char __user *buf, size_t count, unsigned short type,
374374
unsigned int num_pages, offset, i;
375375
char *addr, *base_addr, *next_addr;
376376
int run, addridx, actual_pages;
377+
unsigned long *need_release;
377378

378379
offset = (unsigned int)buf & (PAGE_SIZE - 1);
379380
num_pages = (count + offset + PAGE_SIZE - 1) / PAGE_SIZE;
@@ -384,38 +385,55 @@ create_pagelist(char __user *buf, size_t count, unsigned short type,
384385
** list
385386
*/
386387
pagelist = kmalloc(sizeof(PAGELIST_T) +
387-
(num_pages * sizeof(unsigned long)) +
388-
(num_pages * sizeof(pages[0])),
389-
GFP_KERNEL);
388+
(num_pages * sizeof(unsigned long)) +
389+
sizeof(unsigned long) +
390+
(num_pages * sizeof(pages[0])),
391+
GFP_KERNEL);
390392

391393
vchiq_log_trace(vchiq_arm_log_level,
392394
"create_pagelist - %x", (unsigned int)pagelist);
393395
if (!pagelist)
394396
return -ENOMEM;
395397

396398
addrs = pagelist->addrs;
397-
pages = (struct page **)(addrs + num_pages);
399+
need_release = (unsigned long *)(addrs + num_pages);
400+
pages = (struct page **)(addrs + num_pages + 1);
398401

399-
down_read(&task->mm->mmap_sem);
400-
actual_pages = get_user_pages(task, task->mm,
401-
(unsigned long)buf & ~(PAGE_SIZE - 1), num_pages,
402-
(type == PAGELIST_READ) /*Write */ , 0 /*Force */ ,
403-
pages, NULL /*vmas */);
404-
up_read(&task->mm->mmap_sem);
405-
406-
if (actual_pages != num_pages)
407-
{
408-
/* This is probably due to the process being killed */
409-
while (actual_pages > 0)
410-
{
411-
actual_pages--;
412-
page_cache_release(pages[actual_pages]);
413-
}
414-
kfree(pagelist);
415-
if (actual_pages == 0)
416-
actual_pages = -ENOMEM;
417-
return actual_pages;
418-
}
402+
if (is_vmalloc_addr(buf)) {
403+
for (actual_pages = 0; actual_pages < num_pages; actual_pages++) {
404+
pages[actual_pages] = vmalloc_to_page(buf + (actual_pages * PAGE_SIZE));
405+
}
406+
*need_release = 0; /* do not try and release vmalloc pages */
407+
} else {
408+
down_read(&task->mm->mmap_sem);
409+
actual_pages = get_user_pages(task, task->mm,
410+
(unsigned long)buf & ~(PAGE_SIZE - 1),
411+
num_pages,
412+
(type == PAGELIST_READ) /*Write */ ,
413+
0 /*Force */ ,
414+
pages,
415+
NULL /*vmas */);
416+
up_read(&task->mm->mmap_sem);
417+
418+
if (actual_pages != num_pages) {
419+
vchiq_log_info(vchiq_arm_log_level,
420+
"create_pagelist - only %d/%d pages locked",
421+
actual_pages,
422+
num_pages);
423+
424+
/* This is probably due to the process being killed */
425+
while (actual_pages > 0)
426+
{
427+
actual_pages--;
428+
page_cache_release(pages[actual_pages]);
429+
}
430+
kfree(pagelist);
431+
if (actual_pages == 0)
432+
actual_pages = -ENOMEM;
433+
return actual_pages;
434+
}
435+
*need_release = 1; /* release user pages */
436+
}
419437

420438
pagelist->length = count;
421439
pagelist->type = type;
@@ -482,6 +500,7 @@ create_pagelist(char __user *buf, size_t count, unsigned short type,
482500
static void
483501
free_pagelist(PAGELIST_T *pagelist, int actual)
484502
{
503+
unsigned long *need_release;
485504
struct page **pages;
486505
unsigned int num_pages, i;
487506

@@ -492,7 +511,8 @@ free_pagelist(PAGELIST_T *pagelist, int actual)
492511
(pagelist->length + pagelist->offset + PAGE_SIZE - 1) /
493512
PAGE_SIZE;
494513

495-
pages = (struct page **)(pagelist->addrs + num_pages);
514+
need_release = (unsigned long *)(pagelist->addrs + num_pages);
515+
pages = (struct page **)(pagelist->addrs + num_pages + 1);
496516

497517
/* Deal with any partial cache lines (fragments) */
498518
if (pagelist->type >= PAGELIST_READ_WITH_FRAGMENTS) {
@@ -528,11 +548,14 @@ free_pagelist(PAGELIST_T *pagelist, int actual)
528548
up(&g_free_fragments_sema);
529549
}
530550

531-
for (i = 0; i < num_pages; i++) {
532-
if (pagelist->type != PAGELIST_WRITE)
533-
set_page_dirty(pages[i]);
534-
page_cache_release(pages[i]);
535-
}
551+
if (*need_release) {
552+
for (i = 0; i < num_pages; i++) {
553+
if (pagelist->type != PAGELIST_WRITE)
554+
set_page_dirty(pages[i]);
555+
556+
page_cache_release(pages[i]);
557+
}
558+
}
536559

537560
kfree(pagelist);
538561
}

0 commit comments

Comments
 (0)