123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185 |
- #include <linux/init.h>
- #include <linux/fs.h>
- #include <linux/mm.h>
- #include <linux/module.h>
- #include <linux/slab.h>
- #include <linux/miscdevice.h>
- #include <linux/kallsyms.h>
- #include <linux/sched.h>
- #include <linux/dcache.h>
- #include <linux/namei.h>
- #include <linux/pagemap.h>
- #include <linux/version.h>
- #include <linux/vmalloc.h>
- #include <linux/bitmap.h>
- #include <asm/mman.h>
- #ifdef CONFIG_GRAPHENE_BULK_IPC
- # include "graphene.h"
- #else
- # include "my_tlb.h"
- # include "my_mmap.h"
- #endif
- #include "graphene-ipc.h"
- MODULE_LICENSE("Dual BSD/GPL");
- #define FILE_POISON LIST_POISON1
- struct kmem_cache *gipc_queue_cachep;
- struct kmem_cache *gipc_buf_cachep;
- #define GIPC_DEBUG 0
- #if defined(GIPC_DEBUG) && GIPC_DEBUG == 1
- # define DEBUG(...) printk(KERN_INFO __VA_ARGS__)
- # define GIPC_BUG_ON(cond) BUG_ON(cond)
- #else
- # define DEBUG(...)
- # define GIPC_BUG_ON(cond)
- #endif
- #ifndef gipc_get_session
- u32 (*my_gipc_get_session) (struct task_struct *) = NULL;
- #endif
- /* The page bufs going one direction */
- struct gipc_pages {
- struct file *file; // indicates if the struct is taken or not
- volatile int next, last;
- struct gipc_page_buf {
- struct page *page;
- struct file *file;
- u64 pgoff;
- } page_buf[PAGE_BUFS];
- struct mutex sender_lock;
- struct mutex receiver_lock;
- wait_queue_head_t send, recv;
- struct gipc_sender_buf {
- unsigned long bitmap[PAGE_BITS];
- struct page *pages[PAGE_BUFS];
- struct vm_area_struct *vmas[PAGE_BUFS];
- struct file *files[PAGE_BUFS];
- u64 pgoffs[PAGE_BUFS];
- } *sender_buf;
- };
- #define GIPC_PAGES(queue) \
- ((struct gipc_pages *) ((void *) (queue) + sizeof(struct gipc_queue)))
- struct {
- spinlock_t lock;
- /*
- * For now, just make them monotonically increasing. XXX: At
- * some point, do something smarter for security.
- */
- int64_t max_token;
- struct list_head channels; // gipc_queue structs
- } gdev;
- static inline struct gipc_queue * create_gipc_queue(struct file *creator)
- {
- struct gipc_queue *gq = kmem_cache_alloc(gipc_queue_cachep, GFP_KERNEL);
- struct gipc_pages *gp = GIPC_PAGES(gq);
- if (!gq)
- return gq;
- INIT_LIST_HEAD(&gq->list);
- memset(gp, 0, sizeof(struct gipc_pages) * 2);
- mutex_init(&gp[0].sender_lock);
- mutex_init(&gp[1].sender_lock);
- mutex_init(&gp[0].receiver_lock);
- mutex_init(&gp[1].receiver_lock);
- init_waitqueue_head(&gp[0].send);
- init_waitqueue_head(&gp[0].recv);
- init_waitqueue_head(&gp[1].send);
- init_waitqueue_head(&gp[1].recv);
- gp[0].file = creator;
- creator->private_data = gq;
- gp[1].file = NULL;
- spin_lock(&gdev.lock);
- list_add(&gq->list, &gdev.channels);
- gq->token = gdev.max_token++;
- #ifdef gipc_get_session
- gq->owner = gipc_get_session(current);
- #else
- gq->owner = my_gipc_get_session ? my_gipc_get_session(current) : 0;
- #endif
- spin_unlock(&gdev.lock);
- return gq;
- }
- static inline void release_gipc_pages(struct gipc_pages *gps) {
- int idx;
- while (gps->next != gps->last) {
- idx = gps->next;
- if (gps->page_buf[idx].page) {
- page_cache_release(gps->page_buf[idx].page);
- gps->page_buf[idx].page = NULL;
- }
- if (gps->page_buf[idx].file) {
- fput_atomic(gps->page_buf[idx].file);
- gps->page_buf[idx].file = NULL;
- gps->page_buf[idx].pgoff = 0;
- }
- gps->next++;
- gps->next &= (PAGE_BUFS-1);
- }
- }
- #if defined(SPLIT_RSS_COUNTING)
- static void add_mm_counter_fast(struct mm_struct *mm, int member, int val)
- {
- struct task_struct *task = current;
- if (likely(task->mm == mm))
- task->rss_stat.count[member] += val;
- else
- add_mm_counter(mm, member, val);
- }
- #else
- #define add_mm_counter_fast(mm, member, val) add_mm_counter(mm, member, val)
- #endif
- #define inc_mm_counter_fast(mm, member) add_mm_counter_fast(mm, member, 1)
- #define dec_mm_counter_fast(mm, member) add_mm_counter_fast(mm, member, -1)
- inline int make_pages_cow(struct mm_struct *mm, struct vm_area_struct *vma,
- unsigned long addr, int count)
- {
- int i = 0;
- pgd_t *pgd;
- unsigned long pgd_next;
- pud_t *pud;
- unsigned long pud_next;
- pmd_t *pmd;
- unsigned long pmd_next;
- pte_t *pte;
- spinlock_t *ptl;
- unsigned long end = addr + (PAGE_SIZE * count);
- /* Mark source COW */
- do {
- if ((vma->vm_flags & (VM_SHARED|VM_MAYWRITE)) != VM_MAYWRITE)
- return VM_FAULT_OOM;
- pgd = pgd_offset(mm, addr);
- do {
- pgd_next = pgd_addr_end(addr, end);
- if (pgd_none(*pgd) || pgd_bad(*pgd))
- return VM_FAULT_OOM;
- pud = pud_offset(pgd, addr);
- do {
- pud_next = pud_addr_end(addr, pgd_next);
- if (pud_none(*pud) || pud_bad(*pud))
- return VM_FAULT_OOM;
-
- pmd = pmd_offset(pud, addr);
- do {
- pte_t *pte_base;
- pmd_next = pmd_addr_end(addr, pud_next);
- if (pmd_none(*pmd) || pmd_bad(*pmd))
- return VM_FAULT_OOM;
- pte_base = pte = pte_offset_map_lock(mm, pmd, addr, &ptl);
- if (!pte)
- return VM_FAULT_OOM;
- do {
- if ((!pte_none(*pte)) && pte_present(*pte)) {
- ptep_set_wrprotect(mm, addr, pte);
- pte_wrprotect(*pte);
- } else {
- pte_unmap_unlock(pte, ptl);
- return VM_FAULT_OOM;
- }
- } while (pte++, addr += PAGE_SIZE, i++,
- addr != pmd_next);
- pte_unmap_unlock(pte_base, ptl);
- } while (pmd++,
- addr < pud_next);
- } while (pud++,
- addr < pgd_next);
- } while (pgd++,
- addr < vma->vm_end && addr < end);
- if (i < count) {
- /* Find the next vma. Leverage the fact that
- * addresses are increasing.
- */
- while (vma && addr < vma->vm_end)
- vma = vma->vm_next;
- }
- } while (addr < end && vma);
- return 0;
- }
- static void fill_page_bitmap(struct mm_struct *mm, unsigned long addr,
- unsigned long * page_bit_map, int count)
- {
- int i = 0;
- spinlock_t *ptl;
- pgd_t *pgd;
- unsigned long pgd_next;
-
- pud_t *pud;
- unsigned long pud_next;
- pmd_t *pmd;
- unsigned long pmd_next;
- pte_t *pte;
-
- unsigned long end = addr + (PAGE_SIZE * count);
- pgd = pgd_offset(mm, addr);
- do {
- pgd_next = pgd_addr_end(addr, end);
- if (pgd_none(*pgd) || pgd_bad(*pgd)) {
- while (addr < pgd_next) {
- clear_bit(i, page_bit_map);
- i++;
- addr += PAGE_SIZE;
- }
- continue;
- }
-
- pud = pud_offset(pgd, addr);
- do {
- pud_next = pud_addr_end(addr, pgd_next);
- if (pud_none(*pud) || pud_bad(*pud)) {
- while (addr < pud_next) {
- clear_bit(i, page_bit_map);
- i++;
- addr += PAGE_SIZE;
- }
- continue;
- }
- pmd = pmd_offset(pud, addr);
- do {
- pmd_next = pmd_addr_end(addr, pud_next);
- if(pmd_none(*pmd) || pmd_bad(*pmd)) {
- while (addr < pmd_next) {
- clear_bit(i, page_bit_map);
- i++;
- addr += PAGE_SIZE;
- }
- continue;
- }
- pte = pte_offset_map_lock(mm, pmd, addr, &ptl);
- do {
- if ((!pte) || pte_none(*pte) || (!pte_present(*pte)))
- clear_bit(i, page_bit_map);
- else
- set_bit(i, page_bit_map);
- } while (pte++, addr += PAGE_SIZE, i++,
- addr != pmd_next);
- pte_unmap_unlock(pte - 1, ptl);
-
- } while (pmd++,
- addr < pud_next);
-
- } while (pud++,
- addr < pgd_next);
- } while (pgd++,
- addr < end);
- }
- static int get_pages (struct task_struct *current_tsk,
- unsigned long address, unsigned long * page_bit_map,
- struct page *pages[PAGE_BUFS],
- struct vm_area_struct *vmas[PAGE_BUFS], int count)
- {
- int num_contiguous_bits= 0;
- int index = 0;
- int i = 0, start, rv, page_count = 0;
- unsigned long addr = address;
- struct vm_area_struct *last_vma = NULL;
- while ( index < count ) {
- start = index;
- if ( test_bit(start, page_bit_map) ) {
- index = find_next_zero_bit(page_bit_map, PAGE_BUFS, start+1);
- if (index > count)
- index = count;
- num_contiguous_bits = index - start;
- rv = get_user_pages(current_tsk,
- current_tsk->mm,
- addr,
- num_contiguous_bits,
- 1,
- 1,
- &pages[start],
- &vmas[start]);
- if (rv <= 0) {
- printk(KERN_ERR "Graphene error: "
- "get_user_pages at 0x%016lx-0x%016lx\n",
- addr,
- addr + PAGE_SIZE * num_contiguous_bits);
- return rv;
- }
- if (rv != num_contiguous_bits) {
- printk(KERN_ERR "Graphene error: "
- "get_user_pages at 0x%016lx\n",
- addr + PAGE_SIZE * rv);
- return -EACCES;
- }
- page_count += rv;
- /* Mark source COW */
- rv = make_pages_cow(current_tsk->mm,
- vmas[start], addr, num_contiguous_bits);
- if (rv)
- return rv;
- /* Fix up the counters */
- add_mm_counter_fast(current_tsk->mm, MM_FILEPAGES, num_contiguous_bits);
- add_mm_counter_fast(current_tsk->mm, MM_ANONPAGES, -num_contiguous_bits);
- for (i = 0; i < num_contiguous_bits; i++) {
- pages[start + i]->mapping = NULL;
- addr += PAGE_SIZE;
- }
- last_vma = vmas[start + num_contiguous_bits - 1];
- } else {
- /* This is the case where a page (or pages) are not currently mapped.
- * Handle the hole appropriately.
- */
- index = find_next_bit(page_bit_map, PAGE_BUFS, start+1);
- if (index > count)
- index = count;
- num_contiguous_bits = index - start;
- for (i = 0; i < num_contiguous_bits; i++) {
- struct vm_area_struct *my_vma;
- pages[start + i] = NULL;
- if ( !last_vma ) {
- last_vma = find_vma(current_tsk->mm,
- addr);
- my_vma = last_vma;
- } else {
- /* DEP 6/17/13 - these addresses should be
- * monotonically increasing.
- */
- while (last_vma && addr >= last_vma->vm_end )
- last_vma = last_vma->vm_next;
- /* Leverage monotonic increasing vmas
- * to more quickly detect holes in the address space.
- */
- if (addr < last_vma->vm_start)
- my_vma = NULL;
- else
- my_vma = last_vma;
- }
- vmas[start + i] = my_vma;
- page_count++;
- addr += PAGE_SIZE;
- }
- }
- }
- return page_count;
- }
- static inline
- int recv_next (struct task_struct *current_tsk,
- struct gipc_pages *pending)
- {
- if (pending->next == pending->last) {
- /* The blocking condition for send
- * and recv can't both be true! */
- wake_up_all(&pending->send);
- wait_event_interruptible(pending->recv, (pending->next != pending->last));
- if (signal_pending(current_tsk))
- return -ERESTARTSYS;
- }
-
- return pending->next;
- }
- static
- int recv_helper (unsigned long *addr, unsigned long len, int prot,
- struct task_struct *current_tsk, struct gipc_pages *pending,
- struct file *file, int *physical_pages)
- {
- int page_count = len >> PAGE_SHIFT;
- int i, found;
- long rv = 0;
- struct page *page;
- struct file *vm_file;
- u64 vm_pgoff;
- int flags = MAP_PRIVATE;
- struct vm_area_struct *vma;
- unsigned long my_addr = *addr;
- if (len & ~PAGE_MASK)
- page_count++;
- for (i = 0; i < page_count; ) {
- unsigned long start_addr;
- found = recv_next(current_tsk, pending);
- if (found < 0)
- return -ERESTARTSYS;
-
- page = pending->page_buf[found].page;
- vm_file = pending->page_buf[found].file;
- vm_pgoff = pending->page_buf[found].pgoff;
- pending->next = ((pending->next + 1) & (PAGE_BUFS-1));
- wake_up_all(&pending->send);
- if (my_addr)
- flags |= MAP_FIXED;
- if (file)
- flags = (flags & ~MAP_ANONYMOUS) | MAP_FILE;
- else
- flags = (flags & ~MAP_FILE) | MAP_ANONYMOUS;
- /* Speculate that we will want the entire region under one
- * vma. Correct if not.
- */
- #if defined(CONFIG_GRAPHENE_BULK_IPC) || LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0)
- #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 9, 0)
- my_addr = do_mmap_pgoff(vm_file, my_addr,
- PAGE_SIZE * (page_count - i),
- prot, flags, vm_pgoff);
- #else
- {
- unsigned long populate;
- my_addr = do_mmap_pgoff(vm_file, my_addr,
- PAGE_SIZE * (page_count - i),
- prot,
- flags, vm_pgoff, &populate);
- }
- #endif /* kernel_version >= 3.9.0 */
- #else
- #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 9, 0)
- my_addr = my_do_mmap_pgoff(vm_file, my_addr,
- PAGE_SIZE * (page_count - i),
- prot, flags, vm_pgoff);
- #else
- {
- unsigned long populate;
- my_addr = my_do_mmap_pgoff(vm_file, my_addr,
- PAGE_SIZE * (page_count - i),
- prot,
- flags, vm_pgoff, &populate);
- }
- #endif /* kernel_version >= 3.9.0 */
- #endif
- if (!my_addr) {
- printk(KERN_ERR
- "Graphene error: failed to mmap %p\n",
- (void *) rv);
- rv = -EINVAL;
- goto finish_recv;
- }
- /* Save our staring addr for COW-ing later */
- *addr = start_addr = my_addr;
- vma = find_vma(current_tsk->mm, my_addr);
- if (!vma) {
- printk(KERN_ERR
- "Graphene error: can't find vma at %p\n",
- (void *) my_addr);
- rv = -ENOENT;
- goto finish_recv;
- }
- while (i < page_count) {
- int last_time = ((i+1) == page_count);
- int page_true = (page != NULL);
-
- /* Fill the vma with this page */
- if (page) {
- rv = vm_insert_page(vma, my_addr, page);
- if (rv) {
- printk(KERN_ERR
- "Graphene error: fail to insert page %p\n",
- (void *) rv);
- goto finish_recv;
- }
- (*physical_pages)++;
- page_cache_release(page);
-
- /* Avoid double-putting the page */
- page = NULL;
- }
- /* Check out the next page, maybe it can go in the same VMA */
- if (!last_time) {
- found = recv_next(current_tsk, pending);
- if (found < 0)
- return -ERESTARTSYS;
- }
- if (page_true) {
- /* If the next page will break the vma, isn't there,
- * or we are at the end of the VMA, go ahead
- * and mark the range we've mapped as COW.
- */
- if ((i+1) == page_count
- || (!pending->page_buf[found].page)
- || pending->page_buf[found].file != vm_file) {
- int sz = ((my_addr - start_addr) / PAGE_SIZE) + 1;
- rv = make_pages_cow(current_tsk->mm, vma,
- start_addr,
- sz);
- start_addr = my_addr + PAGE_SIZE;
- if (rv) {
- printk(KERN_ERR
- "Graphene error: can't make vma copy-on-write at %p-%p\n",
- (void *) start_addr,
- (void *) my_addr);
- goto finish_recv;
- }
- }
- }
- /* See if we can continue in the inner loop*/
- if ((!last_time) && pending->page_buf[found].file == vm_file) {
- page = pending->page_buf[found].page;
- pending->next = ((pending->next + 1) & (PAGE_BUFS-1));
- wake_up_all(&pending->send);
- i++;
- my_addr += PAGE_SIZE;
- /* Handle the case where we had a run of missing pages
- * and then one shows up
- */
- if (page && !page_true)
- start_addr = my_addr;
- } else
- break;
- }
- finish_recv:
- /* Drop the kernel's reference to this page */
- if (page)
- page_cache_release(page);
- if (vm_file)
- fput_atomic(vm_file);
- if (rv)
- return rv;
- i++;
- my_addr += PAGE_SIZE;
- }
- return page_count;
- }
- static long gipc_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
- {
- struct task_struct *current_tsk = current;
- long rv = 0;
- switch (cmd) {
- case GIPC_SEND: {
- unsigned long addrs[ADDR_ENTS], lens[ADDR_ENTS];
- unsigned long *page_bitmap;
- struct page **pages;
- struct vm_area_struct **vmas;
- struct file **files;
- u64 *pgoffs;
- struct gipc_send gs;
- unsigned long page_count, total_page_count;
- int page_copied = 0, physical_pages = 0;
- int i, j;
- struct gipc_queue *queue = NULL;
- struct gipc_pages *pending = NULL;
- rv = copy_from_user(&gs, (void *) arg, sizeof(gs));
- if (rv) {
- printk(KERN_ALERT "Graphene SEND: bad buffer %p\n",
- (void *) arg);
- return -EFAULT;
- }
- if (gs.entries > ADDR_ENTS) {
- printk(KERN_ALERT "Graphene SEND: too many entries\n");
- return -EINVAL;
- }
- rv = copy_from_user(&addrs, gs.addr,
- sizeof(unsigned long) * gs.entries);
- if (rv) {
- printk(KERN_ALERT "Graphene SEND: bad buffer %p\n",
- gs.addr);
- return -EFAULT;
- }
- rv = copy_from_user(&lens, gs.len,
- sizeof(unsigned long) * gs.entries);
- if (rv) {
- printk(KERN_ALERT "Graphene SEND: bad buffer %p\n",
- gs.len);
- return -EFAULT;
- }
- for (j = 0; j < gs.entries; j++) {
- unsigned long addr = addrs[j]; //user pointers
- unsigned long len = lens[j]; //user supplied lens
- if (addr > addr + len) {
- printk(KERN_ALERT "Graphene SEND:"
- " attempt to send %p - %p "
- " by thread %d FAIL: bad length\n",
- (void *) addr, (void *) (addr + len),
- current_tsk->pid);
- return -EINVAL;
- }
- }
- /* Find/allocate the gipc_pages struct for our recipient */
- queue = (struct gipc_queue *) file->private_data;
- if (!queue)
- return -EFAULT;
- /* We want to put pages in the partner buf */
- if (file == GIPC_PAGES(queue)[0].file) {
- pending = &GIPC_PAGES(queue)[1];
- } else {
- pending = &GIPC_PAGES(queue)[0];
- BUG_ON(pending->file != file);
- }
- DEBUG("GIPC_SEND %ld entries to token %lld by thread %d\n",
- gs.entries, queue->token, current_tsk->pid);
- for (j = 0; j < gs.entries; j++) {
- unsigned long addr = addrs[j]; //user pointers
- unsigned long len = lens[j]; //user supplied lens
- int get_page_failed = 0;
- total_page_count = len >> PAGE_SHIFT;
- if (len & ~PAGE_MASK)
- total_page_count++;
- if (!access_ok(VERIFY_READ, addr,
- total_page_count << PAGE_SHIFT)) {
- printk(KERN_ALERT "Graphene SEND:"
- " attempt to send %p - %p (%ld pages) "
- " by thread %d FAIL: bad permission\n",
- (void *) addr, (void *) (addr + len),
- total_page_count, current_tsk->pid);
- return -EFAULT;
- }
- DEBUG(" %p - %p (%ld pages) sent by thread %d\n",
- (void *) addr, (void *) (addr + len),
- len >> PAGE_SHIFT, current_tsk->pid);
- while (total_page_count) {
- page_count = total_page_count <= PAGE_BUFS
- ? total_page_count : PAGE_BUFS;
-
- total_page_count -= page_count;
- /* for each of these addresses - check if
- * demand faulting will be triggered
- * if vma is present, but there is no page
- * present(pmd/pud not present or PTE_PRESENT
- * is off) then get_user_pages will trigger
- * the creation of those */
- mutex_lock(&pending->sender_lock);
- if (!pending->sender_buf)
- pending->sender_buf =
- kmem_cache_alloc(gipc_buf_cachep,
- GFP_KERNEL);
- page_bitmap = pending->sender_buf->bitmap;
- pages = pending->sender_buf->pages;
- vmas = pending->sender_buf->vmas;
- files = pending->sender_buf->files;
- pgoffs = pending->sender_buf->pgoffs;
- down_write(¤t_tsk->mm->mmap_sem);
- fill_page_bitmap(current_tsk->mm,
- addr,
- page_bitmap,
- page_count);
- rv = get_pages(current_tsk,
- addr,
- page_bitmap,
- pages,
- vmas,
- page_count);
- if (rv < 0) {
- up_write(¤t_tsk->mm->mmap_sem);
- mutex_unlock(&pending->sender_lock);
- goto out;
- }
- for (i = 0; i < page_count; i++) {
- BUG_ON((!vmas[i]) && (pages[i]));
- if (vmas[i]) {
- files[i] = vmas[i]->vm_file;
- if (files[i]) {
- get_file(files[i]);
- pgoffs[i] = ((addr - vmas[i]->vm_start) >> PAGE_SHIFT)
- + vmas[i]->vm_pgoff;
- } else {
- pgoffs[i] = 0;
- }
- } else {
- files[i] = NULL;
- pgoffs[i] = 0;
- }
- if (pages[i])
- physical_pages++;
- addr += PAGE_SIZE;
- }
- up_write(¤t_tsk->mm->mmap_sem);
- for (i = 0; i < page_count ; i++) {
- /* Put in the pending buffer*/
- if ( ((pending->last + 1)
- & (PAGE_BUFS-1) )
- == pending->next) {
-
- /* The blocking condition for send
- * and recv can't both be true! */
- wake_up_all(&pending->recv);
- wait_event_interruptible(pending->send,
- ( ((pending->last + 1)
- & (PAGE_BUFS-1) )
- != pending->next));
-
- if (signal_pending(current_tsk)) {
- mutex_unlock(&pending->sender_lock);
- return -ERESTARTSYS;
- }
- }
- pending->page_buf[pending->last].page = pages[i];
- pending->page_buf[pending->last].file = files[i];
- pending->page_buf[pending->last].pgoff = pgoffs[i];
- pending->last = ((pending->last + 1) & (PAGE_BUFS-1));
- }
- mutex_unlock(&pending->sender_lock);
- wake_up_all(&pending->recv);
- page_copied += page_count;
- if (get_page_failed)
- goto out;
- }
- if (get_page_failed)
- goto out;
- }
- out:
- DEBUG("GIPC_SEND return to thread %d, %d pages "
- ` "(%d physical pages) are sent\n",
- current_tsk->pid, page_copied, physical_pages);
- rv = page_copied;
- break;
- }
- case GIPC_RECV:
- {
- unsigned long addrs[ADDR_ENTS], lens[ADDR_ENTS];
- int prots[ADDR_ENTS];
- struct gipc_recv gr;
- int page_copied = 0;
- int j;
- struct gipc_pages *pending = NULL;
- struct gipc_queue *queue = (struct gipc_queue *) file->private_data;
- int physical_pages = 0;
- if (!queue)
- return -EFAULT;
- if (file == GIPC_PAGES(queue)[0].file)
- pending = &GIPC_PAGES(queue)[0];
- else {
- pending = &GIPC_PAGES(queue)[1];
- BUG_ON(pending->file != file);
- }
- rv = copy_from_user(&gr, (void *) arg, sizeof(gr));
- if (rv) {
- printk(KERN_ERR "Graphene error: bad buffer %p\n",
- (void *) arg);
- return -EFAULT;
- }
- if (gr.entries > ADDR_ENTS) {
- printk(KERN_ALERT "Graphene RECV: too many entries\n");
- return -EINVAL;
- }
- rv = copy_from_user(&addrs, gr.addr,
- sizeof(unsigned long) * gr.entries);
- if (rv) {
- printk(KERN_ALERT "Graphene RECV: bad buffer %p\n",
- gr.addr);
- return -EFAULT;
- }
- rv = copy_from_user(&lens, gr.len,
- sizeof(unsigned long) * gr.entries);
- if (rv) {
- printk(KERN_ALERT "Graphene RECV: bad buffer %p\n",
- gr.len);
- return -EFAULT;
- }
- rv = copy_from_user(&prots, gr.prot,
- sizeof(int) * gr.entries);
- if (rv) {
- printk(KERN_ALERT "Graphene RECV: bad buffer %p\n",
- gr.prot);
- return -EFAULT;
- }
- down_write(¤t_tsk->mm->mmap_sem);
- mutex_lock(&pending->receiver_lock);
- DEBUG("GIPC_RECV %ld entries to token %lld by thread %d\n",
- gr.entries, queue->token, current_tsk->pid);
- for (j = 0; j < gr.entries; j++) {
- rv = recv_helper(&addrs[j], lens[j], prots[j],
- current_tsk, pending, file,
- &physical_pages);
- if (rv < 0) {
- mutex_unlock(&pending->receiver_lock);
- up_write(¤t_tsk->mm->mmap_sem);
- return rv;
- }
- DEBUG(" %p - %p (%ld pages) received by thread %d\n",
- (void *) addrs[j],
- (void *) addrs[j] + (rv << PAGE_SHIFT),
- rv, current_tsk->pid);
- page_copied += rv;
- }
-
- mutex_unlock(&pending->receiver_lock);
- up_write(¤t_tsk->mm->mmap_sem);
- rv = copy_to_user(((struct gipc_recv *)arg)->addr, addrs,
- sizeof(unsigned long) * gr.entries);
- if (rv) {
- printk(KERN_ERR "Graphene error: bad buffer %p\n",
- (void *) arg);
- return -EFAULT;
- }
- DEBUG("GIPC_RECV return to thread %d, %d pages "
- "(%d physical pages) are received\n",
- current_tsk->pid, page_copied, physical_pages);
- rv = page_copied;
- break;
- }
- case GIPC_CREATE:
- {
- struct gipc_queue *gq = create_gipc_queue(file);
- if (gq == NULL)
- return -ENOMEM;
- DEBUG("GIPC_CREATE token %lld by thread %d\n", gq->token,
- current_tsk->pid);
- rv = gq->token;
- break;
- }
- case GIPC_JOIN:
- {
- struct gipc_queue *gq;
- s64 token = arg;
- #ifdef gipc_get_session
- u32 session = gipc_get_session(current);
- #else
- u32 session = my_gipc_get_session ? my_gipc_get_session(current_tsk) : 0;
- #endif
- if (file->private_data != NULL)
- return -EBUSY;
- /* Search for this token */
- spin_lock(&gdev.lock);
- list_for_each_entry(gq, &gdev.channels, list) {
- if (gq->token == token)
- break;
- }
- /* Fail if we didn't find it */
- if (gq == NULL || &gq->list == &gdev.channels) {
- spin_unlock(&gdev.lock);
- return -ENOENT;
- }
- if (gq->owner != session) {
- spin_unlock(&gdev.lock);
- return -EPERM;
- }
- if (GIPC_PAGES(gq)[1].file == NULL) {
- GIPC_PAGES(gq)[1].file = file;
- file->private_data = gq;
- } else {
- spin_unlock(&gdev.lock);
- return -EBUSY;
- }
- /* Hold the lock until we allocate so only one process
- * gets the queue */
- spin_unlock(&gdev.lock);
- DEBUG("GIPC_JOIN token %lld by thread %d\n", token,
- current_tsk->pid);
- rv = 0;
- break;
- }
- default:
- printk(KERN_ALERT "Graphene unknown ioctl %u %lu\n", cmd, arg);
- rv = -ENOSYS;
- break;
- }
- return rv;
- }
- static int gipc_release(struct inode *inode, struct file *file)
- {
- int free = 0;
- struct gipc_queue *gq = (struct gipc_queue *) file->private_data;
- struct gipc_pages *gp = GIPC_PAGES(gq);
- if (gq) {
- struct gipc_pages *local, *other;
- spin_lock(&gdev.lock);
- if (gp[0].file == file) {
- local = &gp[0];
- other = &gp[1];
- } else {
- local = &gp[1];
- other = &gp[0];
- BUG_ON(local->file != file);
- }
- release_gipc_pages(local);
- /* Detect whether we are the last one out by poisoning the file field */
- local->file = FILE_POISON;
- if (other->file == FILE_POISON) {
- free = 1;
- list_del(&gq->list);
- }
- spin_unlock(&gdev.lock);
- }
- if (free) {
- if (gp[0].sender_buf)
- kmem_cache_free(gipc_buf_cachep, gp[0].sender_buf);
- if (gp[1].sender_buf)
- kmem_cache_free(gipc_buf_cachep, gp[1].sender_buf);
- kmem_cache_free(gipc_queue_cachep, gq);
- }
- file->private_data = NULL;
- return 0;
- }
- static int gipc_open(struct inode *inode, struct file *file)
- {
- file->private_data = NULL;
- return 0;
- }
- static struct file_operations gipc_fops = {
- .owner = THIS_MODULE,
- .release = gipc_release,
- .open = gipc_open,
- .unlocked_ioctl = gipc_ioctl,
- .compat_ioctl = gipc_ioctl,
- .llseek = noop_llseek,
- };
- static struct miscdevice gipc_dev = {
- .minor = GIPC_MINOR,
- .name = "gipc",
- .fops = &gipc_fops,
- .mode = 0666,
- };
- static int __init gipc_init(void)
- {
- int rv = 0;
- #if !defined(CONFIG_GRAPHENE_BULK_IPC) && LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)
- my_do_mmap_pgoff = (do_mmap_pgoff_t)
- kallsyms_lookup_name("do_mmap_pgoff");
- printk(KERN_ERR "resolved symbol do_mmap_pgoff %p\n", my_do_mmap_pgoff);
- if (!my_do_mmap_pgoff) {
- printk(KERN_ERR "Graphene error: "
- "can't find kernel function do_mmap_pgoff\n");
- return -ENOENT;
- }
- #endif
- #ifndef gipc_get_session
- my_gipc_get_session = (void *) kallsyms_lookup_name("gipc_get_session");
- #endif
- #if 0 /* these functions are no longer used, keep here for future use. */
- #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)
- my_tlb_gather_mmu = (tlb_gather_mmu_t)
- kallsyms_lookup_name("tlb_gather_mmu");
- printk(KERN_ERR "resolved symbol tlb_gather_mmu %p\n", my_tlb_gather_mmu);
- if (!my_tlb_gather_mmu) {
- printk(KERN_ERR "Graphene error: "
- "can't find kernel function my_tlb_gather_mmu\n");
- return -ENOENT;
- }
- my_tlb_flush_mmu = (tlb_flush_mmu_t)
- kallsyms_lookup_name("tlb_flush_mmu");
- if (!my_tlb_flush_mmu) {
- printk(KERN_ERR "Graphene error: "
- "can't find kernel function my_tlb_flush_mmu\n");
- return -ENOENT;
- }
- my_tlb_finish_mmu = (tlb_finish_mmu_t)
- kallsyms_lookup_name("tlb_finish_mmu");
- if (!my_tlb_finish_mmu) {
- printk(KERN_ERR "Graphene error: "
- "can't find kernel function my_tlb_finish_mmu\n");
- return -ENOENT;
- }
- #else
- pmmu_gathers = (struct mmu_gather *)
- kallsyms_lookup_name("mmu_gathers");
- if (!pmmu_gathers) {
- printk(KERN_ERR "Graphene error: "
- "can't find kernel function mmu_gathers\n");
- return -ENOENT;
- }
- #endif /* kernel_version < 3.2 */
- kern_free_pages_and_swap_cachep = (free_pages_and_swap_cache_t)
- kallsyms_lookup_name("free_pages_and_swap_cache");
- if (!kern_free_pages_and_swap_cachep) {
- printk(KERN_ERR "Graphene error: "
- "can't find kernel function free_pages_and_swap_cache\n");
- return -ENOENT;
- }
- #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0)
- kern_flush_tlb_mm = (flush_tlb_mm_t)
- kallsyms_lookup_name("flush_tlb_mm");
- if (!kern_flush_tlb_mm) {
- printk(KERN_ERR "Graphene error: "
- "can't find kernel function flush_tlb_mm\n");
- return -ENOENT;
- }
- #endif
- kern_free_pgtables = (free_pgtables_t)
- kallsyms_lookup_name("free_pgtables");
- if (!kern_free_pgtables) {
- printk(KERN_ERR "Graphene error: "
- "can't find kernel function free_pgtables\n");
- return -ENOENT;
- }
- #endif
- /* Register the kmem cache */
- gipc_queue_cachep = kmem_cache_create("gipc_queues",
- sizeof(struct gipc_queue) +
- sizeof(struct gipc_pages) * 2,
- 0,
- SLAB_HWCACHE_ALIGN|
- SLAB_DESTROY_BY_RCU,
- NULL);
- if (!gipc_queue_cachep) {
- printk(KERN_ERR "Graphene error: "
- "failed to create a gipc queues cache\n");
- return -ENOMEM;
- }
- gipc_buf_cachep = kmem_cache_create("gipc_bufs",
- sizeof(struct gipc_sender_buf),
- 0,
- SLAB_HWCACHE_ALIGN|
- SLAB_DESTROY_BY_RCU,
- NULL);
- if (!gipc_buf_cachep) {
- printk(KERN_ERR "Graphene error: "
- "failed to create a gipc buffers cache\n");
- return -ENOMEM;
- }
- INIT_LIST_HEAD(&gdev.channels);
- spin_lock_init(&gdev.lock);
- gdev.max_token = 1;
- rv = misc_register(&gipc_dev);
- if (rv) {
- printk(KERN_ERR "Graphene error: "
- "failed to add a char device (rv=%d)\n", rv);
- return rv;
- }
- printk(KERN_ALERT "Graphene IPC: Hello, world\n");
- return 0;
- }
- static void __exit gipc_exit(void)
- {
- struct gipc_queue *gq, *n;
- spin_lock(&gdev.lock);
- list_for_each_entry_safe(gq, n, &gdev.channels, list) {
- release_gipc_pages(&GIPC_PAGES(gq)[0]);
- release_gipc_pages(&GIPC_PAGES(gq)[1]);
- list_del(&gq->list);
- kmem_cache_free(gipc_queue_cachep, gq);
- }
- spin_unlock(&gdev.lock);
- misc_deregister(&gipc_dev);
- kmem_cache_destroy(gipc_queue_cachep);
- printk(KERN_ALERT "Graphene IPC: Goodbye, cruel world\n");
- }
- module_init(gipc_init);
- module_exit(gipc_exit);
|