flock: Add BSD flock functionality to FUZIX
authorAlan Cox <alan@linux.intel.com>
Sat, 3 Jan 2015 15:14:05 +0000 (15:14 +0000)
committerAlan Cox <alan@linux.intel.com>
Sat, 3 Jan 2015 15:14:05 +0000 (15:14 +0000)
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.

Kernel/devio.c
Kernel/filesys.c
Kernel/include/kernel.h
Kernel/include/syscall_name.h
Kernel/syscall_fs2.c

index cda40a6..c1ca6ee 100644 (file)
@@ -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;
        }
index 8a2fc66..84991e9 100644 (file)
@@ -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));
index cbaaecd..601b979 100644 (file)
@@ -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__ */
index 6a6180d..035219d 100644 (file)
@@ -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
 };
index 2ee3aa9..e0fbb7f 100644 (file)
@@ -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