From: Alan Cox Date: Sat, 3 Jan 2015 15:14:05 +0000 (+0000) Subject: flock: Add BSD flock functionality to FUZIX X-Git-Url: https://git.ndcode.org/public/gitweb.cgi?a=commitdiff_plain;h=dc20145ae7442f9473c3bac4871a54cc447ee84f;p=FUZIX.git flock: Add BSD flock functionality to FUZIX System 5 has its own rather more convoluted locking so for once we'll favour BSD over System 5. This is an initial patch for testing. Theory: We recover spare bits from the inode c_busy, which is now a set of flags not a bool with 7 wasted bits. Z80 has cheap bit ops, and the dirty bit is the top bit which is cheap on most other processors too. The file can be in one of 16 lock states kept in four of the recovered bits (CFLOCK bits) 0 - unlocked 1 - 14 that many shared locks (15+ gives you an ENOLCKS error) 15 - exclusive We use one of the spare O_ bits in each file object to keep a single bit telling us this handle has a lock. The BSD API only allows one lock per file table entry so that one bit is sufficient because we know that - if our lock bit is set and the file handle is exclusive lock then we must own the lock (as its exclusive) so we drop to 0 locks - if our lock bit is set and the handle is not exclusive locked then we can't own an exclusive lock so we must therefore be one of the shared locks Total cost about 450 bytes of kernel memory and no extra data. --- diff --git a/Kernel/devio.c b/Kernel/devio.c index cda40a6b..c1ca6ee0 100644 --- a/Kernel/devio.c +++ b/Kernel/devio.c @@ -536,7 +536,7 @@ void idump(void) ip->c_node.i_mode); kprintf("%d\t%d\t%d\t%d\n", /* line split for compiler */ ip->c_node.i_nlink, ip->c_node.i_addr[0], - ip->c_refs, ip->c_dirty); + ip->c_refs, ip->c_flags); if (!ip->c_magic) break; } diff --git a/Kernel/filesys.c b/Kernel/filesys.c index 8a2fc663..84991e96 100644 --- a/Kernel/filesys.c +++ b/Kernel/filesys.c @@ -321,7 +321,7 @@ bool ch_link(inoptr wd, char *oldname, char *newname, inoptr nindex) if(udata.u_error) return false; - setftime(wd, A_TIME|M_TIME|C_TIME); /* Sets c_dirty */ + setftime(wd, A_TIME|M_TIME|C_TIME); /* Sets CDIRTY */ /* Update file length to next block */ if(wd->c_node.i_size & BLKMASK) @@ -652,13 +652,39 @@ int8_t oft_alloc(void) return -1; } +/* + * To minimise storage we don't track exclusive locks explicitly. We know + * that if we are dropping an exclusive lock then we must be the owner, + * and if we are dropping a lock that is not exclusive we must own one of + * the non exclusive locks. + */ +void deflock(struct oft *ofptr) +{ + inoptr i = ofptr->o_inode; + uint8_t c = i->c_flags & CFLOCK; + if (ofptr->o_access & O_FLOCK) { + if (c == CFLEX) + c = 0; + else + c--; + i->c_flags = (i->c_flags & ~CFLOCK) | c; + wakeup(&i->c_flags); + } +} + +/* + * Drop a reference in the open file table. If this is the last reference + * from a user file table then drop any file locks, dereference the inode + * and mark empty + */ void oft_deref(int8_t of) { struct oft *ofptr; ofptr = of_tab + of; if(!(--ofptr->o_refs) && ofptr->o_inode) { + deflock(ofptr); i_deref(ofptr->o_inode); ofptr->o_inode = NULLINODE; } @@ -726,7 +752,7 @@ void i_deref(inoptr ino) f_trunc(ino); /* If the inode was modified, we must write it to disk. */ - if(!(ino->c_refs) && ino->c_dirty) + if(!(ino->c_refs) && (ino->c_flags & CDIRTY)) { if(!(ino->c_node.i_nlink)) { @@ -753,7 +779,7 @@ void wr_inode(inoptr ino) buf =(struct dinode *)bread(ino->c_dev, blkno,0); memcpy((char *)((char **)&buf[ino->c_num & 0x07]), (char *)(&ino->c_node), 64); bfree((bufptr)buf, 2); - ino->c_dirty = false; + ino->c_flags &= ~CDIRTY; } @@ -793,7 +819,7 @@ void f_trunc(inoptr ino) memset((char *)ino->c_node.i_addr, 0, sizeof(ino->c_node.i_addr)); - ino->c_dirty = true; + ino->c_flags |= CDIRTY; ino->c_node.i_size = 0; } @@ -846,7 +872,7 @@ blkno_t bmap(inoptr ip, blkno_t bn, int rwflg) if(rwflg ||(nb = blk_alloc(dev))==0) return(NULLBLK); ip->c_node.i_addr[bn] = nb; - ip->c_dirty = true; + ip->c_flags |= CDIRTY; } return(nb); } @@ -871,7 +897,7 @@ blkno_t bmap(inoptr ip, blkno_t bn, int rwflg) if(rwflg || !(nb = blk_alloc(dev))) return(NULLBLK); ip->c_node.i_addr[20-j] = nb; - ip->c_dirty = true; + ip->c_flags |= CDIRTY; } /* fetch through the indirect blocks @@ -989,7 +1015,7 @@ uint8_t getperm(inoptr ino) */ void setftime(inoptr ino, uint8_t flag) { - ino->c_dirty = true; + ino->c_flags |= CDIRTY; if(flag & A_TIME) rdtime32(&(ino->c_node.i_atime)); diff --git a/Kernel/include/kernel.h b/Kernel/include/kernel.h index cbaaecd5..601b9796 100644 --- a/Kernel/include/kernel.h +++ b/Kernel/include/kernel.h @@ -183,7 +183,11 @@ typedef struct cinode { // note: exists in memory *and* on disk uint16_t c_num; /* Inode # */ dinode c_node; uint8_t c_refs; /* In-core reference count */ - bool c_dirty; /* Modified flag. */ + uint8_t c_flags; +#define CDIRTY 0x80 /* Modified flag. */ +#define CFLOCK 0x0F /* flock bits */ +#define CFLEX 0x0F /* locked exclusive */ +#define CFMAX 0x0E /* highest shared lock count permitted */ } cinode, *inoptr; #define NULLINODE ((inoptr)NULL) @@ -431,13 +435,14 @@ struct s_argblk { #define O_APPEND 4 #define O_SYNC 8 #define O_NDELAY 16 +#define O_FLOCK 128 /* Cannot be user set */ #define O_CREAT 256 #define O_EXCL 512 #define O_TRUNC 1024 #define O_NOCTTY 2048 #define O_CLOEXEC 4096 -#define O_BADBITS (32 | 64 | 128 | 8192 | 16384 | 32768U) +#define O_BADBITS (32 | 64 | O_FLOCK | 8192 | 16384 | 32768U) #define F_GETFL 0 #define F_SETFL 1 @@ -447,6 +452,11 @@ struct s_argblk { #define FNDELAY O_NDELAY +#define LOCK_SH 0 +#define LOCK_EX 1 +#define LOCK_UN 2 /* Must be highest */ + +#define LOCK_NB O_NDELAY /* Must be O_NDELAY */ /* * Error codes @@ -488,7 +498,8 @@ struct s_argblk { #define EDOM 33 /* Argument too large */ #define ERANGE 34 /* Result too large */ -#define EWOULDBLOCK 35 /* Operation would block */ +#define EWOULDBLOCK EAGAIN /* Operation would block */ +#define ENOLCK 35 /* Lock table full */ #define ENOTEMPTY 36 /* Directory is not empty */ #define ENAMETOOLONG 37 /* File name too long */ @@ -619,6 +630,7 @@ CODE1 void i_free(uint16_t devno, uint16_t ino); CODE1 blkno_t blk_alloc(uint16_t devno); CODE1 void blk_free(uint16_t devno, blkno_t blk); CODE1 int8_t oft_alloc(void); +CODE1 void deflock(struct oft *ofptr); CODE1 void oft_deref(int8_t of); /* returns index of slot, or -1 on failure */ CODE1 int8_t uf_alloc(void); @@ -780,13 +792,15 @@ CODE2 int16_t _fcntl(void); /* FUZIX system call 47 */ CODE2 int16_t _fchdir(void); /* FUZIX system call 48 */ CODE2 int16_t _fchmod(void); /* FUZIX system call 49 */ CODE2 int16_t _fchown(void); /* FUZIX system call 50 */ -CODE2 int16_t _mkdir(void); /* FUZIX system call 51 */ +CODE2 int16_t _mkdir(void); /* FUZIX system call 51 */ CODE2 int16_t _rmdir(void); /* FUZIX system call 52 */ -CODE2 int16_t _setpgrp(void); /* FUZIX system call 53 */ -CODE2 int16_t _uname(void); /* FUZIX system call 54 */ -CODE2 int16_t _waitpid(void); /* FUZIX system call 55 */ -CODE2 int16_t _profil(void); /* FUZIX system call 56 */ -CODE2 int16_t _uadmin(void); /* FUZIX system call 57 */ +CODE2 int16_t _setpgrp(void); /* FUZIX system call 53 */ +CODE2 int16_t _uname(void); /* FUZIX system call 54 */ +CODE2 int16_t _waitpid(void); /* FUZIX system call 55 */ +CODE2 int16_t _profil(void); /* FUZIX system call 56 */ +CODE2 int16_t _uadmin(void); /* FUZIX system call 57 */ CODE2 int16_t _nice(void); /* FUZIX system call 58 */ -CODE2 int16_t _sigdisp(void); /* FUZIX system call 59 */ +CODE2 int16_t _sigdisp(void); /* FUZIX system call 59 */ +CODE2 int16_t _flock(void); /* FUZIX system call 60 */ + #endif /* __FUZIX__KERNEL_DOT_H__ */ diff --git a/Kernel/include/syscall_name.h b/Kernel/include/syscall_name.h index 6a6180d6..035219d6 100644 --- a/Kernel/include/syscall_name.h +++ b/Kernel/include/syscall_name.h @@ -1,4 +1,4 @@ -#define NR_SYSCALL 60 +#define NR_SYSCALL 61 char *syscall_name[NR_SYSCALL] = { "_exit", @@ -61,6 +61,7 @@ char *syscall_name[NR_SYSCALL] = { "uadmin", "nice", "_sigdisp", + "flock", }; int syscall_args[NR_SYSCALL] = { @@ -123,5 +124,6 @@ int syscall_args[NR_SYSCALL] = { 4, //_profil 3, //uadmin 1, //nice - 2 //_sigdisp + 2, //_sigdisp + 2, //flock }; diff --git a/Kernel/syscall_fs2.c b/Kernel/syscall_fs2.c index 2ee3aa91..e0fbb7f1 100644 --- a/Kernel/syscall_fs2.c +++ b/Kernel/syscall_fs2.c @@ -676,3 +676,93 @@ int16_t _uname(void) } #undef buf + +/************************************** +flock(fd, lockop) Function 60 +int file; +int lockop; + +Perform locking upon a file. +**************************************/ + +#define file (uint16_t)udata.u_argn +#define lockop (uint16_t)udata.u_argn1 + +int16_t _flock(void) +{ + inoptr ino; + struct oft *o; + staticfast uint8_t c; + staticfast uint8_t lock = lockop & ~LOCK_NB; + staticfast int self; + + self = 0; + + if (lock > LOCK_UN) { + udata.u_error = EINVAL; + return -1; + } + + if ((ino = getinode(file)) == NULLINODE) + return -1; + o = &of_tab[udata.u_files[file]]; + + c = ino->c_flags & CFLOCK; + + /* Upgrades and downgrades. Check if we are in fact doing a no-op */ + if (o->o_access & O_FLOCK) { + self = 1; + /* Shared or exclusive to shared can't block and is easy */ + if (lock == LOCK_SH) { + if (c == CFLEX) + c = 1; + goto done; + } + /* Exclusive to exclusive - no op */ + if (c == CFLEX && lock == LOCK_EX) + return 0; + /* Shared to exclusive - handle via the loop */ + } + + + /* Unlock - drop the locks, mark us not a lock holder. Doesn't block */ + if (lockop == LOCK_UN) { + o->o_access &= ~O_FLOCK; + deflock(o); + return 0; + } + + do { + /* Exclusive lock must have no holders */ + if (c == self && lock == LOCK_EX) { + c = CFLEX; + goto done; + } + if (c < CFMAX) { + c++; + goto done; + } + if (c == CFMAX) { + udata.u_error = ENOLCK; + return -1; + } + /* LOCK_NB is defined as O_NDELAY... */ + if (psleep_flags(&ino->c_flags, (lockop & LOCK_NB))) + return -1; + /* locks will hopefully have changed .. */ + c = ino->c_flags & CFLOCK; + } while (1); + +done: + if (o->o_access & O_FLOCK) + deflock(o); + ino->c_flags &= ~CFLOCK; + ino->c_flags |= c; + o->o_access |= O_FLOCK; + wakeup(&ino->c_flags); + return 0; +} + + +#undef file +#undef lockop \ No newline at end of file