]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/miscfs/specfs/spec_vnops.c
xnu-517.9.4.tar.gz
[apple/xnu.git] / bsd / miscfs / specfs / spec_vnops.c
index 86dba15885b5c776c0fb31aeb8bd5e12260d6c37..314464b1967e22fcf2f69267c80070f17ee19b15 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2000-2002 Apple Computer, Inc. All rights reserved.
  *
  * @APPLE_LICENSE_HEADER_START@
  * 
 #include <sys/ioctl.h>
 #include <sys/file.h>
 #include <sys/malloc.h>
-#include <dev/disk.h>
+#include <sys/disk.h>
 #include <miscfs/specfs/specdev.h>
+#include <miscfs/specfs/lockf.h>
 #include <vfs/vfs_support.h>
 
+#include <sys/kdebug.h>
 
 struct vnode *speclisth[SPECHSZ];
 
@@ -125,7 +127,7 @@ struct vnodeopv_entry_desc spec_vnodeop_entries[] = {
        { &vop_print_desc, (VOPFUNC)spec_print },               /* print */
        { &vop_islocked_desc, (VOPFUNC)nop_islocked },          /* islocked */
        { &vop_pathconf_desc, (VOPFUNC)spec_pathconf },         /* pathconf */
-       { &vop_advlock_desc, (VOPFUNC)err_advlock },            /* advlock */
+       { &vop_advlock_desc, (VOPFUNC)spec_advlock },           /* advlock */
        { &vop_blkatoff_desc, (VOPFUNC)err_blkatoff },          /* blkatoff */
        { &vop_valloc_desc, (VOPFUNC)err_valloc },              /* valloc */
        { &vop_vfree_desc, (VOPFUNC)err_vfree },                /* vfree */
@@ -271,7 +273,30 @@ spec_open(ap)
                        return (error);
                error = (*bdevsw[maj].d_open)(dev, ap->a_mode, S_IFBLK, p);
                if (!error) {
+                   u_int64_t blkcnt;
+                   u_int32_t blksize;
+
                    set_blocksize(vp, dev);
+
+                   /*
+                    * Cache the size in bytes of the block device for later
+                    * use by spec_write().
+                    */
+                   vp->v_specdevsize = (u_int64_t)0;   /* Default: Can't get */
+                   if (!VOP_IOCTL(vp, DKIOCGETBLOCKSIZE, (caddr_t)&blksize, 0, NOCRED, p)) {
+                       /* Switch to 512 byte sectors (temporarily) */
+                       u_int32_t size512 = 512;
+
+                       if (!VOP_IOCTL(vp, DKIOCSETBLOCKSIZE, (caddr_t)&size512, FWRITE, NOCRED, p)) {
+                           /* Get the number of 512 byte physical blocks. */
+                           if (!VOP_IOCTL(vp, DKIOCGETBLOCKCOUNT, (caddr_t)&blkcnt, 0, NOCRED, p)) {
+                               vp->v_specdevsize = blkcnt * (u_int64_t)size512;
+                           }
+                       }
+                       /* If it doesn't set back, we can't recover */
+                       if (VOP_IOCTL(vp, DKIOCSETBLOCKSIZE, (caddr_t)&blksize, FWRITE, NOCRED, p))
+                           error = ENXIO;
+                   }
                }
                return(error);
        }
@@ -435,11 +460,35 @@ spec_write(ap)
 
                        n = min((unsigned)(bsize - on), uio->uio_resid);
 
+                       /*
+                        * Use getblk() as an optimization IFF:
+                        *
+                        * 1)   We are reading exactly a block on a block
+                        *      aligned boundary
+                        * 2)   We know the size of the device from spec_open
+                        * 3)   The read doesn't span the end of the device
+                        *
+                        * Otherwise, we fall back on bread().
+                        */
+                       if (n == bsize &&
+                           vp->v_specdevsize != (u_int64_t)0 &&
+                           (uio->uio_offset + (u_int64_t)n) > vp->v_specdevsize) {
+                           /* reduce the size of the read to what is there */
+                           n = (uio->uio_offset + (u_int64_t)n) - vp->v_specdevsize;
+                       }
+
                        if (n == bsize)
                                bp = getblk(vp, bn, bsize, 0, 0, BLK_WRITE);
                        else
                                error = bread(vp, bn, bsize, NOCRED, &bp);
 
+                       /* Translate downstream error for upstream, if needed */
+                       if (!error) {
+                               error = bp->b_error;
+                               if (!error && (bp->b_flags & B_ERROR) != 0) {
+                                       error = EIO;
+                               }
+                       }
                        if (error) {
                                brelse(bp);
                                return (error);
@@ -554,7 +603,8 @@ loop:
        s = splbio();
        for (bp = vp->v_dirtyblkhd.lh_first; bp; bp = nbp) {
                nbp = bp->b_vnbufs.le_next;
-               if ((bp->b_flags & B_BUSY))
+               // XXXdbg - don't flush locked blocks.  they may be journaled.
+               if ((bp->b_flags & B_BUSY) || (bp->b_flags & B_LOCKED))
                        continue;
                if ((bp->b_flags & B_DELWRI) == 0)
                        panic("spec_fsync: not dirty");
@@ -589,8 +639,126 @@ spec_strategy(ap)
                struct buf *a_bp;
        } */ *ap;
 {
-       (*bdevsw[major(ap->a_bp->b_dev)].d_strategy)(ap->a_bp);
-       return (0);
+        struct buf *bp;
+       extern int hard_throttle_on_root;
+
+        bp = ap->a_bp;
+
+        if (kdebug_enable) {
+            int    code = 0;
+
+            if (bp->b_flags & B_READ)
+                code |= DKIO_READ;
+            if (bp->b_flags & B_ASYNC)
+                code |= DKIO_ASYNC;
+
+            if (bp->b_flags & B_META)
+                code |= DKIO_META;
+            else if (bp->b_flags & (B_PGIN | B_PAGEOUT))
+                code |= DKIO_PAGING;
+
+            KERNEL_DEBUG_CONSTANT(FSDBG_CODE(DBG_DKRW, code) | DBG_FUNC_NONE,
+                               (unsigned int)bp, bp->b_dev, bp->b_blkno, bp->b_bcount, 0);
+        }
+       if ((bp->b_flags & B_PGIN) && (bp->b_vp->v_mount->mnt_kern_flag & MNTK_ROOTDEV))
+              hard_throttle_on_root = 1;
+
+        (*bdevsw[major(bp->b_dev)].d_strategy)(bp);
+        return (0);
+}
+
+/*
+ * Advisory record locking support
+ */
+int
+spec_advlock(ap)
+       struct vop_advlock_args /* {
+               struct vnode *a_vp;
+               caddr_t  a_id;
+               int  a_op;
+               struct flock *a_fl;
+               int  a_flags;
+       } */ *ap;
+{
+       register struct flock *fl = ap->a_fl;
+       register struct lockf *lock;
+       off_t start, end;
+       int error;
+
+       /*
+        * Avoid the common case of unlocking when inode has no locks.
+        */
+       if (ap->a_vp->v_specinfo->si_lockf == (struct lockf *)0) {
+               if (ap->a_op != F_SETLK) {
+                       fl->l_type = F_UNLCK;
+                       return (0);
+               }
+       }
+       /*
+        * Convert the flock structure into a start and end.
+        */
+       switch (fl->l_whence) {
+
+       case SEEK_SET:
+       case SEEK_CUR:
+               /*
+                * Caller is responsible for adding any necessary offset
+                * when SEEK_CUR is used.
+                */
+               start = fl->l_start;
+               break;
+
+       case SEEK_END:
+               start = ap->a_vp->v_specinfo->si_devsize + fl->l_start;
+               break;
+
+       default:
+               return (EINVAL);
+       }
+       if (fl->l_len == 0)
+               end = -1;
+       else if (fl->l_len > 0)
+               end = start + fl->l_len - 1;
+       else { /* l_len is negative */
+               end = start - 1;
+               start += fl->l_len;
+       }
+       if (start < 0)
+               return (EINVAL);
+       /*
+        * Create the lockf structure
+        */
+       MALLOC(lock, struct lockf *, sizeof *lock, M_LOCKF, M_WAITOK);
+       lock->lf_start = start;
+       lock->lf_end = end;
+       lock->lf_id = ap->a_id;
+       lock->lf_specinfo = ap->a_vp->v_specinfo;
+       lock->lf_type = fl->l_type;
+       lock->lf_next = (struct lockf *)0;
+       TAILQ_INIT(&lock->lf_blkhd);
+       lock->lf_flags = ap->a_flags;
+       /*
+        * Do the requested operation.
+        */
+       switch(ap->a_op) {
+       case F_SETLK:
+               return (spec_lf_setlock(lock));
+
+       case F_UNLCK:
+               error = spec_lf_clearlock(lock);
+               FREE(lock, M_LOCKF);
+               return (error);
+
+       case F_GETLK:
+               error = spec_lf_getlock(lock, fl);
+               FREE(lock, M_LOCKF);
+               return (error);
+       
+       default:
+               _FREE(lock, M_LOCKF);
+               return (EINVAL);
+       }
+       /* NOTREACHED */
 }
 
 /*
@@ -663,8 +831,8 @@ spec_close(ap)
                 */
                if (vcount(vp) == 2 && ap->a_p &&
                    vp == ap->a_p->p_session->s_ttyvp) {
-                       vrele(vp);
                        ap->a_p->p_session->s_ttyvp = NULL;
+                       vrele(vp);
                }
                /*
                 * If the vnode is locked, then we are in the midst