Diff for /src/sys/kern/kern_lockf.c between versions 1.6 and 1.7

version 1.6, 2003/08/26 21:09:02 version 1.7, 2004/05/03 16:06:26
Line 1 Line 1
 /*  /*
    * Copyright (c) 2004 Joerg Sonnenberger <joerg@bec.de>
    *
  * Copyright (c) 1982, 1986, 1989, 1993   * Copyright (c) 1982, 1986, 1989, 1993
  *      The Regents of the University of California.  All rights reserved.   *      The Regents of the University of California.  All rights reserved.
  *   *
Line 38 Line 40
  * $DragonFly$   * $DragonFly$
  */   */
   
 #include "opt_debug_lockf.h"  
   
 #include <sys/param.h>  #include <sys/param.h>
 #include <sys/systm.h>  #include <sys/systm.h>
 #include <sys/kernel.h>  #include <sys/kernel.h>
Line 49 Line 49
 #include <sys/vnode.h>  #include <sys/vnode.h>
 #include <sys/malloc.h>  #include <sys/malloc.h>
 #include <sys/fcntl.h>  #include <sys/fcntl.h>
   #include <sys/resourcevar.h>
   
 #include <sys/lockf.h>  #include <sys/lockf.h>
   #include <machine/limits.h>     /* for LLONG_MAX */
   
   #ifdef INVARIANTS
   int lf_global_counter = 0;
   #endif
   #ifdef LOCKF_DEBUG
   int lf_print_ranges = 0;
   
   static void     lf_print_lock(const struct lockf *);
   #endif
   
   static MALLOC_DEFINE(M_LOCKF, "lockf", "Byte-range locking structures");
   
   static void     lf_wakeup(struct lockf *, off_t, off_t);
   static int      lf_overlap(const struct lockf_range *, off_t, off_t);
   static int      lf_overlap_left(const struct lockf_range *, off_t, off_t);
   static int      lf_overlap_right(const struct lockf_range *, off_t, off_t);
   static int      lf_overlap_left2(const struct lockf_range *, off_t, off_t);
   static int      lf_overlap_right2(const struct lockf_range *, off_t, off_t);
   static int      lf_overlap_embedded(const struct lockf_range *, off_t, off_t);
   static struct lockf_range *
                   lf_create_range(struct proc *, int, int, off_t, off_t, int);
   static void     lf_destroy_range(struct lockf_range *, int);
   
   static int      lf_setlock(struct lockf *, struct proc *, int, int,
                              off_t, off_t);
   static int      lf_clearlock(struct lockf *, struct proc *, int, int,
                                off_t, off_t);
   static int      lf_getlock(struct flock *, struct lockf *, struct proc *,
                              int, int, off_t, off_t);
   
   static int      lf_count_change(struct proc *, int);
   
 /*  /*
 * This variable controls the maximum number of processes that will * Change the POSIX lock accounting for the given process.
 * be checked in doing deadlock detection. 
  */   */
static int maxlockdepth = MAXDEPTH;void
 lf_count_adjust(struct proc *p, struct uidinfo *newowner)
 {
         struct uidinfo *uip;
   
#ifdef LOCKF_DEBUG        KKASSERT(p != NULL);
#include <sys/kernel.h> 
#include <sys/sysctl.h> 
   
#include <vfs/ufs/quota.h>        uip = p->p_ucred->cr_uidinfo;
#include <vfs/ufs/inode.h> 
   
           newowner->ui_posixlocks += p->p_numposixlocks;
           uip->ui_posixlocks -= p->p_numposixlocks;
   
static int        lockf_debug = 0;        KASSERT(uip->ui_posixlocks >= 0,
SYSCTL_INT(_debug, OID_AUTO, lockf_debug, CTLFLAG_RW, &lockf_debug, 0, "");                ("Negative number of POSIX locks held by old user: %d.",
#endif                 uip->ui_posixlocks));
         KASSERT(newowner->ui_posixlocks >= 0,
                 ("Negative number of POSIX locks held by new user: %d.",
                  newowner->ui_posixlocks));
 }
   
static MALLOC_DEFINE(M_LOCKF, "lockf", "Byte-range locking structures");static int
 lf_count_change(struct proc *owner, int diff)
 {
         struct uidinfo *uip;
         int max;
 
         /* we might actually not have a process context */
         if (owner == NULL)
                 return(0);
   
#define NOLOCKF (struct lockf *)0        uip = owner->p_ucred->cr_uidinfo;
#define SELF    0x1
#define OTHERS  0x2        max = MIN(owner->p_rlimit[RLIMIT_POSIXLOCKS].rlim_cur,
static int       lf_clearlock (struct lockf *);                  maxposixlocksperuid);
static int       lf_findoverlap (struct lockf *,        if (diff > 0 && owner->p_ucred->cr_uid != 0 && max != -1 &&
            struct lockf *, int, struct lockf ***, struct lockf **);            uip->ui_posixlocks >= max )
static struct lockf *                return(1);
         lf_getblock (struct lockf *);
static int       lf_getlock (struct lockf *, struct flock *);        uip->ui_posixlocks += diff;
static int       lf_setlock (struct lockf *);
static void      lf_split (struct lockf *, struct lockf *);        KASSERT(uip->ui_posixlocks >= 0,
static void      lf_wakelock (struct lockf *);                ("Negative number of POSIX locks held by user: %d.",
                  uip->ui_posixlocks));
 
         return(0);
 }
   
 /*  /*
  * Advisory record locking support   * Advisory record locking support
  */   */
 int  int
lf_advlock(ap, head, size)lf_advlock(struct vop_advlock_args *ap, struct lockf *lock, u_quad_t size)
        struct vop_advlock_args /* { 
                struct vnode *a_vp; 
                caddr_t  a_id; 
                int  a_op; 
                struct flock *a_fl; 
                int  a_flags; 
        } */ *ap; 
        struct lockf **head; 
        u_quad_t size; 
 {  {
         struct flock *fl = ap->a_fl;          struct flock *fl = ap->a_fl;
        struct lockf *lock;        struct proc *owner;
         off_t start, end;          off_t start, end;
        int error;        int type, flags;
 
         if (lock->init_done == 0) {
                 TAILQ_INIT(&lock->lf_range);
                 TAILQ_INIT(&lock->lf_blocked);
                 lock->init_done = 1;
         }
   
         /*          /*
          * Convert the flock structure into a start and end.           * Convert the flock structure into a start and end.
          */           */
         switch (fl->l_whence) {          switch (fl->l_whence) {
   
         case SEEK_SET:          case SEEK_SET:
         case SEEK_CUR:          case SEEK_CUR:
                 /*                  /*
Line 124  lf_advlock(ap, head, size) Line 170  lf_advlock(ap, head, size)
                 break;                  break;
   
         default:          default:
                return (EINVAL);                return(EINVAL);
         }          }
         if (start < 0)          if (start < 0)
                return (EINVAL);                return(EINVAL);
        if (fl->l_len == 0)        if (fl->l_len == 0) {
                end = -1;                flags |= F_NOEND;
        else {                end = LLONG_MAX;
         } else {
                 end = start + fl->l_len - 1;                  end = start + fl->l_len - 1;
                 if (end < start)                  if (end < start)
                        return (EINVAL);                        return(EINVAL);
        } 
        /* 
         * Avoid the common case of unlocking when inode has no locks. 
         */ 
        if (*head == (struct lockf *)0) { 
                if (ap->a_op != F_SETLK) { 
                        fl->l_type = F_UNLCK; 
                        return (0); 
                } 
         }          }
           
           flags = ap->a_flags;
           type = fl->l_type;
         /*          /*
         * Create the lockf structure         * This isn't really correct for flock-style locks,
          * but the current handling is somewhat broken anyway.
          */           */
        MALLOC(lock, struct lockf *, sizeof *lock, M_LOCKF, M_WAITOK);        owner = (struct proc *)ap->a_id;
        lock->lf_start = start;
        lock->lf_end = end; 
        lock->lf_id = ap->a_id; 
/*      lock->lf_inode = ip; */ /* XXX JH */ 
        lock->lf_type = fl->l_type; 
        lock->lf_head = head; 
        lock->lf_next = (struct lockf *)0; 
        TAILQ_INIT(&lock->lf_blkhd); 
        lock->lf_flags = ap->a_flags; 
         /*          /*
          * Do the requested operation.           * Do the requested operation.
          */           */
         switch(ap->a_op) {          switch(ap->a_op) {
         case F_SETLK:          case F_SETLK:
                return (lf_setlock(lock));                return(lf_setlock(lock, owner, type, flags, start, end));
   
         case F_UNLCK:          case F_UNLCK:
                error = lf_clearlock(lock);                return(lf_clearlock(lock, owner, type, flags, start, end));
                FREE(lock, M_LOCKF); 
                return (error); 
   
         case F_GETLK:          case F_GETLK:
                error = lf_getlock(lock, fl);                return(lf_getlock(fl, lock, owner, type, flags, start, end));
                FREE(lock, M_LOCKF); 
                return (error); 
   
         default:          default:
                 free(lock, M_LOCKF);  
                 return (EINVAL);                  return (EINVAL);
         }          }
         /* NOTREACHED */          /* NOTREACHED */
 }  }
   
 /*  
  * Set a byte-range lock.  
  */  
 static int  static int
lf_setlock(lock)lf_setlock(struct lockf *lock, struct proc *owner, int type, int flags,
        struct lockf *lock;           off_t start, off_t end)
 {  {
        struct lockf *block;        struct lockf_range *range, *first_match, *insert_point;
        struct lockf **head = lock->lf_head;        int wakeup_needed, lock_needed;
        struct lockf **prev, *overlap, *ltmp;        /* for restauration in case of hitting the POSIX lock limit below */
        static char lockstr[] = "lockf";        struct lockf_range *orig_first_match = NULL;
        int ovcase, needtolink, error;        off_t orig_end = -1;
         int orig_flags = 0;
 
 restart:
         first_match = NULL;
         insert_point = NULL;
         wakeup_needed = 0;
   
 #ifdef LOCKF_DEBUG  #ifdef LOCKF_DEBUG
        if (lockf_debug & 1)        if (lf_print_ranges)
                lf_print("lf_setlock", lock);                lf_print_lock(lock);
#endif /* LOCKF_DEBUG */#endif
   
        /*        TAILQ_FOREACH(range, &lock->lf_range, lf_link) {
         * Scan lock list for this file looking for locks that would block us.                if (insert_point == NULL && range->lf_start >= start)
         */                        insert_point = range;
        while ((block = lf_getblock(lock))) {                if (lf_overlap(range, start, end) == 0)
                /*                        continue;
                 * Free the structure and return if nonblocking.                if (range->lf_owner == owner) {
                 */                        if (first_match == NULL)
                if ((lock->lf_flags & F_WAIT) == 0) {                                first_match = range;
                        FREE(lock, M_LOCKF);                        continue;
                        return (EAGAIN); 
                 }                  }
                   if (type == F_WRLCK || range->lf_type == F_WRLCK)
                           break;
           }
   
           if (range != NULL) {
                   struct lockf_range *brange;
                   int error;
   
                   if ((flags & F_WAIT) == 0)
                           return(EAGAIN);
   
                 /*                  /*
                 * We are blocked. Since flock style locks cover                 * We are blocked. For POSIX locks we have to check
                 * the whole file, there is no chance for deadlock.                 * for deadlocks and return with EDEADLK. This is done
                 * For byte-range locks we must check for deadlock.                 * by checking wether range->lf_owner is already
                  * blocked.
                  *
                  * Since flock-style locks cover the whole file, a
                  * deadlock between those is nearly impossible.
                  * This can only occur if a process tries to lock the
                  * same inode exclusively while holding a shared lock
                  * with another descriptor.
                  * XXX How can we cleanly detect this?
                  * XXX The current mixing of flock & fcntl/lockf is evil.
                  *                   *
                 * Deadlock detection is done by looking through the                 * Handle existing locks of flock-style like POSIX locks.
                 * wait channels to see if there are any cycles that 
                 * involve us. MAXDEPTH is set just to make sure we 
                 * do not go off into neverland. 
                  */                   */
                if ((lock->lf_flags & F_POSIX) &&                if (flags & F_POSIX) {
                    (block->lf_flags & F_POSIX)) {                        TAILQ_FOREACH(brange, &lock->lf_blocked, lf_link)
                        struct proc *wproc;                                if (brange->lf_owner == range->lf_owner)
                        struct lockf *waitblock;                                        return(EDEADLK);
                        int i = 0; 
 
                        /* The block is waiting on something */ 
                        wproc = (struct proc *)block->lf_id; 
                        while (wproc->p_wchan && 
                               (wproc->p_wmesg == lockstr) && 
                               (i++ < maxlockdepth)) { 
                                waitblock = (struct lockf *)wproc->p_wchan; 
                                /* Get the owner of the blocking lock */ 
                                waitblock = waitblock->lf_next; 
                                if ((waitblock->lf_flags & F_POSIX) == 0) 
                                        break; 
                                wproc = (struct proc *)waitblock->lf_id; 
                                if (wproc == (struct proc *)lock->lf_id) { 
                                        free(lock, M_LOCKF); 
                                        return (EDEADLK); 
                                } 
                        } 
                 }                  }
                   
                 /*                  /*
                 * For flock type locks, we must first remove                 * For flock-style locks, we must first remove
                  * any shared locks that we hold before we sleep                   * any shared locks that we hold before we sleep
                  * waiting for an exclusive lock.                   * waiting for an exclusive lock.
                  */                   */
                if ((lock->lf_flags & F_FLOCK) &&                if ((flags & F_FLOCK) && type == F_WRLCK)
                    lock->lf_type == F_WRLCK) {                        lf_clearlock(lock, owner, type, flags, start, end);
                        lock->lf_type = F_UNLCK;
                        (void) lf_clearlock(lock);                brange = lf_create_range(owner, type, 0, start, end, 0);
                        lock->lf_type = F_WRLCK;                TAILQ_INSERT_TAIL(&lock->lf_blocked, brange, lf_link);
                }                error = tsleep(brange, PCATCH, "lockf", 0);
                /*
                 * Add our lock to the blocked list and sleep until we're free. 
                 * Remember who blocked us (for deadlock detection). 
                 */ 
                lock->lf_next = block; 
                TAILQ_INSERT_TAIL(&block->lf_blkhd, lock, lf_block); 
#ifdef LOCKF_DEBUG 
                if (lockf_debug & 1) { 
                        lf_print("lf_setlock: blocking on", block); 
                        lf_printlist("lf_setlock", block); 
                } 
#endif /* LOCKF_DEBUG */ 
                error = tsleep((caddr_t)lock, PCATCH, lockstr, 0); 
                 /*                  /*
                 * We may have been awakened by a signal and/or by a                 * We may have been awaked by a signal and/or by a
                 * debugger continuing us (in which cases we must remove                 * debugger continuing us (in which case we must remove
                  * ourselves from the blocked list) and/or by another                   * ourselves from the blocked list) and/or by another
                 * process releasing a lock (in which case we have                 * process releasing/downgrading a lock (in which case
                 * already been removed from the blocked list and our                 * we have already been removed from the blocked list
                 * lf_next field set to NOLOCKF).                 * and our lf_flags field is 1).
                 */                 */
                if (lock->lf_next) {                if (brange->lf_flags == 0)
                        TAILQ_REMOVE(&lock->lf_next->lf_blkhd, lock, lf_block);                        TAILQ_REMOVE(&lock->lf_blocked, brange, lf_link);
                        lock->lf_next = NOLOCKF;                lf_destroy_range(brange, 0);
                }
                if (error) {                if (error)
                        free(lock, M_LOCKF);                        return(error);
                        return (error);                goto restart;
                }        }
 
         if (first_match == NULL) {
                 if (flags & F_POSIX) {
                         if (lf_count_change(owner, 1))
                                 return(ENOLCK);
                 }
                 range = lf_create_range(owner, type, flags, start, end, 1);
                 if (insert_point != NULL)
                         TAILQ_INSERT_BEFORE(insert_point, range, lf_link);
                 else
                         TAILQ_INSERT_TAIL(&lock->lf_range, range, lf_link);
                 goto do_wakeup;
         }          }
        /*
         * No blocks!!  Add the lock.  Note that we will        lock_needed = 1;
         * downgrade or upgrade any overlapping locks this
         * process already owns.        if (lf_overlap_left(first_match, start, end)) {
         *                KKASSERT((flags & F_POSIX) != 0);
         * Skip over locks owned by other processes.                if (first_match->lf_end > end) {
         * Handle any locks that overlap and are owned by ourselves.                        if (first_match->lf_type == type)
         */                                goto do_wakeup;
        prev = head;                        if (lf_count_change(owner, 2))
        block = *head;                                return(ENOLCK);
        needtolink = 1;                        range = lf_create_range(owner, type, flags,
        for (;;) {                                                start, end, 1);
                ovcase = lf_findoverlap(block, lock, SELF, &prev, &overlap);                        if (insert_point != NULL)
                if (ovcase)                                TAILQ_INSERT_BEFORE(insert_point, range,
                        block = overlap->lf_next;                                                    lf_link);
                         else
                                 TAILQ_INSERT_TAIL(&lock->lf_range, range,
                                                   lf_link);
                         insert_point = range;
                         range = lf_create_range(owner, first_match->lf_type,
                                                 first_match->lf_flags, end + 1,
                                                 first_match->lf_end, 1);
                         TAILQ_INSERT_AFTER(&lock->lf_range, insert_point,
                                            range, lf_link);
                         first_match->lf_flags &= ~F_NOEND;
                         first_match->lf_end = start - 1;
                         if (type == F_RDLCK)
                                 wakeup_needed = 1;
                         goto do_wakeup;
                 }
                 /*                  /*
                 * Six cases:                 * left match, but not right match
                 *      0) no overlap                 *
                 *      1) overlap == lock                 * handle the lf_type != type case directly,
                 *       2) overlap contains lock                 * merge the other case with the !lock_needed path.
                 *      3) lock contains overlap 
                 *      4) overlap starts before lock 
                 *      5) overlap ends after lock 
                  */                   */
                switch (ovcase) {                if (first_match->lf_type != type) {
                case 0: /* no overlap */ 
                        if (needtolink) { 
                                *prev = lock; 
                                lock->lf_next = overlap; 
                        } 
                        break; 
 
                case 1: /* overlap == lock */ 
                         /*                          /*
                         * If downgrading lock, others may be                         * This is needed if the lockf acquisition below fails.
                         * able to acquire it. 
                          */                           */
                        if (lock->lf_type == F_RDLCK &&                        orig_first_match = first_match;
                            overlap->lf_type == F_WRLCK)                        orig_end = first_match->lf_end;
                                lf_wakelock(overlap);                        orig_flags = first_match->lf_flags;
                        overlap->lf_type = lock->lf_type;                        first_match->lf_end = start - 1;
                        FREE(lock, M_LOCKF);                        first_match->lf_flags &= ~F_NOEND;
                        lock = overlap; /* for debug output below */                        if (type == F_RDLCK)
                        break;                                wakeup_needed = 1;
                         /* Try to find the next matching range */
                         range = TAILQ_NEXT(first_match, lf_link);
                         while (range != NULL) {
                                 if (range->lf_owner == owner &&
                                     lf_overlap(range, start, end))
                                         break;
                                 range = TAILQ_NEXT(range, lf_link);
                         }
                         if (range == NULL)
                                 goto do_wakeup;
                         first_match = range;
                         /* fall through to !left_match behaviour */
                 } else {
                         first_match->lf_end = end;
                         first_match->lf_flags |= flags & F_NOEND;
                         lock_needed = 0;
                 }
         }
   
                case 2: /* overlap contains lock */        if (lf_overlap_embedded(first_match, start, end)) {
                        /*                if (first_match != insert_point) {
                         * Check for common starting point and different types.                        TAILQ_REMOVE(&lock->lf_range, first_match, lf_link);
                         */                        TAILQ_INSERT_BEFORE(insert_point, first_match, lf_link);
                        if (overlap->lf_type == lock->lf_type) {                }
                                free(lock, M_LOCKF);                first_match->lf_start = start;
                                lock = overlap; /* for debug output below */                first_match->lf_end = end;
                 first_match->lf_flags |= flags & F_NOEND;
                 lock_needed = 0;                
         }
 
         if (lock_needed == 0) {
                 struct lockf_range *nrange;
 
                 range = TAILQ_NEXT(first_match, lf_link);
                 while (range != NULL) {
                         if (range->lf_owner != owner) {
                                 range = TAILQ_NEXT(range, lf_link);
                                 continue;
                         }
                         if (lf_overlap_embedded(range, start, end)) {
                                 nrange = TAILQ_NEXT(range, lf_link);
                                 TAILQ_REMOVE(&lock->lf_range, range,
                                              lf_link);
                                 lf_count_change(owner, -1);
                                 lf_destroy_range(range, 1);
                                 range = nrange;
                                 continue;
                         }
                         if (lf_overlap_right(range, start, end) == 0) {
                                 range = TAILQ_NEXT(range, lf_link);
                                 continue;
                         }
                         if (range->lf_type != type) {
                                 range->lf_start = end + 1;
                                 nrange = TAILQ_NEXT(range, lf_link);
                                 TAILQ_REMOVE(&lock->lf_range, range, lf_link);
                                 while (nrange != NULL) {
                                         if (nrange->lf_start >= end + 1)
                                                 break;
                                         nrange = TAILQ_NEXT(nrange, lf_link);
                                 }
                                 if (nrange != NULL)
                                         TAILQ_INSERT_BEFORE(nrange, range,
                                                             lf_link);
                                 else
                                         TAILQ_INSERT_TAIL(&lock->lf_range,
                                                           range, lf_link);
                                 break;                                  break;
                         }                          }
                        if (overlap->lf_start == lock->lf_start) {                        first_match->lf_end = range->lf_end;
                                *prev = lock;                        first_match->lf_flags |=
                                lock->lf_next = overlap;                            range->lf_flags & F_NOEND;
                                overlap->lf_start = lock->lf_end + 1;                        TAILQ_REMOVE(&lock->lf_range, range, lf_link);
                        } else                        lf_count_change(owner, -1);
                                lf_split(overlap, lock);                        lf_destroy_range(range, 1);
                        lf_wakelock(overlap); 
                         break;                          break;
                   }
                   goto do_wakeup;
           }
   
                case 3: /* lock contains overlap */        if (lf_overlap_right(first_match, start, end)) {
                        /*                KKASSERT((flags & F_POSIX) != 0);
                         * If downgrading lock, others may be able to                if (first_match->lf_type == type) {
                         * acquire it, otherwise take the list.                        first_match->lf_start = start;
                         */                        if (first_match != insert_point) {
                        if (lock->lf_type == F_RDLCK &&                                TAILQ_REMOVE(&lock->lf_range, first_match,
                            overlap->lf_type == F_WRLCK) {                                             lf_link);
                                lf_wakelock(overlap);                                TAILQ_INSERT_BEFORE(insert_point, first_match,
                        } else {                                                    lf_link);
                                while (!TAILQ_EMPTY(&overlap->lf_blkhd)) { 
                                        ltmp = TAILQ_FIRST(&overlap->lf_blkhd); 
                                        TAILQ_REMOVE(&overlap->lf_blkhd, ltmp, 
                                            lf_block); 
                                        TAILQ_INSERT_TAIL(&lock->lf_blkhd, 
                                            ltmp, lf_block); 
                                        ltmp->lf_next = lock; 
                                } 
                         }                          }
                        /*                        goto do_wakeup;
                         * Add the new lock if necessary and delete the overlap.                }
                         */                /* XXX restore partial left match */
                        if (needtolink) {                if (lf_count_change(owner, 1)) {
                                *prev = lock;                        if (orig_first_match != NULL) {
                                lock->lf_next = overlap->lf_next;                                orig_first_match->lf_end = orig_end;
                                prev = &lock->lf_next;                                orig_first_match->lf_flags = orig_end;
                                needtolink = 0; 
                        } else 
                                *prev = overlap->lf_next; 
                        free(overlap, M_LOCKF); 
                        continue; 
 
                case 4: /* overlap starts before lock */ 
                        /* 
                         * Add lock after overlap on the list. 
                         */ 
                        lock->lf_next = overlap->lf_next; 
                        overlap->lf_next = lock; 
                        overlap->lf_end = lock->lf_start - 1; 
                        prev = &lock->lf_next; 
                        lf_wakelock(overlap); 
                        needtolink = 0; 
                        continue; 
 
                case 5: /* overlap ends after lock */ 
                        /* 
                         * Add the new lock before overlap. 
                         */ 
                        if (needtolink) { 
                                *prev = lock; 
                                lock->lf_next = overlap; 
                         }                          }
                        overlap->lf_start = lock->lf_end + 1;                        return(ENOLCK);
                        lf_wakelock(overlap); 
                        break; 
                 }                  }
                break;                first_match->lf_start = end + 1;
                 range = lf_create_range(owner, type, flags, start, end, 1);
                 TAILQ_INSERT_BEFORE(insert_point, range, lf_link);
                 range = TAILQ_NEXT(first_match, lf_link);
                 TAILQ_REMOVE(&lock->lf_range, first_match, lf_link);
                 while (range != NULL) {
                         if (range->lf_start >= first_match->lf_start)
                                 break;
                         range = TAILQ_NEXT(range, lf_link);
                 }
                 if (range != NULL)
                         TAILQ_INSERT_BEFORE(range, first_match, lf_link);
                 else
                         TAILQ_INSERT_TAIL(&lock->lf_range, first_match, lf_link);
                 goto do_wakeup;
         }          }
   
   do_wakeup:
 #ifdef LOCKF_DEBUG  #ifdef LOCKF_DEBUG
        if (lockf_debug & 1) {        if (lf_print_ranges)
                lf_print("lf_setlock: got the lock", lock);                lf_print_lock(lock);
                lf_printlist("lf_setlock", lock);#endif
        }        if (wakeup_needed)
#endif /* LOCKF_DEBUG */                lf_wakeup(lock, start, end);
        return (0);        return(0);
 }  }
   
 /*  
  * Remove a byte-range lock on an inode.  
  *  
  * Generally, find the lock (or an overlap to that lock)  
  * and remove it (or shrink it), then wakeup anyone we can.  
  */  
 static int  static int
lf_clearlock(unlock)lf_clearlock(struct lockf *lock, struct proc *owner, int type, int flags,
        struct lockf *unlock;             off_t start, off_t end)
 {  {
        struct lockf **head = unlock->lf_head;        struct lockf_range *range, *trange;
        struct lockf *lf = *head; 
        struct lockf *overlap, **prev; 
        int ovcase; 
   
        if (lf == NOLOCKF)        TAILQ_FOREACH_MUTABLE(range, &lock->lf_range, lf_link, trange) {
                return (0);                if (range->lf_end < start)
#ifdef LOCKF_DEBUG                        continue;
        if (unlock->lf_type != F_UNLCK)                if (range->lf_start > end)
                panic("lf_clearlock: bad type"); 
        if (lockf_debug & 1) 
                lf_print("lf_clearlock", unlock); 
#endif /* LOCKF_DEBUG */ 
        prev = head; 
        while ((ovcase = lf_findoverlap(lf, unlock, SELF, &prev, &overlap))) { 
                /* 
                 * Wakeup the list of locks to be retried. 
                 */ 
                lf_wakelock(overlap); 
 
                switch (ovcase) { 
 
                case 1: /* overlap == lock */ 
                        *prev = overlap->lf_next; 
                        FREE(overlap, M_LOCKF); 
                         break;                          break;
                if (range->lf_owner != owner)
                case 2: /* overlap contains lock: split it */                        continue;
                        if (overlap->lf_start == unlock->lf_start) {                if (lf_overlap_embedded(range, start, end)) {
                                overlap->lf_start = unlock->lf_end + 1;                        TAILQ_REMOVE(&lock->lf_range, range, lf_link);
                         /* flock-locks are equal */
                         if (range->lf_flags & F_POSIX)
                                 lf_count_change(owner, -1);
                         lf_destroy_range(range, 1);
                         continue;
                 }
                 if (lf_overlap_left2(range, start, end)) {
                         KKASSERT(range->lf_flags & F_POSIX);
                         if (lf_overlap_right2(range, start, end)) {
                                 struct lockf_range *nrange;
 
                                 if (lf_count_change(owner, 1))
                                         return(ENOLCK);
                                 nrange = lf_create_range(range->lf_owner,
                                     range->lf_type, range->lf_flags,
                                     end + 1, range->lf_end, 1);
                                 range->lf_end = start;
                                 range->lf_flags &= ~F_NOEND;
                                 for (; range != NULL;
                                      range = TAILQ_NEXT(range, lf_link))
                                         if (range->lf_start >= nrange->lf_start)
                                                 break;
                                 if (range != NULL)
                                         TAILQ_INSERT_BEFORE(range, nrange,
                                                             lf_link);
                                 else
                                         TAILQ_INSERT_TAIL(&lock->lf_range,
                                                           nrange, lf_link);
                                 break;                                  break;
                         }                          }
                        lf_split(overlap, unlock);                        range->lf_end = start - 1;
                        overlap->lf_next = unlock->lf_next;                        range->lf_flags &= ~F_NOEND;
                        break; 
 
                case 3: /* lock contains overlap */ 
                        *prev = overlap->lf_next; 
                        lf = overlap->lf_next; 
                        free(overlap, M_LOCKF); 
                         continue;                          continue;
                   }
                   if (lf_overlap_right2(range, start, end)) {
                           struct lockf_range *nrange = range;
   
                case 4: /* overlap starts before lock */                        KKASSERT(range->lf_flags & F_POSIX);
                        overlap->lf_end = unlock->lf_start - 1; 
                        prev = &overlap->lf_next; 
                        lf = overlap->lf_next; 
                        continue; 
   
                case 5: /* overlap ends after lock */                        range  = TAILQ_NEXT(range, lf_link);
                        overlap->lf_start = unlock->lf_end + 1;                        TAILQ_REMOVE(&lock->lf_range, nrange, lf_link);
                         for (; range != NULL;
                              range = TAILQ_NEXT(range, lf_link))
                                 if (range->lf_start >= nrange->lf_start)
                                         break;
                         if (range != NULL)
                                 TAILQ_INSERT_BEFORE(range, nrange, lf_link);
                         else
                                 TAILQ_INSERT_TAIL(&lock->lf_range, nrange,
                                                   lf_link);
                         break;                          break;
                 }                  }
                 break;  
         }          }
#ifdef LOCKF_DEBUG
        if (lockf_debug & 1)        lf_wakeup(lock, start, end);
                lf_printlist("lf_clearlock", unlock);        return(0);
#endif /* LOCKF_DEBUG */ 
        return (0); 
 }  }
   
 /*  /*
Line 489  lf_clearlock(unlock) Line 567  lf_clearlock(unlock)
  * and if so return its process identifier.   * and if so return its process identifier.
  */   */
 static int  static int
lf_getlock(lock, fl)lf_getlock(struct flock *fl, struct lockf *lock, struct proc *owner,
        struct lockf *lock;           int type, int flags, off_t start, off_t end)
        struct flock *fl; 
 {  {
        struct lockf *block;        struct lockf_range *range;
   
#ifdef LOCKF_DEBUG        TAILQ_FOREACH(range, &lock->lf_range, lf_link)
        if (lockf_debug & 1)                if (range->lf_owner != owner &&
                lf_print("lf_getlock", lock);                    lf_overlap(range, start, end) &&
#endif /* LOCKF_DEBUG */                    (type == F_WRLCK || range->lf_type == F_WRLCK))
                        break;
        if ((block = lf_getblock(lock))) {        if (range == NULL) {
                fl->l_type = block->lf_type; 
                fl->l_whence = SEEK_SET; 
                fl->l_start = block->lf_start; 
                if (block->lf_end == -1) 
                        fl->l_len = 0; 
                else 
                        fl->l_len = block->lf_end - block->lf_start + 1; 
                if (block->lf_flags & F_POSIX) 
                        fl->l_pid = ((struct proc *)(block->lf_id))->p_pid; 
                else 
                        fl->l_pid = -1; 
        } else { 
                 fl->l_type = F_UNLCK;                  fl->l_type = F_UNLCK;
                   return(0);
         }          }
        return (0);        fl->l_type = range->lf_type;
         fl->l_whence = SEEK_SET;
         fl->l_start = range->lf_start;
         if (range->lf_flags & F_NOEND)
                 fl->l_len = 0;
         else
                 fl->l_len = range->lf_end - range->lf_start + 1;
         if (range->lf_owner != NULL && (range->lf_flags & F_POSIX))
                 fl->l_pid = range->lf_owner->p_pid;
         else
                 fl->l_pid = -1;
         return(0);
 }  }
   
 /*  /*
 * Walk the list of locks for an inode and * Check wether range and [start, end] overlap.
 * return the first blocking lock. 
  */   */
static struct lockf *static int
lf_getblock(lock)lf_overlap(const struct lockf_range *range, off_t start, off_t end)
        struct lockf *lock; 
 {  {
        struct lockf **prev, *overlap, *lf = *(lock->lf_head);        if (range->lf_start >= start && range->lf_start <= end)
        int ovcase;                return(1);
        else if (start >= range->lf_start && start <= range->lf_end)
        prev = lock->lf_head;                return(1);
        while ((ovcase = lf_findoverlap(lf, lock, OTHERS, &prev, &overlap))) {        else
                /*                return(0);
                 * We've found an overlap, see if it blocks us 
                 */ 
                if ((lock->lf_type == F_WRLCK || overlap->lf_type == F_WRLCK)) 
                        return (overlap); 
                /* 
                 * Nope, point to the next one on the list and 
                 * see if it blocks us 
                 */ 
                lf = overlap->lf_next; 
        } 
        return (NOLOCKF); 
 }  }
   
 /*  /*
 * Walk the list of locks for an inode to * Wakeup pending lock attempts.
 * find an overlapping lock (if any). 
 * 
 * NOTE: this returns only the FIRST overlapping lock.  There 
 *       may be more than one. 
  */   */
static intstatic void
lf_findoverlap(lf, lock, type, prev, overlap)lf_wakeup(struct lockf *lock, off_t start, off_t end)
        struct lockf *lf; 
        struct lockf *lock; 
        int type; 
        struct lockf ***prev; 
        struct lockf **overlap; 
 {  {
        off_t start, end;        struct lockf_range *range, *nrange;
        TAILQ_FOREACH_MUTABLE(range, &lock->lf_blocked, lf_link, nrange) {
        *overlap = lf;                if (lf_overlap(range, start, end) == 0)
        if (lf == NOLOCKF) 
                return (0); 
#ifdef LOCKF_DEBUG 
        if (lockf_debug & 2) 
                lf_print("lf_findoverlap: looking for overlap in", lock); 
#endif /* LOCKF_DEBUG */ 
        start = lock->lf_start; 
        end = lock->lf_end; 
        while (lf != NOLOCKF) { 
                if (((type & SELF) && lf->lf_id != lock->lf_id) || 
                    ((type & OTHERS) && lf->lf_id == lock->lf_id)) { 
                        *prev = &lf->lf_next; 
                        *overlap = lf = lf->lf_next; 
                        continue; 
                } 
#ifdef LOCKF_DEBUG 
                if (lockf_debug & 2) 
                        lf_print("\tchecking", lf); 
#endif /* LOCKF_DEBUG */ 
                /* 
                 * OK, check for overlap 
                 * 
                 * Six cases: 
                 *      0) no overlap 
                 *      1) overlap == lock 
                 *      2) overlap contains lock 
                 *      3) lock contains overlap 
                 *      4) overlap starts before lock 
                 *      5) overlap ends after lock 
                 */ 
                if ((lf->lf_end != -1 && start > lf->lf_end) || 
                    (end != -1 && lf->lf_start > end)) { 
                        /* Case 0 */ 
#ifdef LOCKF_DEBUG 
                        if (lockf_debug & 2) 
                                printf("no overlap\n"); 
#endif /* LOCKF_DEBUG */ 
                        if ((type & SELF) && end != -1 && lf->lf_start > end) 
                                return (0); 
                        *prev = &lf->lf_next; 
                        *overlap = lf = lf->lf_next; 
                         continue;                          continue;
                }                TAILQ_REMOVE(&lock->lf_blocked, range, lf_link);
                if ((lf->lf_start == start) && (lf->lf_end == end)) {                range->lf_flags = 1;
                        /* Case 1 */                wakeup(range);
#ifdef LOCKF_DEBUG 
                        if (lockf_debug & 2) 
                                printf("overlap == lock\n"); 
#endif /* LOCKF_DEBUG */ 
                        return (1); 
                } 
                if ((lf->lf_start <= start) && 
                    (end != -1) && 
                    ((lf->lf_end >= end) || (lf->lf_end == -1))) { 
                        /* Case 2 */ 
#ifdef LOCKF_DEBUG 
                        if (lockf_debug & 2) 
                                printf("overlap contains lock\n"); 
#endif /* LOCKF_DEBUG */ 
                        return (2); 
                } 
                if (start <= lf->lf_start && 
                           (end == -1 || 
                           (lf->lf_end != -1 && end >= lf->lf_end))) { 
                        /* Case 3 */ 
#ifdef LOCKF_DEBUG 
                        if (lockf_debug & 2) 
                                printf("lock contains overlap\n"); 
#endif /* LOCKF_DEBUG */ 
                        return (3); 
                } 
                if ((lf->lf_start < start) && 
                        ((lf->lf_end >= start) || (lf->lf_end == -1))) { 
                        /* Case 4 */ 
#ifdef LOCKF_DEBUG 
                        if (lockf_debug & 2) 
                                printf("overlap starts before lock\n"); 
#endif /* LOCKF_DEBUG */ 
                        return (4); 
                } 
                if ((lf->lf_start > start) && 
                        (end != -1) && 
                        ((lf->lf_end > end) || (lf->lf_end == -1))) { 
                        /* Case 5 */ 
#ifdef LOCKF_DEBUG 
                        if (lockf_debug & 2) 
                                printf("overlap ends after lock\n"); 
#endif /* LOCKF_DEBUG */ 
                        return (5); 
                } 
                panic("lf_findoverlap: default"); 
         }          }
         return (0);  
 }  }
   
/*static int
 * Split a lock and a contained region intolf_overlap_left(const struct lockf_range *range, off_t start, off_t end)
 * two or three locks as necessary. 
 */ 
static void 
lf_split(lock1, lock2) 
        struct lockf *lock1; 
        struct lockf *lock2; 
 {  {
        struct lockf *splitlock;        if (range->lf_start < start && range->lf_end >= start - 1 &&
             range->lf_end <= end)
                 return(1);
         else
                 return(0);
                 
 }
   
#ifdef LOCKF_DEBUGstatic int
        if (lockf_debug & 2) {lf_overlap_right(const struct lockf_range *range, off_t start, off_t end)
                lf_print("lf_split", lock1);{
                lf_print("splitting from", lock2);        if (range->lf_end > end && range->lf_start >= start &&
        }            range->lf_start - 1 <= end)
#endif /* LOCKF_DEBUG */                return(1);
        /*        else
         * Check to see if spliting into only two pieces.                return(0);
         */}
        if (lock1->lf_start == lock2->lf_start) {
                lock1->lf_start = lock2->lf_end + 1;static int
                lock2->lf_next = lock1;lf_overlap_left2(const struct lockf_range *range, off_t start, off_t end)
                return;{
        }        if (range->lf_start < start && range->lf_end >= start &&
        if (lock1->lf_end == lock2->lf_end) {            range->lf_end <= end)
                lock1->lf_end = lock2->lf_start - 1;                return(1);
                lock2->lf_next = lock1->lf_next;        else
                lock1->lf_next = lock2;                return(0);
                return;                
        }}
        /*
         * Make a new lock consisting of the last part ofstatic int
         * the encompassing locklf_overlap_right2(const struct lockf_range *range, off_t start, off_t end)
         */{
        MALLOC(splitlock, struct lockf *, sizeof *splitlock, M_LOCKF, M_WAITOK);        if (range->lf_end > end && range->lf_start >= start &&
        bcopy((caddr_t)lock1, (caddr_t)splitlock, sizeof *splitlock);            range->lf_start <= end)
        splitlock->lf_start = lock2->lf_end + 1;                return(1);
        TAILQ_INIT(&splitlock->lf_blkhd);        else
        lock1->lf_end = lock2->lf_start - 1;                return(0);
        /*}
         * OK, now link it in
         */static int
        splitlock->lf_next = lock1->lf_next;lf_overlap_embedded(const struct lockf_range *range, off_t start, off_t end)
        lock2->lf_next = splitlock;{
        lock1->lf_next = lock2;        if (range->lf_start >= start && range->lf_end <= end)
                 return(1);
         else
                 return(0);
 }
 
 static struct lockf_range *
 lf_create_range(struct proc *owner, int type, int flags,
                 off_t start, off_t end, int accounting)
 {
         struct lockf_range *range;
 
         KKASSERT(start <= end);
         if (owner != NULL && (flags & F_POSIX) && accounting)
                 ++owner->p_numposixlocks;
         range = malloc(sizeof(struct lockf_range), M_LOCKF, M_WAITOK);
         range->lf_type = type;
         range->lf_flags = flags;
         range->lf_start = start;
         range->lf_end = end;
         range->lf_owner = owner;
 
 #ifdef LOCKF_DEBUG
         if (lf_print_ranges)
                 printf("lf_create_range: %lld..%lld\n", range->lf_start,
                        range->lf_end);
 #endif
 #ifdef INVARIANTS
         lf_global_counter++;
 #endif
 
         return(range);
 }  }
   
 /*  
  * Wakeup a blocklist  
  */  
 static void  static void
lf_wakelock(listhead)lf_destroy_range(struct lockf_range *range, int accounting)
        struct lockf *listhead; 
 {  {
        struct lockf *wakelock;        struct proc *owner = range->lf_owner;
         int flags = range->lf_flags;
   
         while (!TAILQ_EMPTY(&listhead->lf_blkhd)) {  
                 wakelock = TAILQ_FIRST(&listhead->lf_blkhd);  
                 TAILQ_REMOVE(&listhead->lf_blkhd, wakelock, lf_block);  
                 wakelock->lf_next = NOLOCKF;  
 #ifdef LOCKF_DEBUG  #ifdef LOCKF_DEBUG
                if (lockf_debug & 2)        if (lf_print_ranges)
                        lf_print("lf_wakelock: awakening", wakelock);                printf("lf_destroy_range: %lld..%lld\n", range->lf_start,
#endif /* LOCKF_DEBUG */                       range->lf_end);
                wakeup((caddr_t)wakelock);#endif
 
         free(range, M_LOCKF);
         if (owner != NULL && (flags & F_POSIX) && accounting) {
                 --owner->p_numposixlocks;
                 KASSERT(owner->p_numposixlocks >= 0,
                         ("Negative number of POSIX locks held by process: %d",
                          owner->p_numposixlocks));
         }          }
   
   #ifdef INVARIANTS
           lf_global_counter--;
           KKASSERT(lf_global_counter>=0);
   #endif
 }  }
   
 #ifdef LOCKF_DEBUG  #ifdef LOCKF_DEBUG
/*static void
 * Print out a lock.lf_print_lock(const struct lockf *lock)
 */ 
void 
lf_print(tag, lock) 
        char *tag; 
        struct lockf *lock; 
 {  {
           struct lockf_range *range;
   
        printf("%s: lock %p for ", tag, (void *)lock);        if (TAILQ_EMPTY(&lock->lf_range))
        if (lock->lf_flags & F_POSIX)                printf("lockf %p: no ranges locked\n", lock);
                printf("proc %ld", (long)((struct proc *)lock->lf_id)->p_pid); 
         else          else
                printf("id %p", (void *)lock->lf_id);                printf("lockf %p:\n", lock);
        /* XXX no %qd in kernel.  Truncate. */        TAILQ_FOREACH(range, &lock->lf_range, lf_link)
        printf(" in ino %lu on dev <%d, %d>, %s, start %ld, end %ld",                printf("\t%lld..%lld type %s owned by %d\n",
            (u_long)lock->lf_inode->i_number,                       range->lf_start, range->lf_end,
            major(lock->lf_inode->i_dev),                       range->lf_type == F_RDLCK ? "shared" : "exclusive",
            minor(lock->lf_inode->i_dev),                       range->lf_flags & F_POSIX ? range->lf_owner->p_pid : -1);
            lock->lf_type == F_RDLCK ? "shared" :        if (TAILQ_EMPTY(&lock->lf_blocked))
            lock->lf_type == F_WRLCK ? "exclusive" :                printf("no process waiting for range\n");
            lock->lf_type == F_UNLCK ? "unlock" : 
            "unknown", (long)lock->lf_start, (long)lock->lf_end); 
        if (!TAILQ_EMPTY(&lock->lf_blkhd)) 
                printf(" block %p\n", (void *)TAILQ_FIRST(&lock->lf_blkhd)); 
         else          else
                printf("\n");                printf("blocked locks:");
}        TAILQ_FOREACH(range, &lock->lf_range, lf_link)
                printf("\t%lld..%lld type %s waiting on %p\n",
void                       range->lf_start, range->lf_end,
lf_printlist(tag, lock)                       range->lf_type == F_RDLCK ? "shared" : "exclusive",
        char *tag;                       range);
        struct lockf *lock; 
{ 
        struct lockf *lf, *blk; 
 
        printf("%s: Lock list for ino %lu on dev <%d, %d>:\n", 
            tag, (u_long)lock->lf_inode->i_number, 
            major(lock->lf_inode->i_dev), 
            minor(lock->lf_inode->i_dev)); 
        for (lf = lock->lf_inode->i_lockf; lf; lf = lf->lf_next) { 
                printf("\tlock %p for ",(void *)lf); 
                if (lf->lf_flags & F_POSIX) 
                        printf("proc %ld", 
                            (long)((struct proc *)lf->lf_id)->p_pid); 
                else 
                        printf("id %p", (void *)lf->lf_id); 
                /* XXX no %qd in kernel.  Truncate. */ 
                printf(", %s, start %ld, end %ld", 
                    lf->lf_type == F_RDLCK ? "shared" : 
                    lf->lf_type == F_WRLCK ? "exclusive" : 
                    lf->lf_type == F_UNLCK ? "unlock" : 
                    "unknown", (long)lf->lf_start, (long)lf->lf_end); 
                TAILQ_FOREACH(blk, &lf->lf_blkhd, lf_block) { 
                        printf("\n\t\tlock request %p for ", (void *)blk); 
                        if (blk->lf_flags & F_POSIX) 
                                printf("proc %ld", 
                                    (long)((struct proc *)blk->lf_id)->p_pid); 
                        else 
                                printf("id %p", (void *)blk->lf_id); 
                        /* XXX no %qd in kernel.  Truncate. */ 
                        printf(", %s, start %ld, end %ld", 
                            blk->lf_type == F_RDLCK ? "shared" : 
                            blk->lf_type == F_WRLCK ? "exclusive" : 
                            blk->lf_type == F_UNLCK ? "unlock" : 
                            "unknown", (long)blk->lf_start, 
                            (long)blk->lf_end); 
                        if (!TAILQ_EMPTY(&blk->lf_blkhd)) 
                                panic("lf_printlist: bad list"); 
                } 
                printf("\n"); 
        } 
 }  }
#endif /* LOCKF_DEBUG */#endif /* LOCKF_DEBUG */
   

Removed from v.1.6  
changed lines
  Added in v.1.7