1dd314058SDaniel De Graaf /****************************************************************************** 2dd314058SDaniel De Graaf * gntalloc.c 3dd314058SDaniel De Graaf * 4dd314058SDaniel De Graaf * Device for creating grant references (in user-space) that may be shared 5dd314058SDaniel De Graaf * with other domains. 6dd314058SDaniel De Graaf * 7dd314058SDaniel De Graaf * This program is distributed in the hope that it will be useful, 8dd314058SDaniel De Graaf * but WITHOUT ANY WARRANTY; without even the implied warranty of 9dd314058SDaniel De Graaf * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10dd314058SDaniel De Graaf * GNU General Public License for more details. 11dd314058SDaniel De Graaf * 12dd314058SDaniel De Graaf * You should have received a copy of the GNU General Public License 13dd314058SDaniel De Graaf * along with this program; if not, write to the Free Software 14dd314058SDaniel De Graaf * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 15dd314058SDaniel De Graaf */ 16dd314058SDaniel De Graaf 17dd314058SDaniel De Graaf /* 18dd314058SDaniel De Graaf * This driver exists to allow userspace programs in Linux to allocate kernel 19dd314058SDaniel De Graaf * memory that will later be shared with another domain. Without this device, 20dd314058SDaniel De Graaf * Linux userspace programs cannot create grant references. 21dd314058SDaniel De Graaf * 22dd314058SDaniel De Graaf * How this stuff works: 23dd314058SDaniel De Graaf * X -> granting a page to Y 24dd314058SDaniel De Graaf * Y -> mapping the grant from X 25dd314058SDaniel De Graaf * 26dd314058SDaniel De Graaf * 1. X uses the gntalloc device to allocate a page of kernel memory, P. 27dd314058SDaniel De Graaf * 2. X creates an entry in the grant table that says domid(Y) can access P. 28dd314058SDaniel De Graaf * This is done without a hypercall unless the grant table needs expansion. 29dd314058SDaniel De Graaf * 3. X gives the grant reference identifier, GREF, to Y. 30dd314058SDaniel De Graaf * 4. Y maps the page, either directly into kernel memory for use in a backend 31dd314058SDaniel De Graaf * driver, or via a the gntdev device to map into the address space of an 32dd314058SDaniel De Graaf * application running in Y. This is the first point at which Xen does any 33dd314058SDaniel De Graaf * tracking of the page. 34dd314058SDaniel De Graaf * 5. A program in X mmap()s a segment of the gntalloc device that corresponds 35dd314058SDaniel De Graaf * to the shared page, and can now communicate with Y over the shared page. 36dd314058SDaniel De Graaf * 37dd314058SDaniel De Graaf * 38dd314058SDaniel De Graaf * NOTE TO USERSPACE LIBRARIES: 39dd314058SDaniel De Graaf * The grant allocation and mmap()ing are, naturally, two separate operations. 40dd314058SDaniel De Graaf * You set up the sharing by calling the create ioctl() and then the mmap(). 41dd314058SDaniel De Graaf * Teardown requires munmap() and either close() or ioctl(). 42dd314058SDaniel De Graaf * 43dd314058SDaniel De Graaf * WARNING: Since Xen does not allow a guest to forcibly end the use of a grant 44dd314058SDaniel De Graaf * reference, this device can be used to consume kernel memory by leaving grant 45dd314058SDaniel De Graaf * references mapped by another domain when an application exits. Therefore, 46dd314058SDaniel De Graaf * there is a global limit on the number of pages that can be allocated. When 47dd314058SDaniel De Graaf * all references to the page are unmapped, it will be freed during the next 48dd314058SDaniel De Graaf * grant operation. 49dd314058SDaniel De Graaf */ 50dd314058SDaniel De Graaf 51283c0972SJoe Perches #define pr_fmt(fmt) "xen:" KBUILD_MODNAME ": " fmt 52283c0972SJoe Perches 53dd314058SDaniel De Graaf #include <linux/atomic.h> 54dd314058SDaniel De Graaf #include <linux/module.h> 55dd314058SDaniel De Graaf #include <linux/miscdevice.h> 56dd314058SDaniel De Graaf #include <linux/kernel.h> 57dd314058SDaniel De Graaf #include <linux/init.h> 58dd314058SDaniel De Graaf #include <linux/slab.h> 59dd314058SDaniel De Graaf #include <linux/fs.h> 60dd314058SDaniel De Graaf #include <linux/device.h> 61dd314058SDaniel De Graaf #include <linux/mm.h> 62dd314058SDaniel De Graaf #include <linux/uaccess.h> 63dd314058SDaniel De Graaf #include <linux/types.h> 64dd314058SDaniel De Graaf #include <linux/list.h> 65bdc612dcSDaniel De Graaf #include <linux/highmem.h> 66dd314058SDaniel De Graaf 67dd314058SDaniel De Graaf #include <xen/xen.h> 68dd314058SDaniel De Graaf #include <xen/page.h> 69dd314058SDaniel De Graaf #include <xen/grant_table.h> 70dd314058SDaniel De Graaf #include <xen/gntalloc.h> 71bdc612dcSDaniel De Graaf #include <xen/events.h> 72dd314058SDaniel De Graaf 73dd314058SDaniel De Graaf static int limit = 1024; 74dd314058SDaniel De Graaf module_param(limit, int, 0644); 75dd314058SDaniel De Graaf MODULE_PARM_DESC(limit, "Maximum number of grants that may be allocated by " 76dd314058SDaniel De Graaf "the gntalloc device"); 77dd314058SDaniel De Graaf 78dd314058SDaniel De Graaf static LIST_HEAD(gref_list); 798ca19a89SDaniel De Graaf static DEFINE_MUTEX(gref_mutex); 80dd314058SDaniel De Graaf static int gref_size; 81dd314058SDaniel De Graaf 82bdc612dcSDaniel De Graaf struct notify_info { 83bdc612dcSDaniel De Graaf uint16_t pgoff:12; /* Bits 0-11: Offset of the byte to clear */ 84bdc612dcSDaniel De Graaf uint16_t flags:2; /* Bits 12-13: Unmap notification flags */ 85bdc612dcSDaniel De Graaf int event; /* Port (event channel) to notify */ 86bdc612dcSDaniel De Graaf }; 87bdc612dcSDaniel De Graaf 88dd314058SDaniel De Graaf /* Metadata on a grant reference. */ 89dd314058SDaniel De Graaf struct gntalloc_gref { 90dd314058SDaniel De Graaf struct list_head next_gref; /* list entry gref_list */ 91dd314058SDaniel De Graaf struct list_head next_file; /* list entry file->list, if open */ 92dd314058SDaniel De Graaf struct page *page; /* The shared page */ 93dd314058SDaniel De Graaf uint64_t file_index; /* File offset for mmap() */ 94dd314058SDaniel De Graaf unsigned int users; /* Use count - when zero, waiting on Xen */ 95dd314058SDaniel De Graaf grant_ref_t gref_id; /* The grant reference number */ 96bdc612dcSDaniel De Graaf struct notify_info notify; /* Unmap notification */ 97dd314058SDaniel De Graaf }; 98dd314058SDaniel De Graaf 99dd314058SDaniel De Graaf struct gntalloc_file_private_data { 100dd314058SDaniel De Graaf struct list_head list; 101dd314058SDaniel De Graaf uint64_t index; 102dd314058SDaniel De Graaf }; 103dd314058SDaniel De Graaf 104243082e0SDaniel De Graaf struct gntalloc_vma_private_data { 105243082e0SDaniel De Graaf struct gntalloc_gref *gref; 106243082e0SDaniel De Graaf int users; 107243082e0SDaniel De Graaf int count; 108243082e0SDaniel De Graaf }; 109243082e0SDaniel De Graaf 110dd314058SDaniel De Graaf static void __del_gref(struct gntalloc_gref *gref); 111dd314058SDaniel De Graaf 112dd314058SDaniel De Graaf static void do_cleanup(void) 113dd314058SDaniel De Graaf { 114dd314058SDaniel De Graaf struct gntalloc_gref *gref, *n; 115dd314058SDaniel De Graaf list_for_each_entry_safe(gref, n, &gref_list, next_gref) { 116dd314058SDaniel De Graaf if (!gref->users) 117dd314058SDaniel De Graaf __del_gref(gref); 118dd314058SDaniel De Graaf } 119dd314058SDaniel De Graaf } 120dd314058SDaniel De Graaf 121dd314058SDaniel De Graaf static int add_grefs(struct ioctl_gntalloc_alloc_gref *op, 122dd314058SDaniel De Graaf uint32_t *gref_ids, struct gntalloc_file_private_data *priv) 123dd314058SDaniel De Graaf { 124dd314058SDaniel De Graaf int i, rc, readonly; 125dd314058SDaniel De Graaf LIST_HEAD(queue_gref); 126dd314058SDaniel De Graaf LIST_HEAD(queue_file); 127*5903c6bdSDavid Vrabel struct gntalloc_gref *gref, *next; 128dd314058SDaniel De Graaf 129dd314058SDaniel De Graaf readonly = !(op->flags & GNTALLOC_FLAG_WRITABLE); 130dd314058SDaniel De Graaf rc = -ENOMEM; 131dd314058SDaniel De Graaf for (i = 0; i < op->count; i++) { 132dd314058SDaniel De Graaf gref = kzalloc(sizeof(*gref), GFP_KERNEL); 133dd314058SDaniel De Graaf if (!gref) 134dd314058SDaniel De Graaf goto undo; 135dd314058SDaniel De Graaf list_add_tail(&gref->next_gref, &queue_gref); 136dd314058SDaniel De Graaf list_add_tail(&gref->next_file, &queue_file); 137dd314058SDaniel De Graaf gref->users = 1; 138dd314058SDaniel De Graaf gref->file_index = op->index + i * PAGE_SIZE; 139dd314058SDaniel De Graaf gref->page = alloc_page(GFP_KERNEL|__GFP_ZERO); 140dd314058SDaniel De Graaf if (!gref->page) 141dd314058SDaniel De Graaf goto undo; 142dd314058SDaniel De Graaf 143dd314058SDaniel De Graaf /* Grant foreign access to the page. */ 144e9de2e5fSDavid Vrabel rc = gnttab_grant_foreign_access(op->domid, 145dd314058SDaniel De Graaf pfn_to_mfn(page_to_pfn(gref->page)), readonly); 146e9de2e5fSDavid Vrabel if (rc < 0) 147dd314058SDaniel De Graaf goto undo; 148e9de2e5fSDavid Vrabel gref_ids[i] = gref->gref_id = rc; 149dd314058SDaniel De Graaf } 150dd314058SDaniel De Graaf 151dd314058SDaniel De Graaf /* Add to gref lists. */ 1528ca19a89SDaniel De Graaf mutex_lock(&gref_mutex); 153dd314058SDaniel De Graaf list_splice_tail(&queue_gref, &gref_list); 154dd314058SDaniel De Graaf list_splice_tail(&queue_file, &priv->list); 1558ca19a89SDaniel De Graaf mutex_unlock(&gref_mutex); 156dd314058SDaniel De Graaf 157dd314058SDaniel De Graaf return 0; 158dd314058SDaniel De Graaf 159dd314058SDaniel De Graaf undo: 1608ca19a89SDaniel De Graaf mutex_lock(&gref_mutex); 161dd314058SDaniel De Graaf gref_size -= (op->count - i); 162dd314058SDaniel De Graaf 163*5903c6bdSDavid Vrabel list_for_each_entry_safe(gref, next, &queue_file, next_file) { 164*5903c6bdSDavid Vrabel list_del(&gref->next_file); 165dd314058SDaniel De Graaf __del_gref(gref); 166dd314058SDaniel De Graaf } 167dd314058SDaniel De Graaf 168dd314058SDaniel De Graaf /* It's possible for the target domain to map the just-allocated grant 169dd314058SDaniel De Graaf * references by blindly guessing their IDs; if this is done, then 170dd314058SDaniel De Graaf * __del_gref will leave them in the queue_gref list. They need to be 171dd314058SDaniel De Graaf * added to the global list so that we can free them when they are no 172dd314058SDaniel De Graaf * longer referenced. 173dd314058SDaniel De Graaf */ 174dd314058SDaniel De Graaf if (unlikely(!list_empty(&queue_gref))) 175dd314058SDaniel De Graaf list_splice_tail(&queue_gref, &gref_list); 1768ca19a89SDaniel De Graaf mutex_unlock(&gref_mutex); 177dd314058SDaniel De Graaf return rc; 178dd314058SDaniel De Graaf } 179dd314058SDaniel De Graaf 180dd314058SDaniel De Graaf static void __del_gref(struct gntalloc_gref *gref) 181dd314058SDaniel De Graaf { 182bdc612dcSDaniel De Graaf if (gref->notify.flags & UNMAP_NOTIFY_CLEAR_BYTE) { 183bdc612dcSDaniel De Graaf uint8_t *tmp = kmap(gref->page); 184bdc612dcSDaniel De Graaf tmp[gref->notify.pgoff] = 0; 185bdc612dcSDaniel De Graaf kunmap(gref->page); 186bdc612dcSDaniel De Graaf } 1870cc678f8SDaniel De Graaf if (gref->notify.flags & UNMAP_NOTIFY_SEND_EVENT) { 188bdc612dcSDaniel De Graaf notify_remote_via_evtchn(gref->notify.event); 1890cc678f8SDaniel De Graaf evtchn_put(gref->notify.event); 1900cc678f8SDaniel De Graaf } 191bdc612dcSDaniel De Graaf 192bdc612dcSDaniel De Graaf gref->notify.flags = 0; 193bdc612dcSDaniel De Graaf 194e9de2e5fSDavid Vrabel if (gref->gref_id) { 195dd314058SDaniel De Graaf if (gnttab_query_foreign_access(gref->gref_id)) 196dd314058SDaniel De Graaf return; 197dd314058SDaniel De Graaf 198dd314058SDaniel De Graaf if (!gnttab_end_foreign_access_ref(gref->gref_id, 0)) 199dd314058SDaniel De Graaf return; 2000105d2b4SDaniel De Graaf 2010105d2b4SDaniel De Graaf gnttab_free_grant_reference(gref->gref_id); 202dd314058SDaniel De Graaf } 203dd314058SDaniel De Graaf 204dd314058SDaniel De Graaf gref_size--; 205dd314058SDaniel De Graaf list_del(&gref->next_gref); 206dd314058SDaniel De Graaf 207dd314058SDaniel De Graaf if (gref->page) 208dd314058SDaniel De Graaf __free_page(gref->page); 209dd314058SDaniel De Graaf 210dd314058SDaniel De Graaf kfree(gref); 211dd314058SDaniel De Graaf } 212dd314058SDaniel De Graaf 213dd314058SDaniel De Graaf /* finds contiguous grant references in a file, returns the first */ 214dd314058SDaniel De Graaf static struct gntalloc_gref *find_grefs(struct gntalloc_file_private_data *priv, 215dd314058SDaniel De Graaf uint64_t index, uint32_t count) 216dd314058SDaniel De Graaf { 217dd314058SDaniel De Graaf struct gntalloc_gref *rv = NULL, *gref; 218dd314058SDaniel De Graaf list_for_each_entry(gref, &priv->list, next_file) { 219dd314058SDaniel De Graaf if (gref->file_index == index && !rv) 220dd314058SDaniel De Graaf rv = gref; 221dd314058SDaniel De Graaf if (rv) { 222dd314058SDaniel De Graaf if (gref->file_index != index) 223dd314058SDaniel De Graaf return NULL; 224dd314058SDaniel De Graaf index += PAGE_SIZE; 225dd314058SDaniel De Graaf count--; 226dd314058SDaniel De Graaf if (count == 0) 227dd314058SDaniel De Graaf return rv; 228dd314058SDaniel De Graaf } 229dd314058SDaniel De Graaf } 230dd314058SDaniel De Graaf return NULL; 231dd314058SDaniel De Graaf } 232dd314058SDaniel De Graaf 233dd314058SDaniel De Graaf /* 234dd314058SDaniel De Graaf * ------------------------------------- 235dd314058SDaniel De Graaf * File operations. 236dd314058SDaniel De Graaf * ------------------------------------- 237dd314058SDaniel De Graaf */ 238dd314058SDaniel De Graaf static int gntalloc_open(struct inode *inode, struct file *filp) 239dd314058SDaniel De Graaf { 240dd314058SDaniel De Graaf struct gntalloc_file_private_data *priv; 241dd314058SDaniel De Graaf 242dd314058SDaniel De Graaf priv = kzalloc(sizeof(*priv), GFP_KERNEL); 243dd314058SDaniel De Graaf if (!priv) 244dd314058SDaniel De Graaf goto out_nomem; 245dd314058SDaniel De Graaf INIT_LIST_HEAD(&priv->list); 246dd314058SDaniel De Graaf 247dd314058SDaniel De Graaf filp->private_data = priv; 248dd314058SDaniel De Graaf 249dd314058SDaniel De Graaf pr_debug("%s: priv %p\n", __func__, priv); 250dd314058SDaniel De Graaf 251dd314058SDaniel De Graaf return 0; 252dd314058SDaniel De Graaf 253dd314058SDaniel De Graaf out_nomem: 254dd314058SDaniel De Graaf return -ENOMEM; 255dd314058SDaniel De Graaf } 256dd314058SDaniel De Graaf 257dd314058SDaniel De Graaf static int gntalloc_release(struct inode *inode, struct file *filp) 258dd314058SDaniel De Graaf { 259dd314058SDaniel De Graaf struct gntalloc_file_private_data *priv = filp->private_data; 260dd314058SDaniel De Graaf struct gntalloc_gref *gref; 261dd314058SDaniel De Graaf 262dd314058SDaniel De Graaf pr_debug("%s: priv %p\n", __func__, priv); 263dd314058SDaniel De Graaf 2648ca19a89SDaniel De Graaf mutex_lock(&gref_mutex); 265dd314058SDaniel De Graaf while (!list_empty(&priv->list)) { 266dd314058SDaniel De Graaf gref = list_entry(priv->list.next, 267dd314058SDaniel De Graaf struct gntalloc_gref, next_file); 268dd314058SDaniel De Graaf list_del(&gref->next_file); 269dd314058SDaniel De Graaf gref->users--; 270dd314058SDaniel De Graaf if (gref->users == 0) 271dd314058SDaniel De Graaf __del_gref(gref); 272dd314058SDaniel De Graaf } 273dd314058SDaniel De Graaf kfree(priv); 2748ca19a89SDaniel De Graaf mutex_unlock(&gref_mutex); 275dd314058SDaniel De Graaf 276dd314058SDaniel De Graaf return 0; 277dd314058SDaniel De Graaf } 278dd314058SDaniel De Graaf 279dd314058SDaniel De Graaf static long gntalloc_ioctl_alloc(struct gntalloc_file_private_data *priv, 280dd314058SDaniel De Graaf struct ioctl_gntalloc_alloc_gref __user *arg) 281dd314058SDaniel De Graaf { 282dd314058SDaniel De Graaf int rc = 0; 283dd314058SDaniel De Graaf struct ioctl_gntalloc_alloc_gref op; 284dd314058SDaniel De Graaf uint32_t *gref_ids; 285dd314058SDaniel De Graaf 286dd314058SDaniel De Graaf pr_debug("%s: priv %p\n", __func__, priv); 287dd314058SDaniel De Graaf 288dd314058SDaniel De Graaf if (copy_from_user(&op, arg, sizeof(op))) { 289dd314058SDaniel De Graaf rc = -EFAULT; 290dd314058SDaniel De Graaf goto out; 291dd314058SDaniel De Graaf } 292dd314058SDaniel De Graaf 29321643e69SDan Carpenter gref_ids = kcalloc(op.count, sizeof(gref_ids[0]), GFP_TEMPORARY); 294dd314058SDaniel De Graaf if (!gref_ids) { 295dd314058SDaniel De Graaf rc = -ENOMEM; 296dd314058SDaniel De Graaf goto out; 297dd314058SDaniel De Graaf } 298dd314058SDaniel De Graaf 2998ca19a89SDaniel De Graaf mutex_lock(&gref_mutex); 300dd314058SDaniel De Graaf /* Clean up pages that were at zero (local) users but were still mapped 301dd314058SDaniel De Graaf * by remote domains. Since those pages count towards the limit that we 302dd314058SDaniel De Graaf * are about to enforce, removing them here is a good idea. 303dd314058SDaniel De Graaf */ 304dd314058SDaniel De Graaf do_cleanup(); 305dd314058SDaniel De Graaf if (gref_size + op.count > limit) { 3068ca19a89SDaniel De Graaf mutex_unlock(&gref_mutex); 307dd314058SDaniel De Graaf rc = -ENOSPC; 308dd314058SDaniel De Graaf goto out_free; 309dd314058SDaniel De Graaf } 310dd314058SDaniel De Graaf gref_size += op.count; 311dd314058SDaniel De Graaf op.index = priv->index; 312dd314058SDaniel De Graaf priv->index += op.count * PAGE_SIZE; 3138ca19a89SDaniel De Graaf mutex_unlock(&gref_mutex); 314dd314058SDaniel De Graaf 315dd314058SDaniel De Graaf rc = add_grefs(&op, gref_ids, priv); 316dd314058SDaniel De Graaf if (rc < 0) 317dd314058SDaniel De Graaf goto out_free; 318dd314058SDaniel De Graaf 319dd314058SDaniel De Graaf /* Once we finish add_grefs, it is unsafe to touch the new reference, 320dd314058SDaniel De Graaf * since it is possible for a concurrent ioctl to remove it (by guessing 321dd314058SDaniel De Graaf * its index). If the userspace application doesn't provide valid memory 322dd314058SDaniel De Graaf * to write the IDs to, then it will need to close the file in order to 323dd314058SDaniel De Graaf * release - which it will do by segfaulting when it tries to access the 324dd314058SDaniel De Graaf * IDs to close them. 325dd314058SDaniel De Graaf */ 326dd314058SDaniel De Graaf if (copy_to_user(arg, &op, sizeof(op))) { 327dd314058SDaniel De Graaf rc = -EFAULT; 328dd314058SDaniel De Graaf goto out_free; 329dd314058SDaniel De Graaf } 330dd314058SDaniel De Graaf if (copy_to_user(arg->gref_ids, gref_ids, 331dd314058SDaniel De Graaf sizeof(gref_ids[0]) * op.count)) { 332dd314058SDaniel De Graaf rc = -EFAULT; 333dd314058SDaniel De Graaf goto out_free; 334dd314058SDaniel De Graaf } 335dd314058SDaniel De Graaf 336dd314058SDaniel De Graaf out_free: 337dd314058SDaniel De Graaf kfree(gref_ids); 338dd314058SDaniel De Graaf out: 339dd314058SDaniel De Graaf return rc; 340dd314058SDaniel De Graaf } 341dd314058SDaniel De Graaf 342dd314058SDaniel De Graaf static long gntalloc_ioctl_dealloc(struct gntalloc_file_private_data *priv, 343dd314058SDaniel De Graaf void __user *arg) 344dd314058SDaniel De Graaf { 345dd314058SDaniel De Graaf int i, rc = 0; 346dd314058SDaniel De Graaf struct ioctl_gntalloc_dealloc_gref op; 347dd314058SDaniel De Graaf struct gntalloc_gref *gref, *n; 348dd314058SDaniel De Graaf 349dd314058SDaniel De Graaf pr_debug("%s: priv %p\n", __func__, priv); 350dd314058SDaniel De Graaf 351dd314058SDaniel De Graaf if (copy_from_user(&op, arg, sizeof(op))) { 352dd314058SDaniel De Graaf rc = -EFAULT; 353dd314058SDaniel De Graaf goto dealloc_grant_out; 354dd314058SDaniel De Graaf } 355dd314058SDaniel De Graaf 3568ca19a89SDaniel De Graaf mutex_lock(&gref_mutex); 357dd314058SDaniel De Graaf gref = find_grefs(priv, op.index, op.count); 358dd314058SDaniel De Graaf if (gref) { 359dd314058SDaniel De Graaf /* Remove from the file list only, and decrease reference count. 360dd314058SDaniel De Graaf * The later call to do_cleanup() will remove from gref_list and 361dd314058SDaniel De Graaf * free the memory if the pages aren't mapped anywhere. 362dd314058SDaniel De Graaf */ 363dd314058SDaniel De Graaf for (i = 0; i < op.count; i++) { 364dd314058SDaniel De Graaf n = list_entry(gref->next_file.next, 365dd314058SDaniel De Graaf struct gntalloc_gref, next_file); 366dd314058SDaniel De Graaf list_del(&gref->next_file); 367dd314058SDaniel De Graaf gref->users--; 368dd314058SDaniel De Graaf gref = n; 369dd314058SDaniel De Graaf } 370dd314058SDaniel De Graaf } else { 371dd314058SDaniel De Graaf rc = -EINVAL; 372dd314058SDaniel De Graaf } 373dd314058SDaniel De Graaf 374dd314058SDaniel De Graaf do_cleanup(); 375dd314058SDaniel De Graaf 3768ca19a89SDaniel De Graaf mutex_unlock(&gref_mutex); 377dd314058SDaniel De Graaf dealloc_grant_out: 378dd314058SDaniel De Graaf return rc; 379dd314058SDaniel De Graaf } 380dd314058SDaniel De Graaf 381bdc612dcSDaniel De Graaf static long gntalloc_ioctl_unmap_notify(struct gntalloc_file_private_data *priv, 382bdc612dcSDaniel De Graaf void __user *arg) 383bdc612dcSDaniel De Graaf { 384bdc612dcSDaniel De Graaf struct ioctl_gntalloc_unmap_notify op; 385bdc612dcSDaniel De Graaf struct gntalloc_gref *gref; 386bdc612dcSDaniel De Graaf uint64_t index; 387bdc612dcSDaniel De Graaf int pgoff; 388bdc612dcSDaniel De Graaf int rc; 389bdc612dcSDaniel De Graaf 390bdc612dcSDaniel De Graaf if (copy_from_user(&op, arg, sizeof(op))) 391bdc612dcSDaniel De Graaf return -EFAULT; 392bdc612dcSDaniel De Graaf 393bdc612dcSDaniel De Graaf index = op.index & ~(PAGE_SIZE - 1); 394bdc612dcSDaniel De Graaf pgoff = op.index & (PAGE_SIZE - 1); 395bdc612dcSDaniel De Graaf 3968ca19a89SDaniel De Graaf mutex_lock(&gref_mutex); 397bdc612dcSDaniel De Graaf 398bdc612dcSDaniel De Graaf gref = find_grefs(priv, index, 1); 399bdc612dcSDaniel De Graaf if (!gref) { 400bdc612dcSDaniel De Graaf rc = -ENOENT; 401bdc612dcSDaniel De Graaf goto unlock_out; 402bdc612dcSDaniel De Graaf } 403bdc612dcSDaniel De Graaf 404bdc612dcSDaniel De Graaf if (op.action & ~(UNMAP_NOTIFY_CLEAR_BYTE|UNMAP_NOTIFY_SEND_EVENT)) { 405bdc612dcSDaniel De Graaf rc = -EINVAL; 406bdc612dcSDaniel De Graaf goto unlock_out; 407bdc612dcSDaniel De Graaf } 408bdc612dcSDaniel De Graaf 4090cc678f8SDaniel De Graaf /* We need to grab a reference to the event channel we are going to use 4100cc678f8SDaniel De Graaf * to send the notify before releasing the reference we may already have 4110cc678f8SDaniel De Graaf * (if someone has called this ioctl twice). This is required so that 4120cc678f8SDaniel De Graaf * it is possible to change the clear_byte part of the notification 4130cc678f8SDaniel De Graaf * without disturbing the event channel part, which may now be the last 4140cc678f8SDaniel De Graaf * reference to that event channel. 4150cc678f8SDaniel De Graaf */ 4160cc678f8SDaniel De Graaf if (op.action & UNMAP_NOTIFY_SEND_EVENT) { 4170cc678f8SDaniel De Graaf if (evtchn_get(op.event_channel_port)) { 4180cc678f8SDaniel De Graaf rc = -EINVAL; 4190cc678f8SDaniel De Graaf goto unlock_out; 4200cc678f8SDaniel De Graaf } 4210cc678f8SDaniel De Graaf } 4220cc678f8SDaniel De Graaf 4230cc678f8SDaniel De Graaf if (gref->notify.flags & UNMAP_NOTIFY_SEND_EVENT) 4240cc678f8SDaniel De Graaf evtchn_put(gref->notify.event); 4250cc678f8SDaniel De Graaf 426bdc612dcSDaniel De Graaf gref->notify.flags = op.action; 427bdc612dcSDaniel De Graaf gref->notify.pgoff = pgoff; 428bdc612dcSDaniel De Graaf gref->notify.event = op.event_channel_port; 429bdc612dcSDaniel De Graaf rc = 0; 4308ca19a89SDaniel De Graaf 431bdc612dcSDaniel De Graaf unlock_out: 4328ca19a89SDaniel De Graaf mutex_unlock(&gref_mutex); 433bdc612dcSDaniel De Graaf return rc; 434bdc612dcSDaniel De Graaf } 435bdc612dcSDaniel De Graaf 436dd314058SDaniel De Graaf static long gntalloc_ioctl(struct file *filp, unsigned int cmd, 437dd314058SDaniel De Graaf unsigned long arg) 438dd314058SDaniel De Graaf { 439dd314058SDaniel De Graaf struct gntalloc_file_private_data *priv = filp->private_data; 440dd314058SDaniel De Graaf 441dd314058SDaniel De Graaf switch (cmd) { 442dd314058SDaniel De Graaf case IOCTL_GNTALLOC_ALLOC_GREF: 443dd314058SDaniel De Graaf return gntalloc_ioctl_alloc(priv, (void __user *)arg); 444dd314058SDaniel De Graaf 445dd314058SDaniel De Graaf case IOCTL_GNTALLOC_DEALLOC_GREF: 446dd314058SDaniel De Graaf return gntalloc_ioctl_dealloc(priv, (void __user *)arg); 447dd314058SDaniel De Graaf 448bdc612dcSDaniel De Graaf case IOCTL_GNTALLOC_SET_UNMAP_NOTIFY: 449bdc612dcSDaniel De Graaf return gntalloc_ioctl_unmap_notify(priv, (void __user *)arg); 450bdc612dcSDaniel De Graaf 451dd314058SDaniel De Graaf default: 452dd314058SDaniel De Graaf return -ENOIOCTLCMD; 453dd314058SDaniel De Graaf } 454dd314058SDaniel De Graaf 455dd314058SDaniel De Graaf return 0; 456dd314058SDaniel De Graaf } 457dd314058SDaniel De Graaf 458d79647aeSDaniel De Graaf static void gntalloc_vma_open(struct vm_area_struct *vma) 459d79647aeSDaniel De Graaf { 460243082e0SDaniel De Graaf struct gntalloc_vma_private_data *priv = vma->vm_private_data; 461243082e0SDaniel De Graaf 462243082e0SDaniel De Graaf if (!priv) 463d79647aeSDaniel De Graaf return; 464d79647aeSDaniel De Graaf 4658ca19a89SDaniel De Graaf mutex_lock(&gref_mutex); 466243082e0SDaniel De Graaf priv->users++; 4678ca19a89SDaniel De Graaf mutex_unlock(&gref_mutex); 468d79647aeSDaniel De Graaf } 469d79647aeSDaniel De Graaf 470dd314058SDaniel De Graaf static void gntalloc_vma_close(struct vm_area_struct *vma) 471dd314058SDaniel De Graaf { 472243082e0SDaniel De Graaf struct gntalloc_vma_private_data *priv = vma->vm_private_data; 473243082e0SDaniel De Graaf struct gntalloc_gref *gref, *next; 474243082e0SDaniel De Graaf int i; 475243082e0SDaniel De Graaf 476243082e0SDaniel De Graaf if (!priv) 477dd314058SDaniel De Graaf return; 478dd314058SDaniel De Graaf 4798ca19a89SDaniel De Graaf mutex_lock(&gref_mutex); 480243082e0SDaniel De Graaf priv->users--; 481243082e0SDaniel De Graaf if (priv->users == 0) { 482243082e0SDaniel De Graaf gref = priv->gref; 483243082e0SDaniel De Graaf for (i = 0; i < priv->count; i++) { 484dd314058SDaniel De Graaf gref->users--; 485243082e0SDaniel De Graaf next = list_entry(gref->next_gref.next, 486243082e0SDaniel De Graaf struct gntalloc_gref, next_gref); 487dd314058SDaniel De Graaf if (gref->users == 0) 488dd314058SDaniel De Graaf __del_gref(gref); 489243082e0SDaniel De Graaf gref = next; 490243082e0SDaniel De Graaf } 491243082e0SDaniel De Graaf kfree(priv); 492243082e0SDaniel De Graaf } 4938ca19a89SDaniel De Graaf mutex_unlock(&gref_mutex); 494dd314058SDaniel De Graaf } 495dd314058SDaniel De Graaf 496dd314058SDaniel De Graaf static struct vm_operations_struct gntalloc_vmops = { 497d79647aeSDaniel De Graaf .open = gntalloc_vma_open, 498dd314058SDaniel De Graaf .close = gntalloc_vma_close, 499dd314058SDaniel De Graaf }; 500dd314058SDaniel De Graaf 501dd314058SDaniel De Graaf static int gntalloc_mmap(struct file *filp, struct vm_area_struct *vma) 502dd314058SDaniel De Graaf { 503dd314058SDaniel De Graaf struct gntalloc_file_private_data *priv = filp->private_data; 504243082e0SDaniel De Graaf struct gntalloc_vma_private_data *vm_priv; 505dd314058SDaniel De Graaf struct gntalloc_gref *gref; 506dd314058SDaniel De Graaf int count = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT; 507dd314058SDaniel De Graaf int rv, i; 508dd314058SDaniel De Graaf 509dd314058SDaniel De Graaf if (!(vma->vm_flags & VM_SHARED)) { 510283c0972SJoe Perches pr_err("%s: Mapping must be shared\n", __func__); 511dd314058SDaniel De Graaf return -EINVAL; 512dd314058SDaniel De Graaf } 513dd314058SDaniel De Graaf 514243082e0SDaniel De Graaf vm_priv = kmalloc(sizeof(*vm_priv), GFP_KERNEL); 515243082e0SDaniel De Graaf if (!vm_priv) 516243082e0SDaniel De Graaf return -ENOMEM; 517243082e0SDaniel De Graaf 5188ca19a89SDaniel De Graaf mutex_lock(&gref_mutex); 519243082e0SDaniel De Graaf 520243082e0SDaniel De Graaf pr_debug("%s: priv %p,%p, page %lu+%d\n", __func__, 521243082e0SDaniel De Graaf priv, vm_priv, vma->vm_pgoff, count); 522243082e0SDaniel De Graaf 523dd314058SDaniel De Graaf gref = find_grefs(priv, vma->vm_pgoff << PAGE_SHIFT, count); 524dd314058SDaniel De Graaf if (gref == NULL) { 525dd314058SDaniel De Graaf rv = -ENOENT; 526dd314058SDaniel De Graaf pr_debug("%s: Could not find grant reference", 527dd314058SDaniel De Graaf __func__); 5282e163414SJulia Lawall kfree(vm_priv); 529dd314058SDaniel De Graaf goto out_unlock; 530dd314058SDaniel De Graaf } 531dd314058SDaniel De Graaf 532243082e0SDaniel De Graaf vm_priv->gref = gref; 533243082e0SDaniel De Graaf vm_priv->users = 1; 534243082e0SDaniel De Graaf vm_priv->count = count; 535dd314058SDaniel De Graaf 536243082e0SDaniel De Graaf vma->vm_private_data = vm_priv; 537243082e0SDaniel De Graaf 538314e51b9SKonstantin Khlebnikov vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP; 539dd314058SDaniel De Graaf 540dd314058SDaniel De Graaf vma->vm_ops = &gntalloc_vmops; 541dd314058SDaniel De Graaf 542dd314058SDaniel De Graaf for (i = 0; i < count; i++) { 543dd314058SDaniel De Graaf gref->users++; 544dd314058SDaniel De Graaf rv = vm_insert_page(vma, vma->vm_start + i * PAGE_SIZE, 545dd314058SDaniel De Graaf gref->page); 546dd314058SDaniel De Graaf if (rv) 547dd314058SDaniel De Graaf goto out_unlock; 548dd314058SDaniel De Graaf 549dd314058SDaniel De Graaf gref = list_entry(gref->next_file.next, 550dd314058SDaniel De Graaf struct gntalloc_gref, next_file); 551dd314058SDaniel De Graaf } 552dd314058SDaniel De Graaf rv = 0; 553dd314058SDaniel De Graaf 554dd314058SDaniel De Graaf out_unlock: 5558ca19a89SDaniel De Graaf mutex_unlock(&gref_mutex); 556dd314058SDaniel De Graaf return rv; 557dd314058SDaniel De Graaf } 558dd314058SDaniel De Graaf 559dd314058SDaniel De Graaf static const struct file_operations gntalloc_fops = { 560dd314058SDaniel De Graaf .owner = THIS_MODULE, 561dd314058SDaniel De Graaf .open = gntalloc_open, 562dd314058SDaniel De Graaf .release = gntalloc_release, 563dd314058SDaniel De Graaf .unlocked_ioctl = gntalloc_ioctl, 564dd314058SDaniel De Graaf .mmap = gntalloc_mmap 565dd314058SDaniel De Graaf }; 566dd314058SDaniel De Graaf 567dd314058SDaniel De Graaf /* 568dd314058SDaniel De Graaf * ------------------------------------- 569dd314058SDaniel De Graaf * Module creation/destruction. 570dd314058SDaniel De Graaf * ------------------------------------- 571dd314058SDaniel De Graaf */ 572dd314058SDaniel De Graaf static struct miscdevice gntalloc_miscdev = { 573dd314058SDaniel De Graaf .minor = MISC_DYNAMIC_MINOR, 574dd314058SDaniel De Graaf .name = "xen/gntalloc", 575dd314058SDaniel De Graaf .fops = &gntalloc_fops, 576dd314058SDaniel De Graaf }; 577dd314058SDaniel De Graaf 578dd314058SDaniel De Graaf static int __init gntalloc_init(void) 579dd314058SDaniel De Graaf { 580dd314058SDaniel De Graaf int err; 581dd314058SDaniel De Graaf 582dd314058SDaniel De Graaf if (!xen_domain()) 583dd314058SDaniel De Graaf return -ENODEV; 584dd314058SDaniel De Graaf 585dd314058SDaniel De Graaf err = misc_register(&gntalloc_miscdev); 586dd314058SDaniel De Graaf if (err != 0) { 587283c0972SJoe Perches pr_err("Could not register misc gntalloc device\n"); 588dd314058SDaniel De Graaf return err; 589dd314058SDaniel De Graaf } 590dd314058SDaniel De Graaf 591dd314058SDaniel De Graaf pr_debug("Created grant allocation device at %d,%d\n", 592dd314058SDaniel De Graaf MISC_MAJOR, gntalloc_miscdev.minor); 593dd314058SDaniel De Graaf 594dd314058SDaniel De Graaf return 0; 595dd314058SDaniel De Graaf } 596dd314058SDaniel De Graaf 597dd314058SDaniel De Graaf static void __exit gntalloc_exit(void) 598dd314058SDaniel De Graaf { 599dd314058SDaniel De Graaf misc_deregister(&gntalloc_miscdev); 600dd314058SDaniel De Graaf } 601dd314058SDaniel De Graaf 602dd314058SDaniel De Graaf module_init(gntalloc_init); 603dd314058SDaniel De Graaf module_exit(gntalloc_exit); 604dd314058SDaniel De Graaf 605dd314058SDaniel De Graaf MODULE_LICENSE("GPL"); 606dd314058SDaniel De Graaf MODULE_AUTHOR("Carter Weatherly <carter.weatherly@jhuapl.edu>, " 607dd314058SDaniel De Graaf "Daniel De Graaf <dgdegra@tycho.nsa.gov>"); 608dd314058SDaniel De Graaf MODULE_DESCRIPTION("User-space grant reference allocator driver"); 609