2.5: further llseek cleanup (1/3)
Subsystems (3)
PATCH 1/1 2.5: further llseek cleanup (1/3)
Linus,
This is the first of three patches implementing further llseek cleanup, against 2.5.3.
The 'push locking into llseek methods' patch was integrated into 2.5.3. The networking filesystems, however, do not protect i_size and can not rely on the inode semaphore used in generic_file_llseek.
This patch implements a remote_llseek method, which is basically the pre-2.5.3 version of generic_file_llseek. Locking is done via the BKL. When we have a saner locking system in place, we can push it into this function in lieu.
Coda, ncpfs, nfs, and smbfs have been converted to use this new llseek.
As always, Al's blessing is desired, but since he helped me on this I think he concurs ;-)
Robert Love
fs/coda/file.c +1 -1
@@ -267,7 +267,7 @@ }struct file_operations coda_file_operations = { - llseek: generic_file_llseek, + llseek: remote_llseek, read: coda_file_read, write: coda_file_write, mmap: coda_file_mmap,
fs/ncpfs/file.c +1 -1
@@ -281,7 +281,7 @@struct file_operations ncp_file_operations = { - llseek: generic_file_llseek, + llseek: remote_llseek, read: ncp_file_read, write: ncp_file_write, ioctl: ncp_ioctl,
fs/nfs/file.c +1 -1
@@ -41,7 +41,7 @@static int nfs_fsync (struct file *,struct dentry *dentry,int datasync);struct file_operations nfs_file_operations = { - llseek: generic_file_llseek, + llseek: remote_llseek, read: nfs_file_read, write: nfs_file_write, mmap: nfs_file_mmap,
fs/read_write.c +25 -0
@@ -51,6 +51,31 @@return retval; } +loff_t remote_llseek (struct file *file,loff_t offset,int origin) +{ +long long retval; + +lock_kernel (); +switch (origin) { +case 2 : + offset += file->f_dentry->d_inode->i_size; +break ; +case 1 : + offset += file->f_pos; + } + retval = -EINVAL ; +if (offset>=0 && offset<=file->f_dentry->d_inode->i_sb->s_maxbytes) { +if (offset != file->f_pos) { + file->f_pos = offset; + file->f_reada =0 ; + file->f_version = ++event; + } + retval = offset; + } +unlock_kernel (); +return retval; +} +loff_t no_llseek (struct file *file,loff_t offset,int origin) {return -ESPIPE ;
fs/smbfs/file.c +1 -1
@@ -381,7 +381,7 @@struct file_operations smb_file_operations = { - llseek: generic_file_llseek, + llseek: remote_llseek, read: smb_file_read, write: smb_file_write, ioctl: smb_ioctl,
include/linux/fs.h +1 -0
@@ -1449,6 +1449,7 @@extern void do_generic_file_read (struct file *,loff_t *,read_descriptor_t *,read_actor_t );extern loff_t no_llseek (struct file *file,loff_t offset,int origin);extern loff_t generic_file_llseek (struct file *file,loff_t offset,int origin); +extern loff_t remote_llseek (struct file *file,loff_t offset,int origin);extern ssize_t generic_read_dir (struct file *,char *,size_t ,loff_t *);extern int generic_file_open (struct inode * inode,struct file * filp);
kernel/ksyms.c +1 -0
@@ -251,6 +251,7 @@EXPORT_SYMBOL (vfs_statfs);EXPORT_SYMBOL (generic_read_dir);EXPORT_SYMBOL (generic_file_llseek); +EXPORT_SYMBOL (remote_llseek);EXPORT_SYMBOL (no_llseek);EXPORT_SYMBOL (__pollwait);EXPORT_SYMBOL (poll_freewait);
Jan Harkes
maintainerQuoted 3 lines · Robert Love
The 'push locking into llseek methods' patch was integrated into 2.5.3. The networking filesystems, however, do not protect
i_sizeand can not rely on the inode semaphore used ingeneric_file_llseek.
I'm not sure whether the Coda part of this patch is correct. Coda does rely in the inode semaphore to protect from concurrency between the userspace cachemanager that accesses the file on the host filesystem directly and the applications that access the same file through the /coda mount.
See for instance coda_file_write, where we also use the host inode semaphore for protection. Only sys_stat() accesses i_size unprotected, but that doesn't matter much in my opinion. Any application relying on the result of sys_stat to do appending or subsequent lseeks would be racy anyways. (and it can only be fixed correctly when we get a FS specific getattr method).
Jan
So in my opinion, if your patch got applied, the Coda part needs to be reverted.
#patch -R -p1 << EOF
fs/coda/file.c +1 -1
@@ -267,7 +267,7 @@ }struct file_operations coda_file_operations = { - llseek: generic_file_llseek, + llseek: remote_llseek, read: coda_file_read, write: coda_file_write, mmap: coda_file_mmap,
EOF
Quoted 6 lines · Jan Harkes
I'm not sure whether the Coda part of this patch is correct. Coda does rely in the inode semaphore to protect from concurrency between the userspace cachemanager that accesses the file on the host filesystem directly and the applications that access the same file through the /coda mount.
Quoted 6 lines
See for instance
coda_file_write, where we also use the host inode semaphore for protection. Onlysys_stat()accessesi_sizeunprotected, but that doesn't matter much in my opinion. Any application relying on the result ofsys_statto do appending or subsequent lseeks would be racy anyways. (and it can only be fixed correctly when we get a FS specific getattr method).
Hmm ... the race you mention in sys_stat is the problem I saw. I also can't say for sure whether any code, or future code, would touch i_size. It is just not safe.
Note also that reverting to the remote_llseek method won't break anything; it is the previous behavior. Certainly I would much rather just use the inode semaphore, but I'd prefer to not introduce any races. Ideally we need a solution that eliminates the BKL _and_ is not racy.
I'd be happy to keep Coda using the new generic_file_llseek if Al Viro agrees with you. Al?
Robert Love
Quoted 8 lines · Robert Love
Quoted 6 lines · Jan Harkes
I'm not sure whether the Coda part of this patch is correct. Coda does rely in the inode semaphore to protect from concurrency between the userspace cachemanager that accesses the file on the host filesystem directly and the applications that access the same file through the /coda mount.
Quoted 6 lines
Quoted 6 lines
See for instance
coda_file_write, where we also use the host inode semaphore for protection. Onlysys_stat()accessesi_sizeunprotected, but that doesn't matter much in my opinion. Any application relying on the result ofsys_statto do appending or subsequent lseeks would be racy anyways. (and it can only be fixed correctly when we get a FS specific getattr method).
That will happen pretty soon, actually.
Quoted 4 lines
Hmm ... the race you mention in
sys_statis the problem I saw. I also can't say for sure whether any code, or future code, would touchi_size. It is just not safe.
Quoted 8 lines
Note also that reverting to the
remote_llseekmethod won't break anything; it is the previous behavior. Certainly I would much rather just use the inode semaphore, but I'd prefer to not introduce any races. Ideally we need a solution that eliminates the BKL _and_ is not racy.I'd be happy to keep Coda using the new
generic_file_llseekif Al Viro agrees with you. Al?
I'm OK with that.
Quoted 6 lines · Alexander Viro
Quoted 2 lines · Robert Love
I'd be happy to keep Coda using the new
generic_file_llseekif Al Viro agrees with you. Al?I'm OK with that.
OK, updated patch in my ftp as well as attached below.
Linus, this patch supersedes the previous patch #1. 2 and 3 are separate and up for inclusion.
Robert Love
fs/ncpfs/file.c +1 -1
@@ -281,7 +281,7 @@struct file_operations ncp_file_operations = { - llseek: generic_file_llseek, + llseek: remote_llseek, read: ncp_file_read, write: ncp_file_write, ioctl: ncp_ioctl,
fs/nfs/file.c +1 -1
@@ -41,7 +41,7 @@static int nfs_fsync (struct file *,struct dentry *dentry,int datasync);struct file_operations nfs_file_operations = { - llseek: generic_file_llseek, + llseek: remote_llseek, read: nfs_file_read, write: nfs_file_write, mmap: nfs_file_mmap,
fs/read_write.c +25 -0
@@ -51,6 +51,31 @@return retval; } +loff_t remote_llseek (struct file *file,loff_t offset,int origin) +{ +long long retval; + +lock_kernel (); +switch (origin) { +case 2 : + offset += file->f_dentry->d_inode->i_size; +break ; +case 1 : + offset += file->f_pos; + } + retval = -EINVAL ; +if (offset>=0 && offset<=file->f_dentry->d_inode->i_sb->s_maxbytes) { +if (offset != file->f_pos) { + file->f_pos = offset; + file->f_reada =0 ; + file->f_version = ++event; + } + retval = offset; + } +unlock_kernel (); +return retval; +} +loff_t no_llseek (struct file *file,loff_t offset,int origin) {return -ESPIPE ;
fs/smbfs/file.c +1 -1
@@ -381,7 +381,7 @@struct file_operations smb_file_operations = { - llseek: generic_file_llseek, + llseek: remote_llseek, read: smb_file_read, write: smb_file_write, ioctl: smb_ioctl,
include/linux/fs.h +1 -0
@@ -1449,6 +1449,7 @@extern void do_generic_file_read (struct file *,loff_t *,read_descriptor_t *,read_actor_t );extern loff_t no_llseek (struct file *file,loff_t offset,int origin);extern loff_t generic_file_llseek (struct file *file,loff_t offset,int origin); +extern loff_t remote_llseek (struct file *file,loff_t offset,int origin);extern ssize_t generic_read_dir (struct file *,char *,size_t ,loff_t *);extern int generic_file_open (struct inode * inode,struct file * filp);
kernel/ksyms.c +1 -0
@@ -251,6 +251,7 @@EXPORT_SYMBOL (vfs_statfs);EXPORT_SYMBOL (generic_read_dir);EXPORT_SYMBOL (generic_file_llseek); +EXPORT_SYMBOL (remote_llseek);EXPORT_SYMBOL (no_llseek);EXPORT_SYMBOL (__pollwait);EXPORT_SYMBOL (poll_freewait);