X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;ds=sidebyside;f=fs%2Flocks.c;h=3098b3f622b6f897f88cb01bfd765751ebf2d1f4;hb=6a77f38946aaee1cd85eeec6cf4229b204c15071;hp=47efea9a624b568762aaa6cdb759aa687d702281;hpb=87fc8d1bb10cd459024a742c6a10961fefcef18f;p=linux-2.6.git diff --git a/fs/locks.c b/fs/locks.c index 47efea9a6..3098b3f62 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -122,7 +122,9 @@ #include #include #include +#include #include +#include #include #include @@ -148,12 +150,16 @@ static kmem_cache_t *filelock_cache; /* Allocate an empty lock structure. */ static struct file_lock *locks_alloc_lock(void) { + if (!vx_locks_avail(1)) + return NULL; return kmem_cache_alloc(filelock_cache, SLAB_KERNEL); } /* Free a lock which is not in use. */ static inline void locks_free_lock(struct file_lock *fl) { + vx_locks_dec(fl); + if (fl == NULL) { BUG(); return; @@ -172,7 +178,12 @@ static inline void locks_free_lock(struct file_lock *fl) fl->fl_ops->fl_release_private(fl); fl->fl_ops = NULL; } - fl->fl_lmops = NULL; + + if (fl->fl_lmops) { + if (fl->fl_lmops->fl_release_private) + fl->fl_lmops->fl_release_private(fl); + fl->fl_lmops = NULL; + } kmem_cache_free(filelock_cache, fl); } @@ -192,6 +203,7 @@ void locks_init_lock(struct file_lock *fl) fl->fl_start = fl->fl_end = 0; fl->fl_ops = NULL; fl->fl_lmops = NULL; + fl->fl_xid = -1; } EXPORT_SYMBOL(locks_init_lock); @@ -227,6 +239,10 @@ void locks_copy_lock(struct file_lock *new, struct file_lock *fl) new->fl_lmops = fl->fl_lmops; if (fl->fl_ops && fl->fl_ops->fl_copy_lock) fl->fl_ops->fl_copy_lock(new, fl); + if (fl->fl_lmops && fl->fl_lmops->fl_copy_lock) + fl->fl_lmops->fl_copy_lock(new, fl); + + new->fl_xid = fl->fl_xid; } EXPORT_SYMBOL(locks_copy_lock); @@ -263,6 +279,10 @@ static int flock_make_lock(struct file *filp, struct file_lock **lock, fl->fl_flags = FL_FLOCK; fl->fl_type = type; fl->fl_end = OFFSET_MAX; + + /* check agains file xid maybe ? */ + fl->fl_xid = vx_current_xid(); + vx_locks_inc(fl); *lock = fl; return 0; @@ -383,13 +403,31 @@ static int flock64_to_posix_lock(struct file *filp, struct file_lock *fl, } #endif -/* Allocate a file_lock initialised to this type of lease */ -static int lease_alloc(struct file *filp, int type, struct file_lock **flp) +/* default lease lock manager operations */ +static void lease_break_callback(struct file_lock *fl) { - struct file_lock *fl = locks_alloc_lock(); - if (fl == NULL) - return -ENOMEM; + kill_fasync(&fl->fl_fasync, SIGIO, POLL_MSG); +} + +static void lease_release_private_callback(struct file_lock *fl) +{ + if (!fl->fl_file) + return; + f_delown(fl->fl_file); + fl->fl_file->f_owner.signum = 0; +} + +struct lock_manager_operations lease_manager_ops = { + .fl_break = lease_break_callback, + .fl_release_private = lease_release_private_callback, +}; + +/* + * Initialize a lease, use the default lock manager operations + */ +static int lease_init(struct file *filp, int type, struct file_lock *fl) + { fl->fl_owner = current->files; fl->fl_pid = current->tgid; @@ -402,8 +440,24 @@ static int lease_alloc(struct file *filp, int type, struct file_lock **flp) fl->fl_start = 0; fl->fl_end = OFFSET_MAX; fl->fl_ops = NULL; - fl->fl_lmops = NULL; + fl->fl_lmops = &lease_manager_ops; + return 0; +} + +/* Allocate a file_lock initialised to this type of lease */ +static int lease_alloc(struct file *filp, int type, struct file_lock **flp) +{ + struct file_lock *fl = locks_alloc_lock(); + int error; + if (fl == NULL) + return -ENOMEM; + + fl->fl_xid = vx_current_xid(); + vx_locks_inc(fl); + error = lease_init(filp, type, fl); + if (error) + return error; *flp = fl; return 0; } @@ -730,7 +784,11 @@ static int __posix_lock_file(struct inode *inode, struct file_lock *request) * so we get them in advance to avoid races. */ new_fl = locks_alloc_lock(); + new_fl->fl_xid = vx_current_xid(); + vx_locks_inc(new_fl); new_fl2 = locks_alloc_lock(); + new_fl2->fl_xid = vx_current_xid(); + vx_locks_inc(new_fl2); lock_kernel(); if (request->fl_type != F_UNLCK) { @@ -971,7 +1029,7 @@ int locks_mandatory_locked(struct inode *inode) * @count: length of area to check * * Searches the inode's list of locks to find any POSIX locks which conflict. - * This function is called from locks_verify_area() and + * This function is called from rw_verify_area() and * locks_verify_truncate(). */ int locks_mandatory_area(int read_write, struct inode *inode, @@ -1026,13 +1084,8 @@ static int lease_modify(struct file_lock **before, int arg) if (error) return error; locks_wake_up_blocks(fl); - if (arg == F_UNLCK) { - struct file *filp = fl->fl_file; - - f_delown(filp); - filp->f_owner.signum = 0; + if (arg == F_UNLCK) locks_delete_lock(before); - } return 0; } @@ -1055,6 +1108,24 @@ static void time_out_leases(struct inode *inode) } } + /** +* remove_lease - let time_out_leases remove the lease. +* @@file_lock: the lease to remove +*/ +void remove_lease(struct file_lock *fl) +{ + lock_kernel(); + if (!fl || !IS_LEASE(fl)) + goto out; + fl->fl_type = F_UNLCK | F_INPROGRESS; + fl->fl_break_time = jiffies - 10; + time_out_leases(fl->fl_file->f_dentry->d_inode); +out: + unlock_kernel(); +} + +EXPORT_SYMBOL(remove_lease); + /** * __break_lease - revoke all outstanding leases on file * @inode: the inode of the file to return @@ -1119,7 +1190,10 @@ int __break_lease(struct inode *inode, unsigned int mode) if (fl->fl_type != future) { fl->fl_type = future; fl->fl_break_time = break_time; - kill_fasync(&fl->fl_fasync, SIGIO, POLL_MSG); + if (fl->fl_lmops && fl->fl_lmops->fl_break) + fl->fl_lmops->fl_break(fl); + else /* lease must have lmops break callback */ + BUG(); } } @@ -1170,7 +1244,7 @@ void lease_get_mtime(struct inode *inode, struct timespec *time) { struct file_lock *flock = inode->i_flock; if (flock && IS_LEASE(flock) && (flock->fl_type & F_WRLCK)) - *time = CURRENT_TIME; + *time = current_fs_time(inode->i_sb); else *time = inode->i_mtime; } @@ -1219,45 +1293,36 @@ int fcntl_getlease(struct file *filp) } /** - * fcntl_setlease - sets a lease on an open file - * @fd: open file descriptor + * __setlease - sets a lease on an open file * @filp: file pointer * @arg: type of lease to obtain + * @flp: input - file_lock to use, output - file_lock inserted * - * Call this fcntl to establish a lease on the file. - * Note that you also need to call %F_SETSIG to - * receive a signal when the lease is broken. + * The (input) flp->fl_lmops->fl_break function is required + * by break_lease(). + * + * Called with kernel lock held. */ -int fcntl_setlease(unsigned int fd, struct file *filp, long arg) +int __setlease(struct file *filp, long arg, struct file_lock **flp) { - struct file_lock *fl, **before, **my_before = NULL; - struct dentry *dentry; - struct inode *inode; + struct file_lock *fl, **before, **my_before = NULL, *lease = *flp; + struct dentry *dentry = filp->f_dentry; + struct inode *inode = dentry->d_inode; int error, rdlease_count = 0, wrlease_count = 0; - dentry = filp->f_dentry; - inode = dentry->d_inode; - - if ((current->fsuid != inode->i_uid) && !capable(CAP_LEASE)) - return -EACCES; - if (!S_ISREG(inode->i_mode)) - return -EINVAL; - error = security_file_lock(filp, arg); - if (error) - return error; - - lock_kernel(); - time_out_leases(inode); - /* - * FIXME: What about F_RDLCK and files open for writing? - */ + error = -EINVAL; + if (!flp || !(*flp) || !(*flp)->fl_lmops || !(*flp)->fl_lmops->fl_break) + goto out; + error = -EAGAIN; + if ((arg == F_RDLCK) && (atomic_read(&inode->i_writecount) > 0)) + goto out; if ((arg == F_WRLCK) && ((atomic_read(&dentry->d_count) > 1) || (atomic_read(&inode->i_count) > 1))) - goto out_unlock; + goto out; /* * At this point, we know that if there is an exclusive @@ -1285,33 +1350,112 @@ int fcntl_setlease(unsigned int fd, struct file *filp, long arg) if ((arg == F_RDLCK && (wrlease_count > 0)) || (arg == F_WRLCK && ((rdlease_count + wrlease_count) > 0))) - goto out_unlock; + goto out; if (my_before != NULL) { error = lease_modify(my_before, arg); - goto out_unlock; + goto out; } error = 0; if (arg == F_UNLCK) - goto out_unlock; + goto out; error = -EINVAL; if (!leases_enable) - goto out_unlock; + goto out; error = lease_alloc(filp, arg, &fl); + if (error) + goto out; + + locks_copy_lock(fl, lease); + + locks_insert_lock(before, fl); + + *flp = fl; +out: + return error; +} + + /** + * setlease - sets a lease on an open file + * @filp: file pointer + * @arg: type of lease to obtain + * @lease: file_lock to use + * + * Call this to establish a lease on the file. + * The fl_lmops fl_break function is required by break_lease + */ + +int setlease(struct file *filp, long arg, struct file_lock **lease) +{ + struct dentry *dentry = filp->f_dentry; + struct inode *inode = dentry->d_inode; + int error; + + if ((current->fsuid != inode->i_uid) && !capable(CAP_LEASE)) + return -EACCES; + if (!S_ISREG(inode->i_mode)) + return -EINVAL; + error = security_file_lock(filp, arg); + if (error) + return error; + + lock_kernel(); + error = __setlease(filp, arg, lease); + unlock_kernel(); + + return error; +} + +EXPORT_SYMBOL(setlease); + +/** + * fcntl_setlease - sets a lease on an open file + * @fd: open file descriptor + * @filp: file pointer + * @arg: type of lease to obtain + * + * Call this fcntl to establish a lease on the file. + * Note that you also need to call %F_SETSIG to + * receive a signal when the lease is broken. + */ +int fcntl_setlease(unsigned int fd, struct file *filp, long arg) +{ + struct file_lock fl, *flp = &fl; + struct dentry *dentry = filp->f_dentry; + struct inode *inode = dentry->d_inode; + int error; + + if ((current->fsuid != inode->i_uid) && !capable(CAP_LEASE)) + return -EACCES; + if (!S_ISREG(inode->i_mode)) + return -EINVAL; + error = security_file_lock(filp, arg); + if (error) + return error; + + locks_init_lock(&fl); + error = lease_init(filp, arg, &fl); + if (error) + return error; + + lock_kernel(); + + error = __setlease(filp, arg, &flp); if (error) goto out_unlock; - error = fasync_helper(fd, filp, 1, &fl->fl_fasync); + error = fasync_helper(fd, filp, 1, &flp->fl_fasync); if (error < 0) { - locks_free_lock(fl); + /* remove lease just inserted by __setlease */ + flp->fl_type = F_UNLCK | F_INPROGRESS; + flp->fl_break_time = jiffies- 10; + time_out_leases(inode); goto out_unlock; } - locks_insert_lock(before, fl); - error = f_setown(filp, current->pid, 0); out_unlock: unlock_kernel(); @@ -1435,9 +1579,6 @@ int fcntl_getlk(struct file *filp, struct flock __user *l) error = filp->f_op->lock(filp, F_GETLK, &file_lock); if (error < 0) goto out; - else if (error == LOCK_USE_CLNT) - /* Bypass for NFS with no locking - 2.0.36 compat */ - fl = posix_test_lock(filp, &file_lock); else fl = (file_lock.fl_type == F_UNLCK ? NULL : &file_lock); } else { @@ -1485,6 +1626,9 @@ int fcntl_setlk(struct file *filp, unsigned int cmd, struct flock __user *l) if (file_lock == NULL) return -ENOLCK; + file_lock->fl_xid = vx_current_xid(); + vx_locks_inc(file_lock); + /* * This might block, so we do it before checking the inode. */ @@ -1580,9 +1724,6 @@ int fcntl_getlk64(struct file *filp, struct flock64 __user *l) error = filp->f_op->lock(filp, F_GETLK, &file_lock); if (error < 0) goto out; - else if (error == LOCK_USE_CLNT) - /* Bypass for NFS with no locking - 2.0.36 compat */ - fl = posix_test_lock(filp, &file_lock); else fl = (file_lock.fl_type == F_UNLCK ? NULL : &file_lock); } else { @@ -1619,6 +1760,9 @@ int fcntl_setlk64(struct file *filp, unsigned int cmd, struct flock64 __user *l) if (file_lock == NULL) return -ENOLCK; + file_lock->fl_xid = vx_current_xid(); + vx_locks_inc(file_lock); + /* * This might block, so we do it before checking the inode. */