Index: sys/fs/cuse/cuse.c =================================================================== --- sys/fs/cuse/cuse.c +++ sys/fs/cuse/cuse.c @@ -1,6 +1,6 @@ /* $FreeBSD$ */ /*- - * Copyright (c) 2010-2013 Hans Petter Selasky. All rights reserved. + * Copyright (c) 2010-2017 Hans Petter Selasky. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -46,6 +46,7 @@ #include #include #include +#include #include #include #include @@ -57,6 +58,9 @@ #include #include +#include +#include +#include #include #include @@ -69,8 +73,6 @@ */ MODULE_VERSION(cuse4bsd, 1); -#define NBUSY ((uint8_t *)1) - #ifdef FEATURE FEATURE(cuse, "Userspace character devices"); #endif @@ -95,9 +97,12 @@ struct cuse_memory { struct cuse_server *owner; - uint8_t *virtaddr; uint32_t page_count; uint32_t is_allocated; +#define CUSE_MEM_ALLOCATED (1 << 0) +#define CUSE_MEM_SERVER_BUSY (1 << 1) +#define CUSE_MEM_CLIENT_BUSY (1 << 2) +#define CUSE_MEM_OBJECT_BUSY (1 << 3) }; struct cuse_server_dev { @@ -173,7 +178,7 @@ static d_read_t cuse_client_read; static d_write_t cuse_client_write; static d_poll_t cuse_client_poll; -static d_mmap_t cuse_client_mmap; +static d_mmap_single_t cuse_client_mmap_single; static d_kqfilter_t cuse_client_kqfilter; static struct cdevsw cuse_client_devsw = { @@ -186,7 +191,7 @@ .d_read = cuse_client_read, .d_write = cuse_client_write, .d_poll = cuse_client_poll, - .d_mmap = cuse_client_mmap, + .d_mmap_single = cuse_client_mmap_single, .d_kqfilter = cuse_client_kqfilter, }; @@ -196,7 +201,7 @@ static d_read_t cuse_server_read; static d_write_t cuse_server_write; static d_poll_t cuse_server_poll; -static d_mmap_t cuse_server_mmap; +static d_mmap_single_t cuse_server_mmap_single; static struct cdevsw cuse_server_devsw = { .d_version = D_VERSION, @@ -208,7 +213,7 @@ .d_read = cuse_server_read, .d_write = cuse_server_write, .d_poll = cuse_server_poll, - .d_mmap = cuse_server_mmap, + .d_mmap_single = cuse_server_mmap_single, }; static void cuse_client_is_closing(struct cuse_client *); @@ -255,6 +260,21 @@ SYSINIT(cuse_kern_init, SI_SUB_DEVFS, SI_ORDER_ANY, cuse_kern_init, 0); +static void * +cuse_memory_busy_locked(void) +{ + struct cuse_memory *mem; + uint32_t n; + + for (n = 0; n != CUSE_ALLOC_UNIT_MAX; n++) { + mem = &cuse_mem[n]; + + if (mem->is_allocated != 0) + return (mem); + } + return (NULL); +} + static void cuse_kern_uninit(void *arg) { @@ -269,6 +289,8 @@ cuse_lock(); ptr = TAILQ_FIRST(&cuse_server_head); + if (ptr == NULL) + ptr = cuse_memory_busy_locked(); cuse_unlock(); if (ptr == NULL) @@ -396,7 +418,7 @@ } static void -cuse_server_free_memory(struct cuse_server *pcs) +cuse_server_free_memory_locked(struct cuse_server *pcs) { struct cuse_memory *mem; uint32_t n; @@ -404,10 +426,10 @@ for (n = 0; n != CUSE_ALLOC_UNIT_MAX; n++) { mem = &cuse_mem[n]; - /* this memory is never freed */ if (mem->owner == pcs) { mem->owner = NULL; - mem->is_allocated = 0; + mem->page_count = 0; + mem->is_allocated &= ~CUSE_MEM_ALLOCATED; } } } @@ -416,54 +438,16 @@ cuse_server_alloc_memory(struct cuse_server *pcs, struct cuse_memory *mem, uint32_t page_count) { - void *ptr; - int error; cuse_lock(); - - if (mem->virtaddr == NBUSY) { + if (mem->is_allocated != 0) { cuse_unlock(); return (EBUSY); } - if (mem->virtaddr != NULL) { - if (mem->is_allocated != 0) { - cuse_unlock(); - return (EBUSY); - } - if (mem->page_count == page_count) { - mem->is_allocated = 1; - mem->owner = pcs; - cuse_unlock(); - return (0); - } - cuse_unlock(); - return (EBUSY); - } - memset(mem, 0, sizeof(*mem)); - - mem->virtaddr = NBUSY; - - cuse_unlock(); - - ptr = malloc(page_count * PAGE_SIZE, M_CUSE, M_WAITOK | M_ZERO); - if (ptr == NULL) - error = ENOMEM; - else - error = 0; - - cuse_lock(); - - if (error) { - mem->virtaddr = NULL; - cuse_unlock(); - return (error); - } - mem->virtaddr = ptr; + mem->is_allocated |= CUSE_MEM_ALLOCATED; mem->page_count = page_count; - mem->is_allocated = 1; mem->owner = pcs; cuse_unlock(); - return (0); } @@ -670,7 +654,7 @@ cuse_lock(); } - cuse_server_free_memory(pcs); + cuse_server_free_memory_locked(pcs); knlist_clear(&pcs->selinfo.si_note, 1); knlist_destroy(&pcs->selinfo.si_note); @@ -1110,7 +1094,8 @@ cuse_lock(); if (cuse_mem[pai->alloc_nr].owner == pcs) { - cuse_mem[pai->alloc_nr].is_allocated = 0; + cuse_mem[pai->alloc_nr].is_allocated &= ~CUSE_MEM_ALLOCATED; + cuse_mem[pai->alloc_nr].page_count = 0; cuse_mem[pai->alloc_nr].owner = NULL; } else { error = EINVAL; @@ -1274,13 +1259,51 @@ } static int -cuse_server_mmap(struct cdev *dev, vm_ooffset_t offset, vm_paddr_t *paddr, int nprot, vm_memattr_t *memattr) +cuse_pager_populate(vm_object_t vm_obj, vm_pindex_t pidx, int fault_type, + vm_prot_t max_prot, vm_pindex_t *first, vm_pindex_t *last) { - uint32_t page_nr = offset / PAGE_SIZE; + /* let kernel fill in an empty page */ + return (VM_PAGER_BAD); +} + +static int +cuse_pager_ctor(void *handle, vm_ooffset_t size, vm_prot_t prot, + vm_ooffset_t foff, struct ucred *cred, u_short *color) +{ + struct cuse_memory *mem = handle; + + cuse_lock(); + mem->is_allocated |= CUSE_MEM_OBJECT_BUSY; + cuse_unlock(); + + *color = 0; + return (0); +} + +static void +cuse_pager_dtor(void *handle) +{ + struct cuse_memory *mem = handle; + + cuse_lock(); + mem->is_allocated &= ~CUSE_MEM_OBJECT_BUSY; + cuse_unlock(); +} + +static struct cdev_pager_ops cuse_pager_ops = { + .cdev_pg_populate = cuse_pager_populate, + .cdev_pg_ctor = cuse_pager_ctor, + .cdev_pg_dtor = cuse_pager_dtor +}; + +static int +cuse_server_mmap_single(struct cdev *dev, vm_ooffset_t *offset, + vm_size_t size, struct vm_object **object, int nprot) +{ + uint32_t page_nr = *offset / PAGE_SIZE; uint32_t alloc_nr = page_nr / CUSE_ALLOC_PAGES_MAX; struct cuse_memory *mem; struct cuse_server *pcs; - uint8_t *ptr; int error; if (alloc_nr >= CUSE_ALLOC_UNIT_MAX) @@ -1288,35 +1311,49 @@ error = cuse_server_get(&pcs); if (error != 0) - pcs = NULL; + return (error); cuse_lock(); mem = &cuse_mem[alloc_nr]; - /* try to enforce slight ownership */ - if ((pcs != NULL) && (mem->owner != pcs)) { - cuse_unlock(); - return (EINVAL); - } - if (mem->virtaddr == NULL) { + /* enforce ownership */ + if (mem->owner != pcs) { cuse_unlock(); - return (ENOMEM); + return (EPERM); } - if (mem->virtaddr == NBUSY) { + /* serialize */ + if (mem->is_allocated & CUSE_MEM_SERVER_BUSY) { cuse_unlock(); - return (ENOMEM); + return (EBUSY); } + /* verify page offset */ page_nr %= CUSE_ALLOC_PAGES_MAX; - if (page_nr >= mem->page_count) { cuse_unlock(); - return (ENXIO); + return (EINVAL); + } + /* verify mmap size */ + if (size < PAGE_SIZE || size > ((mem->page_count - page_nr) * PAGE_SIZE)) { + cuse_unlock(); + return (EINVAL); } - ptr = mem->virtaddr + (page_nr * PAGE_SIZE); + mem->is_allocated |= CUSE_MEM_SERVER_BUSY; + cuse_unlock(); + + *object = cdev_pager_allocate(mem, OBJT_MGTDEVICE, &cuse_pager_ops, + size, nprot, page_nr * PAGE_SIZE, curthread->td_ucred); + + cuse_lock(); + mem->is_allocated &= ~CUSE_MEM_SERVER_BUSY; cuse_unlock(); - *paddr = vtophys(ptr); + if (*object == NULL) + return (ENXIO); + + /* set new VM object offset to use */ + *offset = 0; + /* success */ return (0); } @@ -1758,14 +1795,14 @@ } static int -cuse_client_mmap(struct cdev *dev, vm_ooffset_t offset, vm_paddr_t *paddr, int nprot, vm_memattr_t *memattr) +cuse_client_mmap_single(struct cdev *dev, vm_ooffset_t *offset, + vm_size_t size, struct vm_object **object, int nprot) { - uint32_t page_nr = offset / PAGE_SIZE; + uint32_t page_nr = *offset / PAGE_SIZE; uint32_t alloc_nr = page_nr / CUSE_ALLOC_PAGES_MAX; struct cuse_memory *mem; struct cuse_server *pcs; struct cuse_client *pcc; - uint8_t *ptr; int error; if (alloc_nr >= CUSE_ALLOC_UNIT_MAX) @@ -1773,37 +1810,53 @@ error = cuse_client_get(&pcc); if (error != 0) - pcs = NULL; - else - pcs = pcc->server; + return (error); cuse_lock(); + + /* get pointer to server */ + pcs = pcc->server; + mem = &cuse_mem[alloc_nr]; - /* try to enforce slight ownership */ - if ((pcs != NULL) && (mem->owner != pcs)) { + /* enforce ownership */ + if (mem->owner != pcs) { cuse_unlock(); - return (EINVAL); - } - if (mem->virtaddr == NULL) { - cuse_unlock(); - return (ENOMEM); + return (EPERM); } - if (mem->virtaddr == NBUSY) { + /* serialize */ + if (mem->is_allocated & CUSE_MEM_CLIENT_BUSY) { cuse_unlock(); - return (ENOMEM); + return (EBUSY); } + /* verify page offset */ page_nr %= CUSE_ALLOC_PAGES_MAX; - if (page_nr >= mem->page_count) { cuse_unlock(); - return (ENXIO); + return (EINVAL); + } + /* verify mmap size */ + if (size < PAGE_SIZE || size > ((mem->page_count - page_nr) * PAGE_SIZE)) { + cuse_unlock(); + return (EINVAL); } - ptr = mem->virtaddr + (page_nr * PAGE_SIZE); + mem->is_allocated |= CUSE_MEM_CLIENT_BUSY; cuse_unlock(); - *paddr = vtophys(ptr); + *object = cdev_pager_allocate(mem, OBJT_MGTDEVICE, &cuse_pager_ops, + size, nprot, page_nr * PAGE_SIZE, curthread->td_ucred); + + cuse_lock(); + mem->is_allocated &= ~CUSE_MEM_CLIENT_BUSY; + cuse_unlock(); + + if (*object == NULL) + return (ENXIO); + + /* set new VM object offset to use */ + *offset = 0; + /* success */ return (0); }