Files
Arquivotheca.AIX-4.1.3/bos/kernel/lfs/lockctl.c
seta75D d6fe8fe829 Init
2021-10-11 22:19:34 -03:00

897 lines
22 KiB
C

static char sccsid[] = "@(#)34 1.8 src/bos/kernel/lfs/lockctl.c, syslfs, bos41J, 9507A 2/2/95 13:24:17";
/*
* COMPONENT_NAME: (SYSLFS) Logical File System
*
* FUNCTIONS: flckinit(), insflck(), delflck(), regflck(),
* flckadj(), blocked(),
* common_reclock(), convoff(), deadflck(),
* lock_wakeup (), flock_grow()
*
* ORIGINS: 3, 27
*
* This module contains IBM CONFIDENTIAL code. -- (IBM
* Confidential Restricted when combined with the aggregated
* modules for this product)
* SOURCE MATERIALS
* (C) COPYRIGHT International Business Machines Corp. 1985, 1994
* All Rights Reserved
*
* US Government Users Restricted Rights - Use, duplication or
* disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
*
*/
/* Copyright (c) 1984 AT&T */
/* All Rights Reserved */
/* THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T */
/* The copyright notice above does not evidence any */
/* actual or intended publication of such source code. */
/*#ident "@(#)kern-3b2:os/flock.c 1.6"*/
/* (IH)flock.c 1.1.1.4 */
/*static char sccsid[] = "@(#)aix_lckctl.c 1.4 88/09/27 09:26:10";*/
/*
* Notes :
*
* This file contains all of the file/record locking
* specific routines.
*
* All record lock lists (referenced by a pointer in
* the gnode) are ordered by starting position relative
* to the beginning of the file.
*
* In this file the name "l_end" is a macro and is used
* in place of "l_len" because the end, not length, of
* the record lock is stored internally.
*
*/
#include "sys/types.h"
#include "sys/param.h"
#include "sys/sleep.h"
#include "sys/errno.h"
#include "sys/file.h"
#include "sys/vnode.h"
#include "sys/fs_locks.h"
#include "sys/lockname.h"
#include "sys/lock_alloc.h"
#include "sys/dir.h"
#include "sys/signal.h"
#include "sys/proc.h"
#include "sys/user.h"
#include "sys/flock.h"
#include "sys/var.h"
#include "sys/utsname.h"
#include "sys/syspest.h"
#include "sys/vmount.h"
/* region types */
#define S_BEFORE 010
#define S_START 020
#define S_MIDDLE 030
#define S_END 040
#define S_AFTER 050
#define E_BEFORE 001
#define E_START 002
#define E_MIDDLE 003
#define E_END 004
#define E_AFTER 005
struct flckinfo flckinfo; /* configuration and acct info */
struct filock *frlock; /* pointer to record lock free list */
struct filock *sleeplcks; /* head of chain of sleeping locks */
Simple_lock filock_lock; /* file lock table lock */
#define LOCK_WAKEUP(ptr, retry_func) lock_wakeup(ptr, retry_func)
extern int nflox_min, nflox_max;
BUGVDEF(lckbug, 0x0)
/* build file lock free list
*/
flckinit()
{
register i;
v.v_lock = nflox_min;
v.ve_lock = (caddr_t) &flox[nflox_min];
/* Initialize the file lock table global lock */
lock_alloc(&filock_lock,LOCK_ALLOC_PAGED,FILOCK_LOCK_CLASS,-1);
simple_lock_init(&filock_lock);
flckinfo.recs = v.v_lock;
for (i=0; i<flckinfo.recs; i++) {
if (frlock == NULL) {
flox[i].next = flox[i].prev = NULL;
frlock = &flox[i];
} else {
flox[i].next = frlock;
flox[i].prev = NULL;
frlock = (frlock->prev = &flox[i]);
}
}
}
/* Insert lock (lckdat) after given lock (fl); If fl is NULL place the
* new lock at the beginning of the list and update the head ptr to
* list which is stored at the address given by lck_list.
*/
struct filock *
insflck(lck_list, lckdat, fl)
struct filock **lck_list;
struct filock *fl;
struct eflock *lckdat;
{
register struct filock *new;
struct filock *sf;
BUGLPR( lckbug, BUGACT,
("insflck called:lck_list=%x, lckdat=%x, fl=%x\n",
lck_list, lckdat, fl));
/*
* The NFS lock deamon may repeat a request for a lock already
* on the sleep locks queue. In this case, return the current
* entry.
*/
if (lckdat->l_vfs == MNT_NFS && lck_list == &sleeplcks) {
for(sf = sleeplcks; sf; sf = sf->next) {
if ( sf->set.l_start == lckdat->l_start
&& sf->set.l_len == lckdat->l_len
&& sf->set.l_pid == lckdat->l_pid
&& sf->set.l_sysid == lckdat->l_sysid
&& sf->set.l_vfs == lckdat->l_vfs) {
return(sf);
}
}
}
if( frlock == NULL ) /* any file locks available */
flock_grow(); /* try to grow more */
if ((new = frlock) != NULL) {
++flckinfo.reccnt;
++flckinfo.rectot;
frlock = new->next;
if (frlock != NULL)
frlock->prev = NULL;
new->set = *lckdat;
new->state = LCK_UNBLOCK;
new->event = EVENT_NULL;
if (fl == NULL) {
new->next = *lck_list;
if (new->next != NULL)
new->next->prev = new;
*lck_list = new;
} else {
new->next = fl->next;
if (fl->next != NULL)
fl->next->prev = new;
fl->next = new;
}
new->prev = fl;
} else {
++flckinfo.recovf;
}
BUGLPR(lckbug, BUGACT, ("exiting insflock new=%x\n", new));
return (new);
}
/* Delete lock (fl) from the record lock list. If fl is the first lock
* in the list, remove it and update the head ptr to the list which is
* stored at the address given by lck_list.
*/
delflck(lck_list, fl)
struct filock **lck_list;
struct filock *fl;
{
BUGLPR(lckbug, BUGACT,
("enter delflck: lck_list=%x, fl=%x\n", lck_list, fl));
if (fl < &flox[0] || fl >= &flox[v.v_lock])
panic("delflck:fl out of range");
if (fl->prev != NULL)
fl->prev->next = fl->next;
else
*lck_list = fl->next;
if (fl->next != NULL)
fl->next->prev = fl->prev;
--flckinfo.reccnt;
if (frlock == NULL) {
fl->next = fl->prev = NULL;
frlock = fl;
} else {
fl->next = frlock;
fl->prev = NULL;
frlock = (frlock->prev = fl);
}
}
/* regflck sets the type of span of this (un)lock relative to the specified
* already existing locked section.
* There are five regions:
*
* S_BEFORE S_START S_MIDDLE S_END S_AFTER
* 010 020 030 040 050
* E_BEFORE E_START E_MIDDLE E_END E_AFTER
* 01 02 03 04 05
* |-------------------------------|
*
* relative to the already locked section. The type is two octal digits,
* the 8's digit is the start type and the 1's digit is the end type.
*/
int
regflck(ld, flp)
struct eflock *ld;
struct filock *flp;
{
register int regntype;
if (ld->l_start > flp->set.l_start) {
if ((ld->l_start-1) == flp->set.l_end)
return(S_END|E_AFTER);
if (ld->l_start > flp->set.l_end)
return(S_AFTER|E_AFTER);
regntype = S_MIDDLE;
} else if (ld->l_start == flp->set.l_start)
regntype = S_START;
else
regntype = S_BEFORE;
if (ld->l_end < flp->set.l_end) {
if (ld->l_end == (flp->set.l_start-1))
regntype |= E_START;
else if (ld->l_end < flp->set.l_start)
regntype |= E_BEFORE;
else
regntype |= E_MIDDLE;
} else if (ld->l_end == flp->set.l_end)
regntype |= E_END;
else
regntype |= E_AFTER;
return (regntype);
}
/* Adjust file lock from region specified by 'ld', in the record
* lock list indicated by the head ptr stored at the address given
* by lck_list. Start updates at the lock given by 'insrtp'. It is
* assumed the list is ordered on starting position, relative to
* the beginning of the file, and no updating is required on any
* locks in the list previous to the one pointed to by insrtp.
* Insrtp is a result from the routine blocked(). Flckadj() scans
* the list looking for locks owned by the process requesting the
* new (un)lock :
*
* - If the new record (un)lock overlays an existing lock of
* a different type, the region overlaid is released.
*
* - If the new record (un)lock overlays or adjoins an exist-
* ing lock of the same type, the existing lock is deleted
* and its region is coalesced into the new (un)lock.
*
* When the list is sufficiently scanned and the new lock is not
* an unlock, the new lock is inserted into the appropriate
* position in the list.
*/
flckadj(lck_list, insrtp, ld, retry_id)
struct filock **lck_list;
register struct filock *insrtp;
struct eflock *ld;
ulong *retry_id;
{
register struct filock *flp, *nflp;
int regtyp;
int ret_val = 0;
int (* retry_func)() = NULL;
nflp = (insrtp == NULL) ? *lck_list : insrtp;
BUGLPR(lckbug, BUGACT,
("called flckadj: lck_list=%x, insrtp=%x, ld=%x\n",
lck_list, insrtp, ld));
while (flp = nflp) {
nflp = flp->next;
if ( flp->set.l_pid == ld->l_pid
&& flp->set.l_sysid == ld->l_sysid
&& flp->set.l_vfs == ld->l_vfs)
{
/* release already locked region if necessary */
switch (regtyp = regflck(ld, flp)) {
case S_BEFORE|E_BEFORE:
nflp = NULL;
break;
case S_BEFORE|E_START:
if (ld->l_type == flp->set.l_type) {
ld->l_end = flp->set.l_end;
LOCK_WAKEUP(flp, &retry_func);
delflck(lck_list, flp);
}
nflp = NULL;
break;
case S_START|E_END:
/* don't bother if this is in the middle of
* an already similarly set section.
*/
if (ld->l_type == flp->set.l_type) {
return(0);
}
case S_START|E_AFTER:
insrtp = flp->prev;
LOCK_WAKEUP(flp, &retry_func);
delflck(lck_list, flp);
break;
case S_BEFORE|E_END:
if (ld->l_type == flp->set.l_type)
nflp = NULL;
case S_BEFORE|E_AFTER:
LOCK_WAKEUP(flp, &retry_func);
delflck(lck_list, flp);
break;
case S_START|E_MIDDLE:
insrtp = flp->prev;
case S_MIDDLE|E_MIDDLE:
/* don't bother if this is in the middle of
* an already similarly set section.
*/
if (ld->l_type == flp->set.l_type) {
return(0);
}
case S_BEFORE|E_MIDDLE:
if (ld->l_type == flp->set.l_type)
ld->l_end = flp->set.l_end;
else {
/* setup piece after end of (un)lock */
register struct filock *tdi, *tdp;
struct eflock td;
td = flp->set;
td.l_start = ld->l_end + 1;
tdp = tdi = flp;
do {
if (tdp->set.l_start<td.l_start)
tdi = tdp;
else
break;
} while (tdp = tdp->next);
if (insflck(lck_list,&td,tdi) == NULL)
return(ENOLCK);
}
LOCK_WAKEUP(flp, &retry_func);
if (regtyp == (S_MIDDLE|E_MIDDLE)) {
/* setup piece before (un)lock */
flp->set.l_end = ld->l_start - 1;
insrtp = flp;
} else
delflck(lck_list, flp);
nflp = NULL;
break;
case S_MIDDLE|E_END:
/* don't bother if this is in the middle of
* an already similarly set section.
*/
if (ld->l_type == flp->set.l_type) {
return(0);
}
flp->set.l_end = ld->l_start - 1;
LOCK_WAKEUP(flp, &retry_func);
insrtp = flp;
break;
case S_MIDDLE|E_AFTER:
LOCK_WAKEUP(flp, &retry_func);
if (ld->l_type == flp->set.l_type) {
ld->l_start = flp->set.l_start;
insrtp = flp->prev;
delflck(lck_list, flp);
} else {
flp->set.l_end = ld->l_start - 1;
insrtp = flp;
}
break;
case S_END|E_AFTER:
if (ld->l_type == flp->set.l_type) {
ld->l_start = flp->set.l_start;
insrtp = flp->prev;
LOCK_WAKEUP(flp, &retry_func);
delflck(lck_list, flp);
}
break;
case S_AFTER|E_AFTER:
insrtp = flp;
break;
}
}
}
if (ld->l_type != F_UNLCK) {
if (flp = insrtp) {
do {
if (flp->set.l_start < ld->l_start)
insrtp = flp;
else
break;
} while (flp = flp->next);
}
if (insflck(lck_list, ld, insrtp) == NULL) {
ret_val = ENOLCK;
}
}
/*
* if a retry funcion was returned from LOCK_WAKEUP, then
* an external locking agent exists (ie NFS) and its retry
* function should be called to iform it to do the equivelent
* of a wakeup for the remote waiting locks. The retry_id is
* returned back to the caller. It is used to signal the
* caller of the value that the retry function returned.
* It is used to rpc.lockd currently, and is ignored for
* local locking.
*/
if (retry_func) {
ulong rc;
rc = (* retry_func)(NULL, ld);
if (retry_id && rc)
*retry_id = rc;
}
BUGLPR(lckbug, BUGACT, ("flckadj returning %d\n", ret_val));
return (ret_val);
}
/* blocked checks whether a new lock (lckdat) would be
* blocked by a previously set lock owned by another process.
* Insrt is set to point to the lock where lock list updating
* should begin to place the new lock.
*/
struct filock *
blocked(flp, lckdat, insrt)
struct filock *flp;
struct eflock *lckdat;
struct filock **insrt;
{
register struct filock *f;
*insrt = NULL;
for (f = flp; f != NULL; f = f->next) {
if (f->set.l_start < lckdat->l_start)
*insrt = f;
else
break;
if ( (f->set.l_pid == lckdat->l_pid)
&& (f->set.l_sysid == lckdat->l_sysid)
&& (f->set.l_vfs == lckdat->l_vfs))
{
if ((lckdat->l_start-1) <= f->set.l_end)
break;
}
else if ((lckdat->l_start <= f->set.l_end) &&
(f->set.l_type == F_WRLCK ||
(f->set.l_type == F_RDLCK &&
lckdat->l_type == F_WRLCK)))
return(f);
}
for (; f != NULL; f = f->next) {
if (lckdat->l_end < f->set.l_start)
break;
if (lckdat->l_start <= f->set.l_end
&& ( f->set.l_pid != lckdat->l_pid
|| f->set.l_sysid != lckdat->l_sysid
|| f->set.l_vfs != lckdat->l_vfs)
&& (f->set.l_type == F_WRLCK
|| (f->set.l_type == F_RDLCK
&& lckdat->l_type == F_WRLCK)))
return(f);
}
return(NULL);
}
/*
* NAME: common_reclock(gp,
* size,
* offset,
* lckdat,
* cmd,
* retry_fcn,
* retry_id,
* lock_fcn,
* rele_fcn)
*
* FUNCTION: The common_reclock routine gets and checks locks.
*
* PARMETERS: gp is a struct gnode pointer for the object to be locked,
* size is the offset of the end of the object in bytes,
* offset is a ulong indicating where the lock should start,
* lckdat is a pointer to a flock structure with the locking
* details, cmd is an integer with the command for locking,
* retry_fcn, if non-NULL, is a pointer to a function that gets
* called for a sleep locks list entry when the corresponding
* blocking lock is released (for NFS and DS server use),
* retry_id is a pointer to a "ulong" used to return
* the identifier that will be passed as an arguement
* to the retry_fcn. This return value can be used by the
* caller to correlate this VNOP_LOCKCTL() call with
* a later (* retry_fcn)() call. lock_fcn and rele_fcn are
* functions to lock and release the structure containing the
* gnode
*
*
* RETURN VALUE: Zero is returned if fsync completes sucessfully.
* An error (errno) is returned if the routine failed.
*/
common_reclock(
struct gnode * gp,
offset_t size,
offset_t offset,
struct eflock * lckdat,
int cmd,
int (* retry_fcn)(),
ulong * retry_id,
int (* lock_fcn)(),
int (* rele_fcn)())
{
register struct filock **lock_list, *sf;
struct filock *found, *insrt = NULL;
int retval = 0;
int contflg = 0;
short whence;
int klock; /* save kernel_lock state */
BUGLPR(lckbug, BUGACT,
("comm_reclock called: gp=0x%x, offset=%d, lckdat=0x%x, cmd=0x%x, retry_fcn=0x%x",
gp, offset, lckdat, cmd, retry_fcn));
/* Convert start to be relative to beginning of file */
whence = lckdat->l_whence;
if (retval = convoff(size, offset, lckdat, SEEK_SET))
return (retval);
/* Convert l_len to be the end of the rec lock l_end */
if (lckdat->l_len < 0)
return (EINVAL);
if (lckdat->l_len == 0)
lckdat->l_end = MAXEND;
else
lckdat->l_end += (lckdat->l_start - 1);
/* check for arithmetic overflow */
if (lckdat->l_start > lckdat->l_end)
return (EINVAL);
if ((klock = IS_LOCKED(&kernel_lock)) != 0)
unlockl(&kernel_lock);
FILOCK_LOCK();
GN_RECLK_LOCK(gp);
lock_list = & gp->gn_filocks;
do {
contflg = 0;
switch (lckdat->l_type) {
case F_RDLCK:
case F_WRLCK:
if ((found=blocked(*lock_list, lckdat, &insrt))==NULL) {
/* not blocked */
if (cmd & SETFLCK) {
retval=flckadj(
lock_list, insrt, lckdat, retry_id);
} else
lckdat->l_type = F_UNLCK;
}
else if (retry_fcn) {
/*
* blocked but caller cannot sleep,
* so check for deadlock and enter
* entry with retry_fcn on sleep locks
* list
*/
if (deadflck(found, lckdat)) {
retval = EDEADLK;
} else if ((sf = insflck(&sleeplcks,lckdat,
NULL)) == NULL) {
retval = ENOLCK;
} else {
found->state |= LCK_BLOCKER;
sf->state |= LCK_BLOCKED;
sf->pid = found->set.l_pid;
sf->sysid = found->set.l_sysid;
sf->vfs = found->set.l_vfs;
sf->filockp = (struct filock *)found;
sf->retry_fcn = (int (*) ())retry_fcn;
*retry_id = (ulong)found;
retval = EAGAIN;
}
}
else if (cmd & SLPFLCK) {
/* blocked */
/* do deadlock detection here */
if (deadflck (found, lckdat)) {
retval = EDEADLK;
}
else if((sf=insflck(&sleeplcks,lckdat,
NULL)) == NULL) {
retval = ENOLCK;
}
else {
found->state |= LCK_BLOCKER;
sf->state |= LCK_BLOCKED;
sf->pid = found->set.l_pid;
sf->sysid = found->set.l_sysid;
sf->vfs = found->set.l_vfs;
sf->filockp = found;
sf->retry_fcn = NULL;
if (cmd & INOFLCK) {
ASSERT(rele_fcn != NULL);
(*rele_fcn)(gp->gn_data);
}
e_assert_wait(&found->event, 1);
GN_RECLK_UNLOCK(gp);
FILOCK_UNLOCK();
if (e_block_thread()
== THREAD_INTERRUPTED)
retval = EINTR;
else
contflg = 1;
if (cmd & INOFLCK) {
ASSERT(lock_fcn != NULL);
(*lock_fcn)(gp->gn_data);
}
FILOCK_LOCK();
GN_RECLK_LOCK(gp);
sf->pid = 0;
sf->sysid = 0;
sf->vfs = 0;
sf->filockp = 0;
delflck (&sleeplcks, sf);
}
} else if (cmd & SETFLCK) {
retval = EAGAIN;
} else {
*lckdat = found->set;
}
break;
case F_UNLCK:
/* removing a file record lock */
if (cmd & SETFLCK) {
/*
* NFS may remove entries it placed on the
* sleep locks list. If sysid is -1 then
* remove all NFS entries on the sleep queue.
*/
if (lckdat->l_vfs == MNT_NFS && *retry_id) {
struct filock *next;
sf = sleeplcks;
while(sf) {
next = sf->next;
if(sf->set.l_vfs == MNT_NFS) {
if (lckdat->l_sysid == -1) {
delflck(&sleeplcks,sf);
} else if (sf->set.l_sysid ==
lckdat->l_sysid &&
sf->set.l_pid ==
lckdat->l_pid &&
sf->set.l_len ==
lckdat->l_len &&
sf->set.l_start ==
lckdat->l_start) {
delflck(&sleeplcks,sf);
break;
}
}
sf = next;
}
} else
retval = flckadj(lock_list, *lock_list,
lckdat, retry_id);
}
break;
default:
/* invalid lock type */
retval = EINVAL;
break;
}
} while (contflg);
GN_RECLK_UNLOCK(gp);
FILOCK_UNLOCK();
endloop:
/* Restore l_len */
if (lckdat->l_end == MAXEND)
lckdat->l_len = 0;
else
lckdat->l_len -= (lckdat->l_start-1);
if (! (cmd & SETFLCK))
whence = 0;
convoff(size, offset, lckdat, whence);
BUGLPR(lckbug, BUGACT, ("comm_reclock returning = %d\n", retval));
if (klock)
lockl(&kernel_lock, LOCK_SHORT);
return(retval);
}
/* convoff - converts the given data (start, whence) to the
* given whence.
*/
int
convoff(size, offset, lckdat, whence)
offset_t size;
offset_t offset;
struct eflock * lckdat;
int whence;
{
if (lckdat->l_whence == SEEK_CUR)
lckdat->l_start += offset;
else if (lckdat->l_whence == SEEK_END)
lckdat->l_start += size;
else if (lckdat->l_whence != SEEK_SET)
return (EINVAL);
if (lckdat->l_start < 0)
return (EINVAL);
if (whence == SEEK_CUR)
lckdat->l_start -= offset;
else if (whence == SEEK_END)
lckdat->l_start -= size;
else if (whence != SEEK_SET)
return (EINVAL);
lckdat->l_whence = whence;
return (0);
}
/* deadflck does the deadlock detection for the given record */
int
deadflck(flp, lockdat)
struct filock *flp;
struct eflock *lockdat;
{
register struct filock *blck, *sf;
pid_t blckpid;
ulong blcksysid;
ushort blckvfs;
blck = flp; /* current blocking lock pointer */
blckpid = blck->set.l_pid;
blcksysid = blck->set.l_sysid;
blckvfs = blck->set.l_vfs;
do {
if ( (blckpid == lockdat->l_pid)
&& (blcksysid == lockdat->l_sysid)
&& (blckvfs == lockdat->l_vfs))
return(1);
/* if the blocking process is sleeping on a locked region,
* change the blocked lock to this one.
*/
for (sf = sleeplcks; sf != NULL; sf = sf->next) {
if ( (blckpid == sf->set.l_pid)
&& (blcksysid == sf->set.l_sysid)
&& (blckvfs == sf->set.l_vfs)
&& (sf->state & LCK_BLOCKED)) {
blckpid = sf->pid;
blcksysid = sf->sysid;
blckvfs = sf->vfs;
break;
}
}
blck = sf;
} while (blck != NULL);
return(0);
}
/*
** lock wakeup
**
** 1. marks lock as zombie on sleep and current lock lists
** 2. iff necessary, wakes up anyone sleeping on this lock
*/
lock_wakeup (lp, retry_func)
struct filock *lp;
int (**retry_func)();
{
struct filock *slp, *next;
/*
* never will succeed on this test if we're deleting
* the lock from the sleep lock list since
* it will be LCK_BLOCKED, not LCK_BLOCKER
*/
if (lp->state & LCK_BLOCKER)
{
next = sleeplcks;
while (slp = next) {
next = slp->next;
/*
* Find any sleep locks blocking on this lock and
* mark them as not LCK_BLOCKED so deadflck will ignore
* them.
*/
if (lp == slp->filockp) {
slp->state &= ~LCK_BLOCKED;
lp->state &= ~LCK_BLOCKER;
/*
** never test for WASBLOCK
** but useful when debugging
*/
lp->state |= LCK_WASBLOCK;
/*
* If there is a retry funcion assoc.
* with this sleeper, then callit and
* return the address of the retry funcion
* so that flckadj() can call the retry
* when it has done all of its lock_wakeups
*/
if (slp->retry_fcn) {
(*slp->retry_fcn)(lp, NULL);
*retry_func = slp->retry_fcn;
slp->pid = 0;
slp->sysid = 0;
slp->vfs = 0;
slp->retry_fcn = NULL;
delflck(&sleeplcks, slp);
}
}
}
e_wakeupx(&lp->event, E_WKX_NO_PREEMPT);
}
return;
}
/*
* flock_grow - Attempt to grow the file lock list.
*/
static
flock_grow()
{
register struct filock *fip;
register struct filock *fend;
register int i;
fip = (struct filock *) v.ve_lock;
i = MIN(nflox_max, v.v_lock + PAGESIZE/sizeof(struct filock));
fend = flox + i;
/* initialize another page of file locks */
for( ; fip < fend; fip++ )
{
if (frlock == NULL)
{
fip->next = fip->prev = NULL;
frlock = fip;
}
else
{
fip->next = frlock;
fip->prev = NULL;
frlock = (frlock->prev = fip);
}
}
v.v_lock = i;
v.ve_lock = (caddr_t) fip;
}