#include "sys/param.h" #include "sys/types.h" #include "sys/sysmacros.h" #include "sys/systm.h" #include "sys/sysinfo.h" #include "sys/mount.h" #include "sys/dir.h" #include "sys/signal.h" #include "sys/user.h" #include "sys/errno.h" #include "sys/inode.h" #include "sys/file.h" #include "sys/ino.h" #include "sys/filsys.h" #include "sys/buf.h" #include "sys/var.h" #include "sys/proc.h" #include "sys/locking.h" /* * file lock routines * John Bass, PO Box 1223, San Luis Obispo, CA 93401 * Original design spring 1976, CalPoly, San Luis Obispo * Deadlock idea from Ed Grudzien at Basys April 1980 * Extensions Fall 1980, Onyx Systems Inc., San Jose * Linted and ported to System V by UniSoft Systems, Berkeley, CA. */ #define MAXSIZE (long)(1L<<30) /* number larger than any request */ struct locklist *deadlock(); /* * locking -- handles syscall requests */ locking() { struct file *fp; struct inode *ip; /* * define order and type of syscall args */ register struct a { int fdes; int flag; off_t size; } *uap = (struct a *)u.u_ap; register struct locklist *cl, *nl; off_t LB, UB; /* * check for valid open file */ fp = getf(uap->fdes); if(fp == NULL) return; ip = fp->f_inode; if ((ip->i_mode&IFMT) == IFDIR) { u.u_error = EACCES; return; } /* * validate ranges * kludge for zero length */ LB = fp->f_offset; if( uap->size ) { UB = LB + uap->size; if(UB <= 0) UB = MAXSIZE; } else UB = MAXSIZE; /* * test for unlock request */ if(uap->flag == 0) { /* * starting at list head scan * for locks in the range by * this process */ cl = (struct locklist *)&ip->i_locklist;/* addr is pointer */ while(nl = cl->ll_link) { /* * if not by this process skip to next lock */ if(nl->ll_proc != u.u_procp) { cl = nl; continue; } /* * check for locks in proper range */ if( UB <= nl->ll_start ) break; if( nl->ll_end <= LB ) { cl = nl; continue; } /* * for locks fully contained within * requested range, just delete the item */ if( LB <= nl->ll_start && nl->ll_end <= UB) { cl->ll_link = nl->ll_link; lockfree(nl); continue; } /* * if some one is sleeping on this lock * do a wakeup, we may free the region * being slept on */ if(nl->ll_flags & IWANT) { nl->ll_flags &= ~IWANT; wakeup((caddr_t)nl); } /* * middle section is being removed * add new lock for last section * modify existing lock for first section. * if no locks, return in error */ if( nl->ll_start < LB && UB < nl->ll_end) { if( lockadd(nl,UB,nl->ll_end) ) return; nl->ll_end = LB; break; } /* * first section is being deleted * just move starting point up */ if( LB <= nl->ll_start && UB < nl->ll_end) { nl->ll_start = UB; break; } /* * must be deleting last part of this section * move ending point down * continue looking for locks covered by upper * limit of unlock range */ nl->ll_end = LB; cl = nl; } /* * end of scan for unlock request */ return; } /* * request must be a lock of some kind * check to see if the region is lockable by this * process */ if(locked(uap->flag, ip, LB, UB))return; cl = (struct locklist *)&ip->i_locklist;/* note addr is pointer */ /* * simple case, no existing locks, simply add new lock */ if( (nl=cl->ll_link) == NULL ) { (void) lockadd(cl, LB, UB); return; } /* * simple case, lock is before existing locks, * simply insert at head of list */ if( UB < nl->ll_start ) { (void) lockadd(cl,LB,UB); return; } /* * ending range of lock is same as start of lock by * another process, simply insert at head of list */ if( UB <= nl->ll_start && u.u_procp != nl->ll_proc ) { (void) lockadd(cl, LB, UB); return; } /* * request range overlaps with begining of first request * modify starting point in lock to include requested region */ if( UB >= nl->ll_start && LB < nl->ll_start ) { nl->ll_start = LB; } /* * scan thru remaining locklist */ cl = nl; for (;;) { /* * actions for requests at end of list */ if( ( nl = cl->ll_link ) == NULL ) { /* * request overlaps tail of last entry * extend end point */ if( LB <= cl->ll_end && u.u_procp == cl->ll_proc ) { if( UB > cl->ll_end ) cl->ll_end = UB; return; } /* * otherwise add new entry */ (void) lockadd(cl, LB, UB); return; } /* * if more locks in range skip to next * otherwise stop scan */ if( nl->ll_start < LB ) { cl = nl; } else { break; } } /* * if upper bound is fully resolved were done * otherwise fix end of last entry or add new entry */ if(UB <= cl->ll_end) return; if(LB <= cl->ll_end && u.u_procp == cl->ll_proc) cl->ll_end = UB; else { if( lockadd(cl, LB, UB) ) return; cl = cl->ll_link; } /* * end point set above may overlap later entries * if so delete or modify them to perform the compaction */ while( (nl=cl->ll_link) != NULL) { /* * if we found lock by another process we must * be done since we validated the range above */ if(u.u_procp != nl->ll_proc) return; /* * if the new endpoint no longer overlaps were done */ if(cl->ll_end < nl->ll_start) return; /* * if the new range overlaps the first part of the * next lock, take its end point * and delete the next lock * we should be done */ if(cl->ll_end <= nl->ll_end) { cl->ll_end = nl->ll_end; cl->ll_link = nl->ll_link; lockfree(nl); return; } /* * the next lock is fully included in the new range * so it may be deleted */ cl->ll_link = nl->ll_link; lockfree(nl); } } /* * locked -- routine to scan locks and check for a locked condition */ locked(flag, ip, LB, UB) register struct inode *ip; off_t LB, UB; { register struct locklist *nl = ip->i_locklist; /* * scan list while locks are in requested range */ while(nl != NULL && nl->ll_start < UB) { /* * skip locks for this process * and those out of range */ if( nl->ll_proc == u.u_procp || nl->ll_end <= LB) { nl = nl->ll_link; if(nl == NULL) return(NULL); continue; } /* * must have found lock by another process * if request is to test only, then exit with * error code */ if(flag>1) { u.u_error = EACCES; return(1); } /* * will need to sleep on lock, check for deadlock first * abort on error */ if(deadlock(nl) != NULL) return(1); /* * post want flag to get awoken * then sleep till lock is released */ nl->ll_flags |= IWANT; (void) sleep( (caddr_t)nl, PSLEP); /* * set scan back to begining to catch * any new areas locked * or a partial delete */ nl = ip->i_locklist; /* * abort if any errors */ if(u.u_error) return(1); } return(NULL); } /* * deadlock -- routine to follow chain of locks and proc table entries * to find deadlocks on file locks. */ struct locklist * deadlock(lp) register struct locklist *lp; { register struct locklist *nl; /* * scan while the process owning the lock is sleeping */ while(lp->ll_proc->p_stat == SSLEEP) { /* * if the object the process is sleeping on is * NOT in the locktable every thing is ok * fall out of loop and return NULL */ nl = lp->ll_proc->p_wlock; if( nl < &locklist[0] || nl >= &locklist[(short)v.v_flock] ) break; /* * the object was a locklist entry * if the owner of that entry is this * process then a deadlock would occur * set error exit and return */ if(nl->ll_proc == u.u_procp) { u.u_error = EDEADLOCK; return(nl); } /* * the object was a locklist entry * owned by some other process * continue the scan with that process */ lp = nl; } return(NULL); } /* * unlock -- called by close to release all locks for this process */ unlock(ip) struct inode *ip; { register struct locklist *nl; register struct locklist *cl; cl = (struct locklist *)&ip->i_locklist; while( (nl = cl->ll_link) != NULL) { if(nl->ll_proc == u.u_procp) { cl->ll_link = nl->ll_link; lockfree(nl); } else cl = nl; } } /* * lockalloc -- allocates free list, returns free lock items */ struct locklist * lockalloc(){ register struct locklist *fl = &locklist[0]; register struct locklist *nl; /* * if first entry has never been used * link the locklist table into the freelist */ if(fl->ll_proc == NULL) { fl->ll_proc = &proc[0]; for(nl= &locklist[1]; nl < &locklist[(short)v.v_flock]; nl++) { lockfree(nl); } } /* * if all the locks are used error exit */ if( (nl=fl->ll_link) == NULL) { u.u_error = EDEADLOCK; return(NULL); } /* * return the next lock on the list */ fl->ll_link = nl->ll_link; nl->ll_link = NULL; return(nl); } /* * lockfree -- returns a lock item to the free list */ lockfree(lp) register struct locklist *lp; { register struct locklist *fl = &locklist[0]; /* * if some process is sleeping on this lock * wake them up */ if(lp->ll_flags & IWANT) { lp->ll_flags &= ~IWANT; wakeup((caddr_t)lp); } /* * add the lock into the free list */ lp->ll_link = fl->ll_link; fl->ll_link = lp; } /* * lockadd -- routine to add item to list */ lockadd(cl,LB,UB) register struct locklist *cl; off_t LB,UB; { register struct locklist *nl; /* * get a lock, return if none available */ nl = lockalloc(); if(nl == NULL) { return(1); } /* * link the new entry into list at current spot * fill in the data from the args */ nl->ll_link = cl->ll_link; cl->ll_link = nl; nl->ll_proc = u.u_procp; nl->ll_start = LB; nl->ll_end = UB; return(0); }