linux_compat.c revision 294636
1219820Sjeff/*- 2219820Sjeff * Copyright (c) 2010 Isilon Systems, Inc. 3219820Sjeff * Copyright (c) 2010 iX Systems, Inc. 4219820Sjeff * Copyright (c) 2010 Panasas, Inc. 5271127Shselasky * Copyright (c) 2013, 2014 Mellanox Technologies, Ltd. 6219820Sjeff * All rights reserved. 7219820Sjeff * 8219820Sjeff * Redistribution and use in source and binary forms, with or without 9219820Sjeff * modification, are permitted provided that the following conditions 10219820Sjeff * are met: 11219820Sjeff * 1. Redistributions of source code must retain the above copyright 12219820Sjeff * notice unmodified, this list of conditions, and the following 13219820Sjeff * disclaimer. 14219820Sjeff * 2. Redistributions in binary form must reproduce the above copyright 15219820Sjeff * notice, this list of conditions and the following disclaimer in the 16219820Sjeff * documentation and/or other materials provided with the distribution. 17219820Sjeff * 18219820Sjeff * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 19219820Sjeff * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20219820Sjeff * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21219820Sjeff * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 22219820Sjeff * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 23219820Sjeff * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24219820Sjeff * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25219820Sjeff * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26219820Sjeff * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27219820Sjeff * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28219820Sjeff */ 29219820Sjeff 30219820Sjeff#include <sys/param.h> 31219820Sjeff#include <sys/systm.h> 32219820Sjeff#include <sys/malloc.h> 33219820Sjeff#include <sys/kernel.h> 34219820Sjeff#include <sys/sysctl.h> 35282513Shselasky#include <sys/proc.h> 36287637Sjhb#include <sys/sglist.h> 37282513Shselasky#include <sys/sleepqueue.h> 38219820Sjeff#include <sys/lock.h> 39219820Sjeff#include <sys/mutex.h> 40219820Sjeff#include <sys/bus.h> 41219820Sjeff#include <sys/fcntl.h> 42219820Sjeff#include <sys/file.h> 43219820Sjeff#include <sys/filio.h> 44248084Sattilio#include <sys/rwlock.h> 45219820Sjeff 46219820Sjeff#include <vm/vm.h> 47219820Sjeff#include <vm/pmap.h> 48219820Sjeff 49219820Sjeff#include <machine/stdarg.h> 50219820Sjeff#include <machine/pmap.h> 51219820Sjeff 52219820Sjeff#include <linux/kobject.h> 53219820Sjeff#include <linux/device.h> 54219820Sjeff#include <linux/slab.h> 55219820Sjeff#include <linux/module.h> 56219820Sjeff#include <linux/cdev.h> 57219820Sjeff#include <linux/file.h> 58219820Sjeff#include <linux/sysfs.h> 59219820Sjeff#include <linux/mm.h> 60219820Sjeff#include <linux/io.h> 61219820Sjeff#include <linux/vmalloc.h> 62282513Shselasky#include <linux/timer.h> 63282513Shselasky#include <linux/netdevice.h> 64219820Sjeff 65219820Sjeff#include <vm/vm_pager.h> 66219820Sjeff 67293151Shselasky#include <linux/workqueue.h> 68293151Shselasky 69219820SjeffMALLOC_DEFINE(M_KMALLOC, "linux", "Linux kmalloc compat"); 70219820Sjeff 71219820Sjeff#include <linux/rbtree.h> 72219820Sjeff/* Undo Linux compat changes. */ 73219820Sjeff#undef RB_ROOT 74219820Sjeff#undef file 75219820Sjeff#undef cdev 76219820Sjeff#define RB_ROOT(head) (head)->rbh_root 77219820Sjeff 78219820Sjeffstruct kobject class_root; 79219820Sjeffstruct device linux_rootdev; 80219820Sjeffstruct class miscclass; 81219820Sjeffstruct list_head pci_drivers; 82219820Sjeffstruct list_head pci_devices; 83282513Shselaskystruct net init_net; 84219820Sjeffspinlock_t pci_lock; 85219820Sjeff 86282513Shselaskyunsigned long linux_timer_hz_mask; 87282513Shselasky 88219820Sjeffint 89219820Sjeffpanic_cmp(struct rb_node *one, struct rb_node *two) 90219820Sjeff{ 91219820Sjeff panic("no cmp"); 92219820Sjeff} 93219820Sjeff 94219820SjeffRB_GENERATE(linux_root, rb_node, __entry, panic_cmp); 95293151Shselasky 96219820Sjeffint 97293151Shselaskykobject_set_name_vargs(struct kobject *kobj, const char *fmt, va_list args) 98293151Shselasky{ 99293151Shselasky va_list tmp_va; 100293151Shselasky int len; 101293151Shselasky char *old; 102293151Shselasky char *name; 103293151Shselasky char dummy; 104293151Shselasky 105293151Shselasky old = kobj->name; 106293151Shselasky 107293151Shselasky if (old && fmt == NULL) 108293151Shselasky return (0); 109293151Shselasky 110293151Shselasky /* compute length of string */ 111293151Shselasky va_copy(tmp_va, args); 112293151Shselasky len = vsnprintf(&dummy, 0, fmt, tmp_va); 113293151Shselasky va_end(tmp_va); 114293151Shselasky 115293151Shselasky /* account for zero termination */ 116293151Shselasky len++; 117293151Shselasky 118293151Shselasky /* check for error */ 119293151Shselasky if (len < 1) 120293151Shselasky return (-EINVAL); 121293151Shselasky 122293151Shselasky /* allocate memory for string */ 123293151Shselasky name = kzalloc(len, GFP_KERNEL); 124293151Shselasky if (name == NULL) 125293151Shselasky return (-ENOMEM); 126293151Shselasky vsnprintf(name, len, fmt, args); 127293151Shselasky kobj->name = name; 128293151Shselasky 129293151Shselasky /* free old string */ 130293151Shselasky kfree(old); 131293151Shselasky 132293151Shselasky /* filter new string */ 133293151Shselasky for (; *name != '\0'; name++) 134293151Shselasky if (*name == '/') 135293151Shselasky *name = '!'; 136293151Shselasky return (0); 137293151Shselasky} 138293151Shselasky 139293151Shselaskyint 140219820Sjeffkobject_set_name(struct kobject *kobj, const char *fmt, ...) 141219820Sjeff{ 142219820Sjeff va_list args; 143219820Sjeff int error; 144219820Sjeff 145219820Sjeff va_start(args, fmt); 146219820Sjeff error = kobject_set_name_vargs(kobj, fmt, args); 147219820Sjeff va_end(args); 148219820Sjeff 149219820Sjeff return (error); 150219820Sjeff} 151219820Sjeff 152219820Sjeffstatic inline int 153219820Sjeffkobject_add_complete(struct kobject *kobj, struct kobject *parent) 154219820Sjeff{ 155219820Sjeff struct kobj_type *t; 156219820Sjeff int error; 157219820Sjeff 158219820Sjeff kobj->parent = kobject_get(parent); 159219820Sjeff error = sysfs_create_dir(kobj); 160219820Sjeff if (error == 0 && kobj->ktype && kobj->ktype->default_attrs) { 161219820Sjeff struct attribute **attr; 162219820Sjeff t = kobj->ktype; 163219820Sjeff 164219820Sjeff for (attr = t->default_attrs; *attr != NULL; attr++) { 165219820Sjeff error = sysfs_create_file(kobj, *attr); 166219820Sjeff if (error) 167219820Sjeff break; 168219820Sjeff } 169219820Sjeff if (error) 170219820Sjeff sysfs_remove_dir(kobj); 171219820Sjeff 172219820Sjeff } 173219820Sjeff return (error); 174219820Sjeff} 175219820Sjeff 176219820Sjeffint 177219820Sjeffkobject_add(struct kobject *kobj, struct kobject *parent, const char *fmt, ...) 178219820Sjeff{ 179219820Sjeff va_list args; 180219820Sjeff int error; 181219820Sjeff 182219820Sjeff va_start(args, fmt); 183219820Sjeff error = kobject_set_name_vargs(kobj, fmt, args); 184219820Sjeff va_end(args); 185219820Sjeff if (error) 186219820Sjeff return (error); 187219820Sjeff 188219820Sjeff return kobject_add_complete(kobj, parent); 189219820Sjeff} 190219820Sjeff 191219820Sjeffvoid 192219820Sjeffkobject_release(struct kref *kref) 193219820Sjeff{ 194219820Sjeff struct kobject *kobj; 195219820Sjeff char *name; 196219820Sjeff 197219820Sjeff kobj = container_of(kref, struct kobject, kref); 198219820Sjeff sysfs_remove_dir(kobj); 199219820Sjeff if (kobj->parent) 200219820Sjeff kobject_put(kobj->parent); 201219820Sjeff kobj->parent = NULL; 202219820Sjeff name = kobj->name; 203219820Sjeff if (kobj->ktype && kobj->ktype->release) 204219820Sjeff kobj->ktype->release(kobj); 205219820Sjeff kfree(name); 206219820Sjeff} 207219820Sjeff 208219820Sjeffstatic void 209219820Sjeffkobject_kfree(struct kobject *kobj) 210219820Sjeff{ 211219820Sjeff kfree(kobj); 212219820Sjeff} 213219820Sjeff 214271127Shselaskystatic void 215271127Shselaskykobject_kfree_name(struct kobject *kobj) 216271127Shselasky{ 217271127Shselasky if (kobj) { 218271127Shselasky kfree(kobj->name); 219271127Shselasky } 220271127Shselasky} 221271127Shselasky 222219820Sjeffstruct kobj_type kfree_type = { .release = kobject_kfree }; 223219820Sjeff 224277139Shselaskystatic void 225277139Shselaskydev_release(struct device *dev) 226277139Shselasky{ 227277139Shselasky pr_debug("dev_release: %s\n", dev_name(dev)); 228277139Shselasky kfree(dev); 229277139Shselasky} 230277139Shselasky 231219820Sjeffstruct device * 232219820Sjeffdevice_create(struct class *class, struct device *parent, dev_t devt, 233219820Sjeff void *drvdata, const char *fmt, ...) 234219820Sjeff{ 235219820Sjeff struct device *dev; 236219820Sjeff va_list args; 237219820Sjeff 238219820Sjeff dev = kzalloc(sizeof(*dev), M_WAITOK); 239219820Sjeff dev->parent = parent; 240219820Sjeff dev->class = class; 241219820Sjeff dev->devt = devt; 242219820Sjeff dev->driver_data = drvdata; 243277139Shselasky dev->release = dev_release; 244219820Sjeff va_start(args, fmt); 245219820Sjeff kobject_set_name_vargs(&dev->kobj, fmt, args); 246219820Sjeff va_end(args); 247219820Sjeff device_register(dev); 248219820Sjeff 249219820Sjeff return (dev); 250219820Sjeff} 251219820Sjeff 252219820Sjeffint 253219820Sjeffkobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype, 254219820Sjeff struct kobject *parent, const char *fmt, ...) 255219820Sjeff{ 256219820Sjeff va_list args; 257219820Sjeff int error; 258219820Sjeff 259219820Sjeff kobject_init(kobj, ktype); 260219820Sjeff kobj->ktype = ktype; 261219820Sjeff kobj->parent = parent; 262219820Sjeff kobj->name = NULL; 263219820Sjeff 264219820Sjeff va_start(args, fmt); 265219820Sjeff error = kobject_set_name_vargs(kobj, fmt, args); 266219820Sjeff va_end(args); 267219820Sjeff if (error) 268219820Sjeff return (error); 269219820Sjeff return kobject_add_complete(kobj, parent); 270219820Sjeff} 271219820Sjeff 272219820Sjeffstatic void 273219820Sjefflinux_file_dtor(void *cdp) 274219820Sjeff{ 275219820Sjeff struct linux_file *filp; 276219820Sjeff 277219820Sjeff filp = cdp; 278251617Sjhb filp->f_op->release(filp->f_vnode, filp); 279251617Sjhb vdrop(filp->f_vnode); 280219820Sjeff kfree(filp); 281219820Sjeff} 282219820Sjeff 283219820Sjeffstatic int 284219820Sjefflinux_dev_open(struct cdev *dev, int oflags, int devtype, struct thread *td) 285219820Sjeff{ 286219820Sjeff struct linux_cdev *ldev; 287219820Sjeff struct linux_file *filp; 288219820Sjeff struct file *file; 289219820Sjeff int error; 290219820Sjeff 291219820Sjeff file = curthread->td_fpop; 292219820Sjeff ldev = dev->si_drv1; 293219820Sjeff if (ldev == NULL) 294219820Sjeff return (ENODEV); 295219820Sjeff filp = kzalloc(sizeof(*filp), GFP_KERNEL); 296219820Sjeff filp->f_dentry = &filp->f_dentry_store; 297219820Sjeff filp->f_op = ldev->ops; 298219820Sjeff filp->f_flags = file->f_flag; 299251617Sjhb vhold(file->f_vnode); 300251617Sjhb filp->f_vnode = file->f_vnode; 301219820Sjeff if (filp->f_op->open) { 302219820Sjeff error = -filp->f_op->open(file->f_vnode, filp); 303219820Sjeff if (error) { 304219820Sjeff kfree(filp); 305219820Sjeff return (error); 306219820Sjeff } 307219820Sjeff } 308219820Sjeff error = devfs_set_cdevpriv(filp, linux_file_dtor); 309219820Sjeff if (error) { 310219820Sjeff filp->f_op->release(file->f_vnode, filp); 311219820Sjeff kfree(filp); 312219820Sjeff return (error); 313219820Sjeff } 314219820Sjeff 315219820Sjeff return 0; 316219820Sjeff} 317219820Sjeff 318219820Sjeffstatic int 319219820Sjefflinux_dev_close(struct cdev *dev, int fflag, int devtype, struct thread *td) 320219820Sjeff{ 321219820Sjeff struct linux_cdev *ldev; 322219820Sjeff struct linux_file *filp; 323219820Sjeff struct file *file; 324219820Sjeff int error; 325219820Sjeff 326219820Sjeff file = curthread->td_fpop; 327219820Sjeff ldev = dev->si_drv1; 328219820Sjeff if (ldev == NULL) 329219820Sjeff return (0); 330219820Sjeff if ((error = devfs_get_cdevpriv((void **)&filp)) != 0) 331219820Sjeff return (error); 332219820Sjeff filp->f_flags = file->f_flag; 333255932Salfred devfs_clear_cdevpriv(); 334255932Salfred 335219820Sjeff 336219820Sjeff return (0); 337219820Sjeff} 338219820Sjeff 339219820Sjeffstatic int 340219820Sjefflinux_dev_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, 341219820Sjeff struct thread *td) 342219820Sjeff{ 343219820Sjeff struct linux_cdev *ldev; 344219820Sjeff struct linux_file *filp; 345219820Sjeff struct file *file; 346219820Sjeff int error; 347219820Sjeff 348219820Sjeff file = curthread->td_fpop; 349219820Sjeff ldev = dev->si_drv1; 350219820Sjeff if (ldev == NULL) 351219820Sjeff return (0); 352219820Sjeff if ((error = devfs_get_cdevpriv((void **)&filp)) != 0) 353219820Sjeff return (error); 354219820Sjeff filp->f_flags = file->f_flag; 355219820Sjeff /* 356219820Sjeff * Linux does not have a generic ioctl copyin/copyout layer. All 357219820Sjeff * linux ioctls must be converted to void ioctls which pass a 358219820Sjeff * pointer to the address of the data. We want the actual user 359219820Sjeff * address so we dereference here. 360219820Sjeff */ 361219820Sjeff data = *(void **)data; 362219820Sjeff if (filp->f_op->unlocked_ioctl) 363219820Sjeff error = -filp->f_op->unlocked_ioctl(filp, cmd, (u_long)data); 364219820Sjeff else 365219820Sjeff error = ENOTTY; 366219820Sjeff 367219820Sjeff return (error); 368219820Sjeff} 369219820Sjeff 370219820Sjeffstatic int 371219820Sjefflinux_dev_read(struct cdev *dev, struct uio *uio, int ioflag) 372219820Sjeff{ 373219820Sjeff struct linux_cdev *ldev; 374219820Sjeff struct linux_file *filp; 375219820Sjeff struct file *file; 376219820Sjeff ssize_t bytes; 377219820Sjeff int error; 378219820Sjeff 379219820Sjeff file = curthread->td_fpop; 380219820Sjeff ldev = dev->si_drv1; 381219820Sjeff if (ldev == NULL) 382219820Sjeff return (0); 383219820Sjeff if ((error = devfs_get_cdevpriv((void **)&filp)) != 0) 384219820Sjeff return (error); 385219820Sjeff filp->f_flags = file->f_flag; 386219820Sjeff if (uio->uio_iovcnt != 1) 387219820Sjeff panic("linux_dev_read: uio %p iovcnt %d", 388219820Sjeff uio, uio->uio_iovcnt); 389219820Sjeff if (filp->f_op->read) { 390219820Sjeff bytes = filp->f_op->read(filp, uio->uio_iov->iov_base, 391219820Sjeff uio->uio_iov->iov_len, &uio->uio_offset); 392219820Sjeff if (bytes >= 0) { 393219820Sjeff uio->uio_iov->iov_base += bytes; 394219820Sjeff uio->uio_iov->iov_len -= bytes; 395219820Sjeff uio->uio_resid -= bytes; 396219820Sjeff } else 397219820Sjeff error = -bytes; 398219820Sjeff } else 399219820Sjeff error = ENXIO; 400219820Sjeff 401219820Sjeff return (error); 402219820Sjeff} 403219820Sjeff 404219820Sjeffstatic int 405219820Sjefflinux_dev_write(struct cdev *dev, struct uio *uio, int ioflag) 406219820Sjeff{ 407219820Sjeff struct linux_cdev *ldev; 408219820Sjeff struct linux_file *filp; 409219820Sjeff struct file *file; 410219820Sjeff ssize_t bytes; 411219820Sjeff int error; 412219820Sjeff 413219820Sjeff file = curthread->td_fpop; 414219820Sjeff ldev = dev->si_drv1; 415219820Sjeff if (ldev == NULL) 416219820Sjeff return (0); 417219820Sjeff if ((error = devfs_get_cdevpriv((void **)&filp)) != 0) 418219820Sjeff return (error); 419219820Sjeff filp->f_flags = file->f_flag; 420219820Sjeff if (uio->uio_iovcnt != 1) 421219820Sjeff panic("linux_dev_write: uio %p iovcnt %d", 422219820Sjeff uio, uio->uio_iovcnt); 423219820Sjeff if (filp->f_op->write) { 424219820Sjeff bytes = filp->f_op->write(filp, uio->uio_iov->iov_base, 425219820Sjeff uio->uio_iov->iov_len, &uio->uio_offset); 426219820Sjeff if (bytes >= 0) { 427219820Sjeff uio->uio_iov->iov_base += bytes; 428219820Sjeff uio->uio_iov->iov_len -= bytes; 429219820Sjeff uio->uio_resid -= bytes; 430219820Sjeff } else 431219820Sjeff error = -bytes; 432219820Sjeff } else 433219820Sjeff error = ENXIO; 434219820Sjeff 435219820Sjeff return (error); 436219820Sjeff} 437219820Sjeff 438219820Sjeffstatic int 439219820Sjefflinux_dev_poll(struct cdev *dev, int events, struct thread *td) 440219820Sjeff{ 441219820Sjeff struct linux_cdev *ldev; 442219820Sjeff struct linux_file *filp; 443219820Sjeff struct file *file; 444219820Sjeff int revents; 445219820Sjeff int error; 446219820Sjeff 447219820Sjeff file = curthread->td_fpop; 448219820Sjeff ldev = dev->si_drv1; 449219820Sjeff if (ldev == NULL) 450219820Sjeff return (0); 451219820Sjeff if ((error = devfs_get_cdevpriv((void **)&filp)) != 0) 452219820Sjeff return (error); 453219820Sjeff filp->f_flags = file->f_flag; 454219820Sjeff if (filp->f_op->poll) 455219820Sjeff revents = filp->f_op->poll(filp, NULL) & events; 456219820Sjeff else 457219820Sjeff revents = 0; 458219820Sjeff 459219820Sjeff return (revents); 460219820Sjeff} 461219820Sjeff 462219820Sjeffstatic int 463219820Sjefflinux_dev_mmap_single(struct cdev *dev, vm_ooffset_t *offset, 464219820Sjeff vm_size_t size, struct vm_object **object, int nprot) 465219820Sjeff{ 466219820Sjeff struct linux_cdev *ldev; 467219820Sjeff struct linux_file *filp; 468219820Sjeff struct file *file; 469219820Sjeff struct vm_area_struct vma; 470219820Sjeff int error; 471219820Sjeff 472219820Sjeff file = curthread->td_fpop; 473219820Sjeff ldev = dev->si_drv1; 474219820Sjeff if (ldev == NULL) 475219820Sjeff return (ENODEV); 476219820Sjeff if ((error = devfs_get_cdevpriv((void **)&filp)) != 0) 477219820Sjeff return (error); 478219820Sjeff filp->f_flags = file->f_flag; 479219820Sjeff vma.vm_start = 0; 480287637Sjhb vma.vm_end = size; 481219820Sjeff vma.vm_pgoff = *offset / PAGE_SIZE; 482219820Sjeff vma.vm_pfn = 0; 483294636Sjhb vma.vm_page_prot = VM_MEMATTR_DEFAULT; 484219820Sjeff if (filp->f_op->mmap) { 485219820Sjeff error = -filp->f_op->mmap(filp, &vma); 486219820Sjeff if (error == 0) { 487287637Sjhb struct sglist *sg; 488287637Sjhb 489287637Sjhb sg = sglist_alloc(1, M_WAITOK); 490287637Sjhb sglist_append_phys(sg, 491287637Sjhb (vm_paddr_t)vma.vm_pfn << PAGE_SHIFT, vma.vm_len); 492287637Sjhb *object = vm_pager_allocate(OBJT_SG, sg, vma.vm_len, 493287637Sjhb nprot, 0, curthread->td_ucred); 494287637Sjhb if (*object == NULL) { 495287637Sjhb sglist_free(sg); 496287637Sjhb return (EINVAL); 497287637Sjhb } 498287637Sjhb *offset = 0; 499287637Sjhb if (vma.vm_page_prot != VM_MEMATTR_DEFAULT) { 500287637Sjhb VM_OBJECT_WLOCK(*object); 501287637Sjhb vm_object_set_memattr(*object, 502287637Sjhb vma.vm_page_prot); 503287637Sjhb VM_OBJECT_WUNLOCK(*object); 504287637Sjhb } 505219820Sjeff } 506219820Sjeff } else 507219820Sjeff error = ENODEV; 508219820Sjeff 509219820Sjeff return (error); 510219820Sjeff} 511219820Sjeff 512219820Sjeffstruct cdevsw linuxcdevsw = { 513219820Sjeff .d_version = D_VERSION, 514219820Sjeff .d_flags = D_TRACKCLOSE, 515219820Sjeff .d_open = linux_dev_open, 516219820Sjeff .d_close = linux_dev_close, 517219820Sjeff .d_read = linux_dev_read, 518219820Sjeff .d_write = linux_dev_write, 519219820Sjeff .d_ioctl = linux_dev_ioctl, 520219820Sjeff .d_mmap_single = linux_dev_mmap_single, 521219820Sjeff .d_poll = linux_dev_poll, 522219820Sjeff}; 523219820Sjeff 524219820Sjeffstatic int 525219820Sjefflinux_file_read(struct file *file, struct uio *uio, struct ucred *active_cred, 526219820Sjeff int flags, struct thread *td) 527219820Sjeff{ 528219820Sjeff struct linux_file *filp; 529219820Sjeff ssize_t bytes; 530219820Sjeff int error; 531219820Sjeff 532219820Sjeff error = 0; 533219820Sjeff filp = (struct linux_file *)file->f_data; 534219820Sjeff filp->f_flags = file->f_flag; 535219820Sjeff if (uio->uio_iovcnt != 1) 536219820Sjeff panic("linux_file_read: uio %p iovcnt %d", 537219820Sjeff uio, uio->uio_iovcnt); 538219820Sjeff if (filp->f_op->read) { 539219820Sjeff bytes = filp->f_op->read(filp, uio->uio_iov->iov_base, 540219820Sjeff uio->uio_iov->iov_len, &uio->uio_offset); 541219820Sjeff if (bytes >= 0) { 542219820Sjeff uio->uio_iov->iov_base += bytes; 543219820Sjeff uio->uio_iov->iov_len -= bytes; 544219820Sjeff uio->uio_resid -= bytes; 545219820Sjeff } else 546219820Sjeff error = -bytes; 547219820Sjeff } else 548219820Sjeff error = ENXIO; 549219820Sjeff 550219820Sjeff return (error); 551219820Sjeff} 552219820Sjeff 553219820Sjeffstatic int 554219820Sjefflinux_file_poll(struct file *file, int events, struct ucred *active_cred, 555219820Sjeff struct thread *td) 556219820Sjeff{ 557219820Sjeff struct linux_file *filp; 558219820Sjeff int revents; 559219820Sjeff 560219820Sjeff filp = (struct linux_file *)file->f_data; 561219820Sjeff filp->f_flags = file->f_flag; 562219820Sjeff if (filp->f_op->poll) 563219820Sjeff revents = filp->f_op->poll(filp, NULL) & events; 564219820Sjeff else 565219820Sjeff revents = 0; 566219820Sjeff 567219820Sjeff return (0); 568219820Sjeff} 569219820Sjeff 570219820Sjeffstatic int 571219820Sjefflinux_file_close(struct file *file, struct thread *td) 572219820Sjeff{ 573219820Sjeff struct linux_file *filp; 574219820Sjeff int error; 575219820Sjeff 576219820Sjeff filp = (struct linux_file *)file->f_data; 577219820Sjeff filp->f_flags = file->f_flag; 578219820Sjeff error = -filp->f_op->release(NULL, filp); 579219820Sjeff funsetown(&filp->f_sigio); 580219820Sjeff kfree(filp); 581219820Sjeff 582219820Sjeff return (error); 583219820Sjeff} 584219820Sjeff 585219820Sjeffstatic int 586219820Sjefflinux_file_ioctl(struct file *fp, u_long cmd, void *data, struct ucred *cred, 587219820Sjeff struct thread *td) 588219820Sjeff{ 589219820Sjeff struct linux_file *filp; 590219820Sjeff int error; 591219820Sjeff 592219820Sjeff filp = (struct linux_file *)fp->f_data; 593219820Sjeff filp->f_flags = fp->f_flag; 594219820Sjeff error = 0; 595219820Sjeff 596219820Sjeff switch (cmd) { 597219820Sjeff case FIONBIO: 598219820Sjeff break; 599219820Sjeff case FIOASYNC: 600219820Sjeff if (filp->f_op->fasync == NULL) 601219820Sjeff break; 602219820Sjeff error = filp->f_op->fasync(0, filp, fp->f_flag & FASYNC); 603219820Sjeff break; 604219820Sjeff case FIOSETOWN: 605219820Sjeff error = fsetown(*(int *)data, &filp->f_sigio); 606219820Sjeff if (error == 0) 607219820Sjeff error = filp->f_op->fasync(0, filp, 608219820Sjeff fp->f_flag & FASYNC); 609219820Sjeff break; 610219820Sjeff case FIOGETOWN: 611219820Sjeff *(int *)data = fgetown(&filp->f_sigio); 612219820Sjeff break; 613219820Sjeff default: 614219820Sjeff error = ENOTTY; 615219820Sjeff break; 616219820Sjeff } 617219820Sjeff return (error); 618219820Sjeff} 619219820Sjeff 620219820Sjeffstruct fileops linuxfileops = { 621219820Sjeff .fo_read = linux_file_read, 622219820Sjeff .fo_poll = linux_file_poll, 623219820Sjeff .fo_close = linux_file_close, 624224914Skib .fo_ioctl = linux_file_ioctl, 625224914Skib .fo_chmod = invfo_chmod, 626224914Skib .fo_chown = invfo_chown, 627254356Sglebius .fo_sendfile = invfo_sendfile, 628219820Sjeff}; 629219820Sjeff 630219820Sjeff/* 631219820Sjeff * Hash of vmmap addresses. This is infrequently accessed and does not 632219820Sjeff * need to be particularly large. This is done because we must store the 633219820Sjeff * caller's idea of the map size to properly unmap. 634219820Sjeff */ 635219820Sjeffstruct vmmap { 636219820Sjeff LIST_ENTRY(vmmap) vm_next; 637219820Sjeff void *vm_addr; 638219820Sjeff unsigned long vm_size; 639219820Sjeff}; 640219820Sjeff 641282513Shselaskystruct vmmaphd { 642282513Shselasky struct vmmap *lh_first; 643282513Shselasky}; 644219820Sjeff#define VMMAP_HASH_SIZE 64 645219820Sjeff#define VMMAP_HASH_MASK (VMMAP_HASH_SIZE - 1) 646219820Sjeff#define VM_HASH(addr) ((uintptr_t)(addr) >> PAGE_SHIFT) & VMMAP_HASH_MASK 647219820Sjeffstatic struct vmmaphd vmmaphead[VMMAP_HASH_SIZE]; 648219820Sjeffstatic struct mtx vmmaplock; 649219820Sjeff 650219820Sjeffstatic void 651219820Sjeffvmmap_add(void *addr, unsigned long size) 652219820Sjeff{ 653219820Sjeff struct vmmap *vmmap; 654219820Sjeff 655219820Sjeff vmmap = kmalloc(sizeof(*vmmap), GFP_KERNEL); 656219820Sjeff mtx_lock(&vmmaplock); 657219820Sjeff vmmap->vm_size = size; 658219820Sjeff vmmap->vm_addr = addr; 659219820Sjeff LIST_INSERT_HEAD(&vmmaphead[VM_HASH(addr)], vmmap, vm_next); 660219820Sjeff mtx_unlock(&vmmaplock); 661219820Sjeff} 662219820Sjeff 663219820Sjeffstatic struct vmmap * 664219820Sjeffvmmap_remove(void *addr) 665219820Sjeff{ 666219820Sjeff struct vmmap *vmmap; 667219820Sjeff 668219820Sjeff mtx_lock(&vmmaplock); 669219820Sjeff LIST_FOREACH(vmmap, &vmmaphead[VM_HASH(addr)], vm_next) 670219820Sjeff if (vmmap->vm_addr == addr) 671219820Sjeff break; 672219820Sjeff if (vmmap) 673219820Sjeff LIST_REMOVE(vmmap, vm_next); 674219820Sjeff mtx_unlock(&vmmaplock); 675219820Sjeff 676219820Sjeff return (vmmap); 677219820Sjeff} 678219820Sjeff 679219820Sjeffvoid * 680219820Sjeff_ioremap_attr(vm_paddr_t phys_addr, unsigned long size, int attr) 681219820Sjeff{ 682219820Sjeff void *addr; 683219820Sjeff 684219820Sjeff addr = pmap_mapdev_attr(phys_addr, size, attr); 685219820Sjeff if (addr == NULL) 686219820Sjeff return (NULL); 687219820Sjeff vmmap_add(addr, size); 688219820Sjeff 689219820Sjeff return (addr); 690219820Sjeff} 691219820Sjeff 692219820Sjeffvoid 693219820Sjeffiounmap(void *addr) 694219820Sjeff{ 695219820Sjeff struct vmmap *vmmap; 696219820Sjeff 697219820Sjeff vmmap = vmmap_remove(addr); 698219820Sjeff if (vmmap == NULL) 699219820Sjeff return; 700219820Sjeff pmap_unmapdev((vm_offset_t)addr, vmmap->vm_size); 701219820Sjeff kfree(vmmap); 702219820Sjeff} 703219820Sjeff 704219820Sjeff 705219820Sjeffvoid * 706219820Sjeffvmap(struct page **pages, unsigned int count, unsigned long flags, int prot) 707219820Sjeff{ 708219820Sjeff vm_offset_t off; 709219820Sjeff size_t size; 710219820Sjeff 711219820Sjeff size = count * PAGE_SIZE; 712254025Sjeff off = kva_alloc(size); 713219820Sjeff if (off == 0) 714219820Sjeff return (NULL); 715219820Sjeff vmmap_add((void *)off, size); 716219820Sjeff pmap_qenter(off, pages, count); 717219820Sjeff 718219820Sjeff return ((void *)off); 719219820Sjeff} 720219820Sjeff 721219820Sjeffvoid 722219820Sjeffvunmap(void *addr) 723219820Sjeff{ 724219820Sjeff struct vmmap *vmmap; 725219820Sjeff 726219820Sjeff vmmap = vmmap_remove(addr); 727219820Sjeff if (vmmap == NULL) 728219820Sjeff return; 729219820Sjeff pmap_qremove((vm_offset_t)addr, vmmap->vm_size / PAGE_SIZE); 730254025Sjeff kva_free((vm_offset_t)addr, vmmap->vm_size); 731219820Sjeff kfree(vmmap); 732219820Sjeff} 733219820Sjeff 734285410Shselaskychar * 735285410Shselaskykvasprintf(gfp_t gfp, const char *fmt, va_list ap) 736285410Shselasky{ 737285410Shselasky unsigned int len; 738285410Shselasky char *p; 739285410Shselasky va_list aq; 740282513Shselasky 741285410Shselasky va_copy(aq, ap); 742285410Shselasky len = vsnprintf(NULL, 0, fmt, aq); 743285410Shselasky va_end(aq); 744285410Shselasky 745285410Shselasky p = kmalloc(len + 1, gfp); 746285410Shselasky if (p != NULL) 747285410Shselasky vsnprintf(p, len + 1, fmt, ap); 748285410Shselasky 749285410Shselasky return (p); 750285410Shselasky} 751285410Shselasky 752282513Shselaskychar * 753282513Shselaskykasprintf(gfp_t gfp, const char *fmt, ...) 754282513Shselasky{ 755282513Shselasky va_list ap; 756282513Shselasky char *p; 757282513Shselasky 758282513Shselasky va_start(ap, fmt); 759282513Shselasky p = kvasprintf(gfp, fmt, ap); 760282513Shselasky va_end(ap); 761282513Shselasky 762285410Shselasky return (p); 763282513Shselasky} 764282513Shselasky 765282513Shselaskystatic int 766282513Shselaskylinux_timer_jiffies_until(unsigned long expires) 767282513Shselasky{ 768282513Shselasky int delta = expires - jiffies; 769282513Shselasky /* guard against already expired values */ 770282513Shselasky if (delta < 1) 771282513Shselasky delta = 1; 772282513Shselasky return (delta); 773282513Shselasky} 774282513Shselasky 775219820Sjeffstatic void 776282513Shselaskylinux_timer_callback_wrapper(void *context) 777282513Shselasky{ 778282513Shselasky struct timer_list *timer; 779282513Shselasky 780282513Shselasky timer = context; 781282513Shselasky timer->function(timer->data); 782282513Shselasky} 783282513Shselasky 784282513Shselaskyvoid 785282513Shselaskymod_timer(struct timer_list *timer, unsigned long expires) 786282513Shselasky{ 787282513Shselasky 788282513Shselasky timer->expires = expires; 789282513Shselasky callout_reset(&timer->timer_callout, 790282513Shselasky linux_timer_jiffies_until(expires), 791282513Shselasky &linux_timer_callback_wrapper, timer); 792282513Shselasky} 793282513Shselasky 794282513Shselaskyvoid 795282513Shselaskyadd_timer(struct timer_list *timer) 796282513Shselasky{ 797282513Shselasky 798282513Shselasky callout_reset(&timer->timer_callout, 799282513Shselasky linux_timer_jiffies_until(timer->expires), 800282513Shselasky &linux_timer_callback_wrapper, timer); 801282513Shselasky} 802282513Shselasky 803282513Shselaskystatic void 804282513Shselaskylinux_timer_init(void *arg) 805282513Shselasky{ 806282513Shselasky 807282513Shselasky /* 808282513Shselasky * Compute an internal HZ value which can divide 2**32 to 809282513Shselasky * avoid timer rounding problems when the tick value wraps 810282513Shselasky * around 2**32: 811282513Shselasky */ 812282513Shselasky linux_timer_hz_mask = 1; 813282513Shselasky while (linux_timer_hz_mask < (unsigned long)hz) 814282513Shselasky linux_timer_hz_mask *= 2; 815282513Shselasky linux_timer_hz_mask--; 816282513Shselasky} 817282513ShselaskySYSINIT(linux_timer, SI_SUB_DRIVERS, SI_ORDER_FIRST, linux_timer_init, NULL); 818282513Shselasky 819282513Shselaskyvoid 820282513Shselaskylinux_complete_common(struct completion *c, int all) 821282513Shselasky{ 822282513Shselasky int wakeup_swapper; 823282513Shselasky 824282513Shselasky sleepq_lock(c); 825282513Shselasky c->done++; 826282513Shselasky if (all) 827282513Shselasky wakeup_swapper = sleepq_broadcast(c, SLEEPQ_SLEEP, 0, 0); 828282513Shselasky else 829282513Shselasky wakeup_swapper = sleepq_signal(c, SLEEPQ_SLEEP, 0, 0); 830282513Shselasky sleepq_release(c); 831282513Shselasky if (wakeup_swapper) 832282513Shselasky kick_proc0(); 833282513Shselasky} 834282513Shselasky 835282513Shselasky/* 836282513Shselasky * Indefinite wait for done != 0 with or without signals. 837282513Shselasky */ 838282513Shselaskylong 839282513Shselaskylinux_wait_for_common(struct completion *c, int flags) 840282513Shselasky{ 841282513Shselasky 842282513Shselasky if (flags != 0) 843282513Shselasky flags = SLEEPQ_INTERRUPTIBLE | SLEEPQ_SLEEP; 844282513Shselasky else 845282513Shselasky flags = SLEEPQ_SLEEP; 846282513Shselasky for (;;) { 847282513Shselasky sleepq_lock(c); 848282513Shselasky if (c->done) 849282513Shselasky break; 850282513Shselasky sleepq_add(c, NULL, "completion", flags, 0); 851282513Shselasky if (flags & SLEEPQ_INTERRUPTIBLE) { 852282513Shselasky if (sleepq_wait_sig(c, 0) != 0) 853282513Shselasky return (-ERESTARTSYS); 854282513Shselasky } else 855282513Shselasky sleepq_wait(c, 0); 856282513Shselasky } 857282513Shselasky c->done--; 858282513Shselasky sleepq_release(c); 859282513Shselasky 860282513Shselasky return (0); 861282513Shselasky} 862282513Shselasky 863282513Shselasky/* 864282513Shselasky * Time limited wait for done != 0 with or without signals. 865282513Shselasky */ 866282513Shselaskylong 867282513Shselaskylinux_wait_for_timeout_common(struct completion *c, long timeout, int flags) 868282513Shselasky{ 869282513Shselasky long end = jiffies + timeout; 870282513Shselasky 871282513Shselasky if (flags != 0) 872282513Shselasky flags = SLEEPQ_INTERRUPTIBLE | SLEEPQ_SLEEP; 873282513Shselasky else 874282513Shselasky flags = SLEEPQ_SLEEP; 875282513Shselasky for (;;) { 876282513Shselasky int ret; 877282513Shselasky 878282513Shselasky sleepq_lock(c); 879282513Shselasky if (c->done) 880282513Shselasky break; 881282513Shselasky sleepq_add(c, NULL, "completion", flags, 0); 882282513Shselasky sleepq_set_timeout(c, linux_timer_jiffies_until(end)); 883282513Shselasky if (flags & SLEEPQ_INTERRUPTIBLE) 884282513Shselasky ret = sleepq_timedwait_sig(c, 0); 885282513Shselasky else 886282513Shselasky ret = sleepq_timedwait(c, 0); 887282513Shselasky if (ret != 0) { 888282513Shselasky /* check for timeout or signal */ 889282513Shselasky if (ret == EWOULDBLOCK) 890282513Shselasky return (0); 891282513Shselasky else 892282513Shselasky return (-ERESTARTSYS); 893282513Shselasky } 894282513Shselasky } 895282513Shselasky c->done--; 896282513Shselasky sleepq_release(c); 897282513Shselasky 898282513Shselasky /* return how many jiffies are left */ 899282513Shselasky return (linux_timer_jiffies_until(end)); 900282513Shselasky} 901282513Shselasky 902282513Shselaskyint 903282513Shselaskylinux_try_wait_for_completion(struct completion *c) 904282513Shselasky{ 905282513Shselasky int isdone; 906282513Shselasky 907282513Shselasky isdone = 1; 908282513Shselasky sleepq_lock(c); 909282513Shselasky if (c->done) 910282513Shselasky c->done--; 911282513Shselasky else 912282513Shselasky isdone = 0; 913282513Shselasky sleepq_release(c); 914282513Shselasky return (isdone); 915282513Shselasky} 916282513Shselasky 917282513Shselaskyint 918282513Shselaskylinux_completion_done(struct completion *c) 919282513Shselasky{ 920282513Shselasky int isdone; 921282513Shselasky 922282513Shselasky isdone = 1; 923282513Shselasky sleepq_lock(c); 924282513Shselasky if (c->done == 0) 925282513Shselasky isdone = 0; 926282513Shselasky sleepq_release(c); 927282513Shselasky return (isdone); 928282513Shselasky} 929282513Shselasky 930293151Shselaskyvoid 931293151Shselaskylinux_delayed_work_fn(void *arg) 932293151Shselasky{ 933293151Shselasky struct delayed_work *work; 934293151Shselasky 935293151Shselasky work = arg; 936293151Shselasky taskqueue_enqueue(work->work.taskqueue, &work->work.work_task); 937293151Shselasky} 938293151Shselasky 939293151Shselaskyvoid 940293151Shselaskylinux_work_fn(void *context, int pending) 941293151Shselasky{ 942293151Shselasky struct work_struct *work; 943293151Shselasky 944293151Shselasky work = context; 945293151Shselasky work->fn(work); 946293151Shselasky} 947293151Shselasky 948293151Shselaskyvoid 949293151Shselaskylinux_flush_fn(void *context, int pending) 950293151Shselasky{ 951293151Shselasky} 952293151Shselasky 953293151Shselaskystruct workqueue_struct * 954293151Shselaskylinux_create_workqueue_common(const char *name, int cpus) 955293151Shselasky{ 956293151Shselasky struct workqueue_struct *wq; 957293151Shselasky 958293151Shselasky wq = kmalloc(sizeof(*wq), M_WAITOK); 959293151Shselasky wq->taskqueue = taskqueue_create(name, M_WAITOK, 960293151Shselasky taskqueue_thread_enqueue, &wq->taskqueue); 961293151Shselasky atomic_set(&wq->draining, 0); 962293151Shselasky taskqueue_start_threads(&wq->taskqueue, cpus, PWAIT, "%s", name); 963293151Shselasky 964293151Shselasky return (wq); 965293151Shselasky} 966293151Shselasky 967293151Shselaskyvoid 968293151Shselaskydestroy_workqueue(struct workqueue_struct *wq) 969293151Shselasky{ 970293151Shselasky taskqueue_free(wq->taskqueue); 971293151Shselasky kfree(wq); 972293151Shselasky} 973293151Shselasky 974282513Shselaskystatic void 975280540Shselaskylinux_compat_init(void *arg) 976219820Sjeff{ 977219820Sjeff struct sysctl_oid *rootoid; 978219820Sjeff int i; 979219820Sjeff 980219820Sjeff rootoid = SYSCTL_ADD_NODE(NULL, SYSCTL_STATIC_CHILDREN(), 981219820Sjeff OID_AUTO, "sys", CTLFLAG_RD|CTLFLAG_MPSAFE, NULL, "sys"); 982219820Sjeff kobject_init(&class_root, &class_ktype); 983219820Sjeff kobject_set_name(&class_root, "class"); 984219820Sjeff class_root.oidp = SYSCTL_ADD_NODE(NULL, SYSCTL_CHILDREN(rootoid), 985219820Sjeff OID_AUTO, "class", CTLFLAG_RD|CTLFLAG_MPSAFE, NULL, "class"); 986219820Sjeff kobject_init(&linux_rootdev.kobj, &dev_ktype); 987219820Sjeff kobject_set_name(&linux_rootdev.kobj, "device"); 988219820Sjeff linux_rootdev.kobj.oidp = SYSCTL_ADD_NODE(NULL, 989219820Sjeff SYSCTL_CHILDREN(rootoid), OID_AUTO, "device", CTLFLAG_RD, NULL, 990219820Sjeff "device"); 991219820Sjeff linux_rootdev.bsddev = root_bus; 992219820Sjeff miscclass.name = "misc"; 993219820Sjeff class_register(&miscclass); 994219820Sjeff INIT_LIST_HEAD(&pci_drivers); 995219820Sjeff INIT_LIST_HEAD(&pci_devices); 996219820Sjeff spin_lock_init(&pci_lock); 997219820Sjeff mtx_init(&vmmaplock, "IO Map lock", NULL, MTX_DEF); 998219820Sjeff for (i = 0; i < VMMAP_HASH_SIZE; i++) 999219820Sjeff LIST_INIT(&vmmaphead[i]); 1000219820Sjeff} 1001219820Sjeff 1002219820SjeffSYSINIT(linux_compat, SI_SUB_DRIVERS, SI_ORDER_SECOND, linux_compat_init, NULL); 1003271127Shselasky 1004271127Shselaskystatic void 1005280540Shselaskylinux_compat_uninit(void *arg) 1006271127Shselasky{ 1007271127Shselasky kobject_kfree_name(&class_root); 1008271127Shselasky kobject_kfree_name(&linux_rootdev.kobj); 1009271127Shselasky kobject_kfree_name(&miscclass.kobj); 1010271127Shselasky} 1011271127ShselaskySYSUNINIT(linux_compat, SI_SUB_DRIVERS, SI_ORDER_SECOND, linux_compat_uninit, NULL); 1012