The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/uvm/uvm_device.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*      $OpenBSD: uvm_device.c,v 1.66 2021/12/15 12:53:53 mpi Exp $     */
    2 /*      $NetBSD: uvm_device.c,v 1.30 2000/11/25 06:27:59 chs Exp $      */
    3 
    4 /*
    5  * Copyright (c) 1997 Charles D. Cranor and Washington University.
    6  * All rights reserved.
    7  *
    8  * Redistribution and use in source and binary forms, with or without
    9  * modification, are permitted provided that the following conditions
   10  * are met:
   11  * 1. Redistributions of source code must retain the above copyright
   12  *    notice, this list of conditions and the following disclaimer.
   13  * 2. Redistributions in binary form must reproduce the above copyright
   14  *    notice, this list of conditions and the following disclaimer in the
   15  *    documentation and/or other materials provided with the distribution.
   16  *
   17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
   18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
   20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
   21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
   22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
   23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
   24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
   26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   27  *
   28  * from: Id: uvm_device.c,v 1.1.2.9 1998/02/06 05:11:47 chs Exp
   29  */
   30 
   31 /*
   32  * uvm_device.c: the device pager.
   33  */
   34 
   35 #include <sys/param.h>
   36 #include <sys/systm.h>
   37 #include <sys/conf.h>
   38 #include <sys/malloc.h>
   39 #include <sys/mutex.h>
   40 
   41 #include <uvm/uvm.h>
   42 #include <uvm/uvm_device.h>
   43 
   44 #include "drm.h"
   45 
   46 /*
   47  * private global data structure
   48  *
   49  * we keep a list of active device objects in the system.
   50  */
   51 
   52 LIST_HEAD(, uvm_device) udv_list = LIST_HEAD_INITIALIZER(udv_list);
   53 struct mutex udv_lock = MUTEX_INITIALIZER(IPL_NONE);
   54 
   55 /*
   56  * functions
   57  */
   58 static void             udv_reference(struct uvm_object *);
   59 static void             udv_detach(struct uvm_object *);
   60 static int              udv_fault(struct uvm_faultinfo *, vaddr_t,
   61                                        vm_page_t *, int, int, vm_fault_t,
   62                                        vm_prot_t, int);
   63 static boolean_t        udv_flush(struct uvm_object *, voff_t, voff_t,
   64                                        int);
   65 
   66 /*
   67  * master pager structure
   68  */
   69 const struct uvm_pagerops uvm_deviceops = {
   70         .pgo_reference = udv_reference,
   71         .pgo_detach = udv_detach,
   72         .pgo_fault = udv_fault,
   73         .pgo_flush = udv_flush,
   74 };
   75 
   76 /*
   77  * the ops!
   78  */
   79 
   80 
   81 /*
   82  * udv_attach
   83  *
   84  * get a VM object that is associated with a device.   allocate a new
   85  * one if needed.
   86  *
   87  * => nothing should be locked so that we can sleep here.
   88  *
   89  * The last two arguments (off and size) are only used for access checking.
   90  */
   91 struct uvm_object *
   92 udv_attach(dev_t device, vm_prot_t accessprot, voff_t off, vsize_t size)
   93 {
   94         struct uvm_device *udv, *lcv;
   95         paddr_t (*mapfn)(dev_t, off_t, int);
   96 #if NDRM > 0
   97         struct uvm_object *obj;
   98 #endif
   99 
  100         /*
  101          * before we do anything, ensure this device supports mmap
  102          */
  103         mapfn = cdevsw[major(device)].d_mmap;
  104         if (mapfn == NULL ||
  105             mapfn == (paddr_t (*)(dev_t, off_t, int)) enodev ||
  106             mapfn == (paddr_t (*)(dev_t, off_t, int)) nullop)
  107                 return(NULL);
  108 
  109         /*
  110          * Negative offsets on the object are not allowed.
  111          */
  112         if (off < 0)
  113                 return(NULL);
  114 
  115 #if NDRM > 0
  116         obj = udv_attach_drm(device, accessprot, off, size);
  117         if (obj)
  118                 return(obj);
  119 #endif
  120 
  121         /*
  122          * Check that the specified range of the device allows the
  123          * desired protection.
  124          * 
  125          * XXX clobbers off and size, but nothing else here needs them.
  126          */
  127         while (size != 0) {
  128                 if ((*mapfn)(device, off, accessprot) == -1)
  129                         return (NULL);
  130                 off += PAGE_SIZE; size -= PAGE_SIZE;
  131         }
  132 
  133         /*
  134          * keep looping until we get it
  135          */
  136         for (;;) {
  137                 /*
  138                  * first, attempt to find it on the main list
  139                  */
  140                 mtx_enter(&udv_lock);
  141                 LIST_FOREACH(lcv, &udv_list, u_list) {
  142                         if (device == lcv->u_device)
  143                                 break;
  144                 }
  145 
  146                 /*
  147                  * got it on main list.  put a hold on it and unlock udv_lock.
  148                  */
  149                 if (lcv) {
  150                         /*
  151                          * if someone else has a hold on it, sleep and start
  152                          * over again. Else, we need take HOLD flag so we
  153                          * don't have to re-order locking here.
  154                          */
  155                         if (lcv->u_flags & UVM_DEVICE_HOLD) {
  156                                 lcv->u_flags |= UVM_DEVICE_WANTED;
  157                                 msleep_nsec(lcv, &udv_lock, PVM | PNORELOCK,
  158                                     "udv_attach", INFSLP);
  159                                 continue;
  160                         }
  161 
  162                         /* we are now holding it */
  163                         lcv->u_flags |= UVM_DEVICE_HOLD;
  164                         mtx_leave(&udv_lock);
  165 
  166                         /*
  167                          * bump reference count, unhold, return.
  168                          */
  169                         rw_enter(lcv->u_obj.vmobjlock, RW_WRITE);
  170                         lcv->u_obj.uo_refs++;
  171                         rw_exit(lcv->u_obj.vmobjlock);
  172 
  173                         mtx_enter(&udv_lock);
  174                         if (lcv->u_flags & UVM_DEVICE_WANTED)
  175                                 wakeup(lcv);
  176                         lcv->u_flags &= ~(UVM_DEVICE_WANTED|UVM_DEVICE_HOLD);
  177                         mtx_leave(&udv_lock);
  178                         return(&lcv->u_obj);
  179                 }
  180 
  181                 /*
  182                  * Did not find it on main list.  Need to allocate a new one.
  183                  */
  184                 mtx_leave(&udv_lock);
  185                 /* NOTE: we could sleep in the following malloc() */
  186                 udv = malloc(sizeof(*udv), M_TEMP, M_WAITOK);
  187                 uvm_obj_init(&udv->u_obj, &uvm_deviceops, 1);
  188                 mtx_enter(&udv_lock);
  189 
  190                 /*
  191                  * now we have to double check to make sure no one added it
  192                  * to the list while we were sleeping...
  193                  */
  194                 LIST_FOREACH(lcv, &udv_list, u_list) {
  195                         if (device == lcv->u_device)
  196                                 break;
  197                 }
  198 
  199                 /*
  200                  * did we lose a race to someone else?
  201                  * free our memory and retry.
  202                  */
  203                 if (lcv) {
  204                         mtx_leave(&udv_lock);
  205                         uvm_obj_destroy(&udv->u_obj);
  206                         free(udv, M_TEMP, sizeof(*udv));
  207                         continue;
  208                 }
  209 
  210                 /*
  211                  * we have it!   init the data structures, add to list
  212                  * and return.
  213                  */
  214                 udv->u_flags = 0;
  215                 udv->u_device = device;
  216                 LIST_INSERT_HEAD(&udv_list, udv, u_list);
  217                 mtx_leave(&udv_lock);
  218                 return(&udv->u_obj);
  219         }
  220         /*NOTREACHED*/
  221 }
  222         
  223 /*
  224  * udv_reference
  225  *
  226  * add a reference to a VM object.   Note that the reference count must
  227  * already be one (the passed in reference) so there is no chance of the
  228  * udv being released or locked out here.
  229  */
  230 static void
  231 udv_reference(struct uvm_object *uobj)
  232 {
  233         rw_enter(uobj->vmobjlock, RW_WRITE);
  234         uobj->uo_refs++;
  235         rw_exit(uobj->vmobjlock);
  236 }
  237 
  238 /*
  239  * udv_detach
  240  *
  241  * remove a reference to a VM object.
  242  */
  243 static void
  244 udv_detach(struct uvm_object *uobj)
  245 {
  246         struct uvm_device *udv = (struct uvm_device *)uobj;
  247 
  248         KERNEL_ASSERT_LOCKED();
  249 
  250         /*
  251          * loop until done
  252          */
  253 again:
  254         rw_enter(uobj->vmobjlock, RW_WRITE);
  255         if (uobj->uo_refs > 1) {
  256                 uobj->uo_refs--;
  257                 rw_exit(uobj->vmobjlock);
  258                 return;
  259         }
  260         KASSERT(uobj->uo_npages == 0 && RBT_EMPTY(uvm_objtree, &uobj->memt));
  261 
  262         /*
  263          * is it being held?   if so, wait until others are done.
  264          */
  265         mtx_enter(&udv_lock);
  266         if (udv->u_flags & UVM_DEVICE_HOLD) {
  267                 udv->u_flags |= UVM_DEVICE_WANTED;
  268                 rw_exit(uobj->vmobjlock);
  269                 msleep_nsec(udv, &udv_lock, PVM | PNORELOCK, "udv_detach",
  270                     INFSLP);
  271                 goto again;
  272         }
  273 
  274         /*
  275          * got it!   nuke it now.
  276          */
  277         LIST_REMOVE(udv, u_list);
  278         if (udv->u_flags & UVM_DEVICE_WANTED)
  279                 wakeup(udv);
  280         mtx_leave(&udv_lock);
  281         rw_exit(uobj->vmobjlock);
  282 
  283         uvm_obj_destroy(uobj);
  284         free(udv, M_TEMP, sizeof(*udv));
  285 }
  286 
  287 
  288 /*
  289  * udv_flush
  290  *
  291  * flush pages out of a uvm object.   a no-op for devices.
  292  */
  293 static boolean_t
  294 udv_flush(struct uvm_object *uobj, voff_t start, voff_t stop, int flags)
  295 {
  296 
  297         return(TRUE);
  298 }
  299 
  300 /*
  301  * udv_fault: non-standard fault routine for device "pages"
  302  *
  303  * => rather than having a "get" function, we have a fault routine
  304  *      since we don't return vm_pages we need full control over the
  305  *      pmap_enter map in
  306  * => on return, we unlock all fault data structures
  307  * => flags: PGO_ALLPAGES: get all of the pages
  308  *           PGO_LOCKED: fault data structures are locked
  309  *    XXX: currently PGO_LOCKED is always required ... consider removing
  310  *      it as a flag
  311  * => NOTE: vaddr is the VA of pps[0] in ufi->entry, _NOT_ pps[centeridx]
  312  */
  313 static int
  314 udv_fault(struct uvm_faultinfo *ufi, vaddr_t vaddr, vm_page_t *pps, int npages,
  315     int centeridx, vm_fault_t fault_type, vm_prot_t access_type, int flags)
  316 {
  317         struct vm_map_entry *entry = ufi->entry;
  318         struct uvm_object *uobj = entry->object.uvm_obj;
  319         struct uvm_device *udv = (struct uvm_device *)uobj;
  320         vaddr_t curr_va;
  321         off_t curr_offset;
  322         paddr_t paddr;
  323         int lcv, retval;
  324         dev_t device;
  325         paddr_t (*mapfn)(dev_t, off_t, int);
  326         vm_prot_t mapprot;
  327 
  328         KERNEL_ASSERT_LOCKED();
  329 
  330         /*
  331          * we do not allow device mappings to be mapped copy-on-write
  332          * so we kill any attempt to do so here.
  333          */
  334         if (UVM_ET_ISCOPYONWRITE(entry)) {
  335                 uvmfault_unlockall(ufi, ufi->entry->aref.ar_amap, uobj);
  336                 return(VM_PAGER_ERROR);
  337         }
  338 
  339         /*
  340          * get device map function.
  341          */
  342         device = udv->u_device;
  343         mapfn = cdevsw[major(device)].d_mmap;
  344 
  345         /*
  346          * now we must determine the offset in udv to use and the VA to
  347          * use for pmap_enter.  note that we always use orig_map's pmap
  348          * for pmap_enter (even if we have a submap).   since virtual
  349          * addresses in a submap must match the main map, this is ok.
  350          */
  351         /* udv offset = (offset from start of entry) + entry's offset */
  352         curr_offset = entry->offset + (vaddr - entry->start);
  353         /* pmap va = vaddr (virtual address of pps[0]) */
  354         curr_va = vaddr;
  355 
  356         /*
  357          * loop over the page range entering in as needed
  358          */
  359         retval = VM_PAGER_OK;
  360         for (lcv = 0 ; lcv < npages ; lcv++, curr_offset += PAGE_SIZE,
  361             curr_va += PAGE_SIZE) {
  362                 if ((flags & PGO_ALLPAGES) == 0 && lcv != centeridx)
  363                         continue;
  364 
  365                 if (pps[lcv] == PGO_DONTCARE)
  366                         continue;
  367 
  368                 paddr = (*mapfn)(device, curr_offset, access_type);
  369                 if (paddr == -1) {
  370                         retval = VM_PAGER_ERROR;
  371                         break;
  372                 }
  373                 mapprot = ufi->entry->protection;
  374                 if (pmap_enter(ufi->orig_map->pmap, curr_va, paddr,
  375                     mapprot, PMAP_CANFAIL | mapprot) != 0) {
  376                         /*
  377                          * pmap_enter() didn't have the resource to
  378                          * enter this mapping.  Unlock everything,
  379                          * wait for the pagedaemon to free up some
  380                          * pages, and then tell uvm_fault() to start
  381                          * the fault again.
  382                          *
  383                          * XXX Needs some rethinking for the PGO_ALLPAGES
  384                          * XXX case.
  385                          */
  386                         uvmfault_unlockall(ufi, ufi->entry->aref.ar_amap,
  387                             uobj);
  388 
  389                         /* sync what we have so far */
  390                         pmap_update(ufi->orig_map->pmap);      
  391                         uvm_wait("udv_fault");
  392                         return (VM_PAGER_REFAULT);
  393                 }
  394         }
  395 
  396         uvmfault_unlockall(ufi, ufi->entry->aref.ar_amap, uobj);
  397         pmap_update(ufi->orig_map->pmap);
  398         return (retval);
  399 }

Cache object: cf525f95ecf4ecef911c646f767d297b


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.