From b9a61c0d0d94182340ac416abea42d07bc64baff Mon Sep 17 00:00:00 2001 From: David Crawshaw Date: Thu, 7 Dec 2006 19:25:30 -0800 Subject: [PATCH] moving locking code into UnixRuntime and use GlobalScope to track extra-process locks darcs-hash:20061208032530-0c629-d37fccab40ea0f32674611126e45830f63de4ee1.gz --- src/org/ibex/nestedvm/Runtime.java | 137 ++----------------------- src/org/ibex/nestedvm/UnixRuntime.java | 162 +++++++++++++++++++++++++++++- src/org/ibex/nestedvm/util/Platform.java | 2 +- src/org/ibex/nestedvm/util/Seekable.java | 31 ++++-- 4 files changed, 193 insertions(+), 139 deletions(-) diff --git a/src/org/ibex/nestedvm/Runtime.java b/src/org/ibex/nestedvm/Runtime.java index 44505c1..f8a1579 100644 --- a/src/org/ibex/nestedvm/Runtime.java +++ b/src/org/ibex/nestedvm/Runtime.java @@ -76,10 +76,6 @@ public abstract class Runtime implements UsermodeConstants,Registers,Cloneable { /** Table containing all open file descriptors. (Entries are null if the fd is not in use */ FD[] fds; // package-private for UnixRuntime boolean closeOnExec[]; - - /** Table of all current file locks held by this process. */ - Seekable.Lock[] locks; - public static final int LOCK_MAX = 8; /** Pointer to a SecurityManager for this process */ SecurityManager sm; @@ -126,7 +122,6 @@ public abstract class Runtime implements UsermodeConstants,Registers,Cloneable { r.startTime = 0; r.fds = new FD[OPEN_MAX]; for(int i=0;ifdn and removes it from the file descriptor table */ public final boolean closeFD(int fdn) { if(state == EXITED || state == EXECED) throw new IllegalStateException("closeFD called in inappropriate state"); if(fdn < 0 || fdn >= OPEN_MAX) return false; if(fds[fdn] == null) return false; - // release all fcntl locks on this file - Seekable s = fds[fdn].seekable(); - if (s != null) { - try { - for (int i=0; i < LOCK_MAX; i++) { - if (locks[i] != null && s.equals(locks[i].seekable())) { - locks[i].release(); - locks[i] = null; - } - } - } catch (IOException e) { throw new RuntimeException(e); } - } + _closedFD(fds[fdn]); fds[fdn].close(); fds[fdn] = null; @@ -1031,7 +1017,7 @@ public abstract class Runtime implements UsermodeConstants,Registers,Cloneable { return 0; } - private int sys_fcntl(int fdn, int cmd, int arg) throws FaultException { + final int sys_fcntl(int fdn, int cmd, int arg) throws FaultException { int i; if(fdn < 0 || fdn >= OPEN_MAX) return -EBADFD; @@ -1054,123 +1040,14 @@ public abstract class Runtime implements UsermodeConstants,Registers,Cloneable { return closeOnExec[fdn] ? 1 : 0; case F_GETLK: case F_SETLK: - try { - return sys_fcntl_lock(fd, cmd, arg); - } catch (IOException e) { throw new RuntimeException(e);} + if(STDERR_DIAG) System.err.println("WARNING: file locking requires UnixRuntime"); + return -ENOSYS; default: if(STDERR_DIAG) System.err.println("WARNING: Unknown fcntl command: " + cmd); return -ENOSYS; } } - /** Implements the F_GETLK and F_SETLK cases of fcntl syscall. - * If l_start = 0 and l_len = 0 the lock refers to the entire file. - struct flock { - short l_type; // lock type: F_UNLCK, F_RDLCK, F_WRLCK - short l_whence; // type of l_start: SEEK_SET, SEEK_CUR, SEEK_END - long l_start; // starting offset, bytes - long l_len; // len = 0 means until EOF - short l_pid; // lock owner - short l_xxx; // padding - }; - */ - private int sys_fcntl_lock(FD fd, int cmd, int arg) - throws FaultException, IOException { - if (arg == 0) { System.out.println("BAD ARG"); return -EINVAL; } - int word = memRead(arg); - int l_start = memRead(arg+4); - int l_len = memRead(arg+8); - int l_type = word>>16; - int l_whence = word&0x00ff; - - Seekable s = fd.seekable(); - if (s == null) return -EINVAL; - - switch (l_whence) { - case SEEK_SET: break; - case SEEK_CUR: l_start += s.pos(); break; - case SEEK_END: l_start += s.length(); break; - default: return -1; - } - - if (cmd != F_GETLK && cmd != F_SETLK) return -EINVAL; - - if (cmd == F_GETLK) { - // Check if an l_type lock can be aquired. The only way to - // do this within the Java API is to try and create a lock. - Seekable.Lock lock = s.lock(l_start, l_len, l_type == F_RDLCK); - - if (lock != null) { - // no lock exists - memWrite(arg, SEEK_SET|(F_UNLCK<<16)); - lock.release(); - } - - return 0; - } - - // now processing F_SETLK - if (cmd != F_SETLK) return -EINVAL; - - if (l_type == F_UNLCK) { - // release all locks that fall within the boundaries given - for (int i=0; i < LOCK_MAX; i++) { - if (locks[i] == null || !s.equals(locks[i].seekable())) - continue; - - int pos = (int)locks[i].position(); - if (pos < l_start) continue; - if (l_start != 0 && l_len != 0) // start/len 0 means unlock all - if (pos + locks[i].size() > l_start + l_len) - continue; - - locks[i].release(); - locks[i] = null; - } - return 0; - - } else if (l_type == F_RDLCK || l_type == F_WRLCK) { - // first see if a lock already exists - for (int i=0; i < LOCK_MAX; i++) { - if (locks[i] == null || !s.equals(locks[i].seekable())) - continue; - int pos = (int)locks[i].position(); - int size = (int)locks[i].size(); - if (l_start < pos && pos + size < l_start + l_len) { - // found a lock contained in the new requested lock - locks[i].release(); - locks[i] = null; - - } else if (l_start >= pos && pos + size >= l_start + l_len) { - // found a lock that contains the requested lock - if (locks[i].isShared() == (l_type == F_RDLCK)) { - memWrite(arg+4, pos); - memWrite(arg+8, size); - return 0; - } else { - locks[i].release(); - locks[i] = null; - } - } - } - - // create the lock - Seekable.Lock lock = s.lock(l_start, l_len, l_type == F_RDLCK); - if (lock == null) return -EAGAIN; - - int i; - for (i=0; i < LOCK_MAX; i++) - if (locks[i] == null) break; - if (i == LOCK_MAX) return -ENOLCK; - locks[i] = lock; - return 0; - - } else { - return -EINVAL; - } - } - - /** The syscall dispatcher. The should be called by subclasses when the syscall instruction is invoked. syscall should be the contents of V0 and a, b, c, and d should be diff --git a/src/org/ibex/nestedvm/UnixRuntime.java b/src/org/ibex/nestedvm/UnixRuntime.java index 8031f58..0291ab8 100644 --- a/src/org/ibex/nestedvm/UnixRuntime.java +++ b/src/org/ibex/nestedvm/UnixRuntime.java @@ -156,6 +156,7 @@ public abstract class UnixRuntime extends Runtime implements Cloneable { case SYS_fchown: return sys_fchown(a,b,c); case SYS_chmod: return sys_chmod(a,b,c); case SYS_fchmod: return sys_fchmod(a,b,c); + case SYS_fcntl: return sys_fcntl_lock(a,b,c); case SYS_umask: return sys_umask(a); default: return super._syscall(syscall,a,b,c,d,e,f); @@ -701,7 +702,163 @@ public abstract class UnixRuntime extends Runtime implements Cloneable { copyout(buf,addr,n); return n; } - + + void _closedFD(FD fd) { + // release all fcntl locks on this file + Seekable s = fd.seekable(); + if (s == null) return; + + try { + for (int i=0; i < gs.locks.length; i++) { + Seekable.Lock l = gs.locks[i]; + if (l == null) continue; + if (s.equals(l.seekable()) && l.getOwner() == this) { + l.release(); + gs.locks[i] = null; + } + } + } catch (IOException e) { throw new RuntimeException(e); } + } + + /** Implements the F_GETLK and F_SETLK cases of fcntl syscall. + * If l_start = 0 and l_len = 0 the lock refers to the entire file. + * Uses GlobalState to ensure locking across processes in the same JVM. + struct flock { + short l_type; // lock type: F_UNLCK, F_RDLCK, F_WRLCK + short l_whence; // type of l_start: SEEK_SET, SEEK_CUR, SEEK_END + long l_start; // starting offset, bytes + long l_len; // len = 0 means until EOF + short l_pid; // lock owner + short l_xxx; // padding + }; + */ + private int sys_fcntl_lock(int fdn, int cmd, int arg) throws FaultException{ + if (cmd != F_GETLK && cmd != F_SETLK) return sys_fcntl(fdn, cmd, arg); + + if(fdn < 0 || fdn >= OPEN_MAX) return -EBADFD; + if(fds[fdn] == null) return -EBADFD; + FD fd = fds[fdn]; + + if (arg == 0) return -EINVAL; + int word = memRead(arg); + int l_start = memRead(arg+4); + int l_len = memRead(arg+8); + int l_type = word>>16; + int l_whence = word&0x00ff; + + Seekable.Lock[] locks = gs.locks; + Seekable s = fd.seekable(); + if (s == null) return -EINVAL; + + try { + + switch (l_whence) { + case SEEK_SET: break; + case SEEK_CUR: l_start += s.pos(); break; + case SEEK_END: l_start += s.length(); break; + default: return -1; + } + + if (cmd == F_GETLK) { + // The simple Java file locking below will happily return + // a lock that overlaps one already held by the JVM. Thus + // we must check over all the locks held by other Runtimes + for (int i=0; i < locks.length; i++) { + if (locks[i] == null || !s.equals(locks[i].seekable())) + continue; + if (!locks[i].overlaps(l_start, l_len)) + continue; + if (locks[i].getOwner() == this) + continue; + if (locks[i].isShared() && l_type == F_RDLCK) + continue; + + // overlapping lock held by another process + return 0; + } + + // check if an area is lockable by attempting to obtain a lock + Seekable.Lock lock = s.lock(l_start, l_len, l_type == F_RDLCK); + + if (lock != null) { // no lock exists + memWrite(arg, SEEK_SET|(F_UNLCK<<16)); + lock.release(); + } + + return 0; + } + + // now processing F_SETLK + if (cmd != F_SETLK) return -EINVAL; + + if (l_type == F_UNLCK) { + // release all locks that fall within the boundaries given + for (int i=0; i < locks.length; i++) { + if (locks[i] == null || !s.equals(locks[i].seekable())) + continue; + if (locks[i].getOwner() != this) continue; + + int pos = (int)locks[i].position(); + if (pos < l_start) continue; + if (l_start != 0 && l_len != 0) // start/len 0 means unlock all + if (pos + locks[i].size() > l_start + l_len) + continue; + + locks[i].release(); + locks[i] = null; + } + return 0; + + } else if (l_type == F_RDLCK || l_type == F_WRLCK) { + // first see if a lock already exists + for (int i=0; i < locks.length; i++) { + if (locks[i] == null || !s.equals(locks[i].seekable())) + continue; + + if (locks[i].getOwner() == this) { + // if this Runtime owns an overlapping lock work with it + if (locks[i].contained(l_start, l_len)) { + locks[i].release(); + locks[i] = null; + } else if (locks[i].contains(l_start, l_len)) { + if (locks[i].isShared() == (l_type == F_RDLCK)) { + // return this more general lock + memWrite(arg+4, (int)locks[i].position()); + memWrite(arg+8, (int)locks[i].size()); + return 0; + } else { + locks[i].release(); + locks[i] = null; + } + } + } else { + // if another Runtime has an lock and it is exclusive or + // we want an exclusive lock then fail + if (locks[i].overlaps(l_start, l_len) + && (!locks[i].isShared() || l_type == F_WRLCK)) + return -EAGAIN; + } + } + + // create the lock + Seekable.Lock lock = s.lock(l_start, l_len, l_type == F_RDLCK); + if (lock == null) return -EAGAIN; + lock.setOwner(this); + + int i; + for (i=0; i < locks.length; i++) + if (locks[i] == null) break; + if (i == locks.length) return -ENOLCK; + locks[i] = lock; + return 0; + + } else { + return -EINVAL; + } + + } catch (IOException e) { throw new RuntimeException(e); } + } + static class SocketFD extends FD { public static final int TYPE_STREAM = 0; public static final int TYPE_DGRAM = 1; @@ -1160,6 +1317,9 @@ public abstract class UnixRuntime extends Runtime implements Cloneable { final UnixRuntime[] tasks; int nextPID = 1; + /** Table of all current file locks held by this process. */ + Seekable.Lock[] locks = new Seekable.Lock[16]; + private MP[] mps = new MP[0]; private FS root; diff --git a/src/org/ibex/nestedvm/util/Platform.java b/src/org/ibex/nestedvm/util/Platform.java index a8db074..4b50f18 100644 --- a/src/org/ibex/nestedvm/util/Platform.java +++ b/src/org/ibex/nestedvm/util/Platform.java @@ -205,7 +205,7 @@ public abstract class Platform { } } - private static final class Jdk14FileLock implements Seekable.Lock { + private static final class Jdk14FileLock extends Seekable.Lock { private final Seekable s; private final FileLock l; diff --git a/src/org/ibex/nestedvm/util/Seekable.java b/src/org/ibex/nestedvm/util/Seekable.java index 964ebc2..d49df66 100644 --- a/src/org/ibex/nestedvm/util/Seekable.java +++ b/src/org/ibex/nestedvm/util/Seekable.java @@ -149,12 +149,29 @@ public abstract class Seekable { public void close() throws IOException { is.close(); } } - public interface Lock { - public Seekable seekable(); - public boolean isShared(); - public boolean isValid(); - public void release() throws IOException; - public long position(); - public long size(); + public abstract static class Lock { + private Object owner = null; + + public abstract Seekable seekable(); + public abstract boolean isShared(); + public abstract boolean isValid(); + public abstract void release() throws IOException; + public abstract long position(); + public abstract long size(); + + public void setOwner(Object o) { owner = o; } + public Object getOwner() { return owner; } + + public final boolean contains(int start, int len) { + return start >= position() && position() + size() >= start + len; + } + + public final boolean contained(int start, int len) { + return start < position() && position() + size() < start + len; + } + + public final boolean overlaps(int start, int len) { + return contains(start, len) || contained(start, len); + } } } -- 1.7.10.4