diff options
-rw-r--r-- | fs/btrfs/file.c | 2 | ||||
-rw-r--r-- | fs/cifs/cifsfs.c | 2 | ||||
-rw-r--r-- | fs/gfs2/file.c | 4 | ||||
-rw-r--r-- | fs/nfs/file.c | 5 | ||||
-rw-r--r-- | fs/read_write.c | 85 | ||||
-rw-r--r-- | include/linux/fs.h | 9 |
6 files changed, 54 insertions, 53 deletions
diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c index e4e57d59edb7..1266f6e9cdb2 100644 --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c | |||
@@ -1821,7 +1821,7 @@ static loff_t btrfs_file_llseek(struct file *file, loff_t offset, int origin) | |||
1821 | switch (origin) { | 1821 | switch (origin) { |
1822 | case SEEK_END: | 1822 | case SEEK_END: |
1823 | case SEEK_CUR: | 1823 | case SEEK_CUR: |
1824 | offset = generic_file_llseek_unlocked(file, offset, origin); | 1824 | offset = generic_file_llseek(file, offset, origin); |
1825 | goto out; | 1825 | goto out; |
1826 | case SEEK_DATA: | 1826 | case SEEK_DATA: |
1827 | case SEEK_HOLE: | 1827 | case SEEK_HOLE: |
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index 54b8f1e7da94..db7ce87d37a5 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c | |||
@@ -723,7 +723,7 @@ static loff_t cifs_llseek(struct file *file, loff_t offset, int origin) | |||
723 | if (rc < 0) | 723 | if (rc < 0) |
724 | return (loff_t)rc; | 724 | return (loff_t)rc; |
725 | } | 725 | } |
726 | return generic_file_llseek_unlocked(file, offset, origin); | 726 | return generic_file_llseek(file, offset, origin); |
727 | } | 727 | } |
728 | 728 | ||
729 | static int cifs_setlease(struct file *file, long arg, struct file_lock **lease) | 729 | static int cifs_setlease(struct file *file, long arg, struct file_lock **lease) |
diff --git a/fs/gfs2/file.c b/fs/gfs2/file.c index edeb9e802903..fe6bc0207818 100644 --- a/fs/gfs2/file.c +++ b/fs/gfs2/file.c | |||
@@ -63,11 +63,11 @@ static loff_t gfs2_llseek(struct file *file, loff_t offset, int origin) | |||
63 | error = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED, LM_FLAG_ANY, | 63 | error = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED, LM_FLAG_ANY, |
64 | &i_gh); | 64 | &i_gh); |
65 | if (!error) { | 65 | if (!error) { |
66 | error = generic_file_llseek_unlocked(file, offset, origin); | 66 | error = generic_file_llseek(file, offset, origin); |
67 | gfs2_glock_dq_uninit(&i_gh); | 67 | gfs2_glock_dq_uninit(&i_gh); |
68 | } | 68 | } |
69 | } else | 69 | } else |
70 | error = generic_file_llseek_unlocked(file, offset, origin); | 70 | error = generic_file_llseek(file, offset, origin); |
71 | 71 | ||
72 | return error; | 72 | return error; |
73 | } | 73 | } |
diff --git a/fs/nfs/file.c b/fs/nfs/file.c index 28b8c3f3cda3..12623abcf3d4 100644 --- a/fs/nfs/file.c +++ b/fs/nfs/file.c | |||
@@ -198,11 +198,12 @@ static loff_t nfs_file_llseek(struct file *filp, loff_t offset, int origin) | |||
198 | if (retval < 0) | 198 | if (retval < 0) |
199 | return (loff_t)retval; | 199 | return (loff_t)retval; |
200 | 200 | ||
201 | /* AK: should drop this lock. Unlikely to be needed. */ | ||
201 | spin_lock(&inode->i_lock); | 202 | spin_lock(&inode->i_lock); |
202 | loff = generic_file_llseek_unlocked(filp, offset, origin); | 203 | loff = generic_file_llseek(filp, offset, origin); |
203 | spin_unlock(&inode->i_lock); | 204 | spin_unlock(&inode->i_lock); |
204 | } else | 205 | } else |
205 | loff = generic_file_llseek_unlocked(filp, offset, origin); | 206 | loff = generic_file_llseek(filp, offset, origin); |
206 | return loff; | 207 | return loff; |
207 | } | 208 | } |
208 | 209 | ||
diff --git a/fs/read_write.c b/fs/read_write.c index 179f1c33ea57..672b187def62 100644 --- a/fs/read_write.c +++ b/fs/read_write.c | |||
@@ -35,23 +35,45 @@ static inline int unsigned_offsets(struct file *file) | |||
35 | return file->f_mode & FMODE_UNSIGNED_OFFSET; | 35 | return file->f_mode & FMODE_UNSIGNED_OFFSET; |
36 | } | 36 | } |
37 | 37 | ||
38 | static loff_t lseek_execute(struct file *file, struct inode *inode, | ||
39 | loff_t offset, loff_t maxsize) | ||
40 | { | ||
41 | if (offset < 0 && !unsigned_offsets(file)) | ||
42 | return -EINVAL; | ||
43 | if (offset > maxsize) | ||
44 | return -EINVAL; | ||
45 | |||
46 | if (offset != file->f_pos) { | ||
47 | file->f_pos = offset; | ||
48 | file->f_version = 0; | ||
49 | } | ||
50 | return offset; | ||
51 | } | ||
52 | |||
38 | /** | 53 | /** |
39 | * generic_file_llseek_unlocked - lockless generic llseek implementation | 54 | * generic_file_llseek - generic llseek implementation for regular files |
40 | * @file: file structure to seek on | 55 | * @file: file structure to seek on |
41 | * @offset: file offset to seek to | 56 | * @offset: file offset to seek to |
42 | * @origin: type of seek | 57 | * @origin: type of seek |
43 | * | 58 | * |
44 | * Updates the file offset to the value specified by @offset and @origin. | 59 | * This is a generic implemenation of ->llseek usable for all normal local |
45 | * Locking must be provided by the caller. | 60 | * filesystems. It just updates the file offset to the value specified by |
61 | * @offset and @origin under i_mutex. | ||
62 | * | ||
63 | * Synchronization: | ||
64 | * SEEK_SET is unsynchronized (but atomic on 64bit platforms) | ||
65 | * SEEK_CUR is synchronized against other SEEK_CURs, but not read/writes. | ||
66 | * read/writes behave like SEEK_SET against seeks. | ||
67 | * SEEK_END | ||
46 | */ | 68 | */ |
47 | loff_t | 69 | loff_t |
48 | generic_file_llseek_unlocked(struct file *file, loff_t offset, int origin) | 70 | generic_file_llseek(struct file *file, loff_t offset, int origin) |
49 | { | 71 | { |
50 | struct inode *inode = file->f_mapping->host; | 72 | struct inode *inode = file->f_mapping->host; |
51 | 73 | ||
52 | switch (origin) { | 74 | switch (origin) { |
53 | case SEEK_END: | 75 | case SEEK_END: |
54 | offset += inode->i_size; | 76 | offset += i_size_read(inode); |
55 | break; | 77 | break; |
56 | case SEEK_CUR: | 78 | case SEEK_CUR: |
57 | /* | 79 | /* |
@@ -62,14 +84,22 @@ generic_file_llseek_unlocked(struct file *file, loff_t offset, int origin) | |||
62 | */ | 84 | */ |
63 | if (offset == 0) | 85 | if (offset == 0) |
64 | return file->f_pos; | 86 | return file->f_pos; |
65 | offset += file->f_pos; | 87 | /* |
66 | break; | 88 | * f_lock protects against read/modify/write race with other |
89 | * SEEK_CURs. Note that parallel writes and reads behave | ||
90 | * like SEEK_SET. | ||
91 | */ | ||
92 | spin_lock(&file->f_lock); | ||
93 | offset = lseek_execute(file, inode, file->f_pos + offset, | ||
94 | inode->i_sb->s_maxbytes); | ||
95 | spin_unlock(&file->f_lock); | ||
96 | return offset; | ||
67 | case SEEK_DATA: | 97 | case SEEK_DATA: |
68 | /* | 98 | /* |
69 | * In the generic case the entire file is data, so as long as | 99 | * In the generic case the entire file is data, so as long as |
70 | * offset isn't at the end of the file then the offset is data. | 100 | * offset isn't at the end of the file then the offset is data. |
71 | */ | 101 | */ |
72 | if (offset >= inode->i_size) | 102 | if (offset >= i_size_read(inode)) |
73 | return -ENXIO; | 103 | return -ENXIO; |
74 | break; | 104 | break; |
75 | case SEEK_HOLE: | 105 | case SEEK_HOLE: |
@@ -77,46 +107,13 @@ generic_file_llseek_unlocked(struct file *file, loff_t offset, int origin) | |||
77 | * There is a virtual hole at the end of the file, so as long as | 107 | * There is a virtual hole at the end of the file, so as long as |
78 | * offset isn't i_size or larger, return i_size. | 108 | * offset isn't i_size or larger, return i_size. |
79 | */ | 109 | */ |
80 | if (offset >= inode->i_size) | 110 | if (offset >= i_size_read(inode)) |
81 | return -ENXIO; | 111 | return -ENXIO; |
82 | offset = inode->i_size; | 112 | offset = i_size_read(inode); |
83 | break; | 113 | break; |
84 | } | 114 | } |
85 | 115 | ||
86 | if (offset < 0 && !unsigned_offsets(file)) | 116 | return lseek_execute(file, inode, offset, inode->i_sb->s_maxbytes); |
87 | return -EINVAL; | ||
88 | if (offset > inode->i_sb->s_maxbytes) | ||
89 | return -EINVAL; | ||
90 | |||
91 | /* Special lock needed here? */ | ||
92 | if (offset != file->f_pos) { | ||
93 | file->f_pos = offset; | ||
94 | file->f_version = 0; | ||
95 | } | ||
96 | |||
97 | return offset; | ||
98 | } | ||
99 | EXPORT_SYMBOL(generic_file_llseek_unlocked); | ||
100 | |||
101 | /** | ||
102 | * generic_file_llseek - generic llseek implementation for regular files | ||
103 | * @file: file structure to seek on | ||
104 | * @offset: file offset to seek to | ||
105 | * @origin: type of seek | ||
106 | * | ||
107 | * This is a generic implemenation of ->llseek useable for all normal local | ||
108 | * filesystems. It just updates the file offset to the value specified by | ||
109 | * @offset and @origin under i_mutex. | ||
110 | */ | ||
111 | loff_t generic_file_llseek(struct file *file, loff_t offset, int origin) | ||
112 | { | ||
113 | loff_t rval; | ||
114 | |||
115 | mutex_lock(&file->f_dentry->d_inode->i_mutex); | ||
116 | rval = generic_file_llseek_unlocked(file, offset, origin); | ||
117 | mutex_unlock(&file->f_dentry->d_inode->i_mutex); | ||
118 | |||
119 | return rval; | ||
120 | } | 117 | } |
121 | EXPORT_SYMBOL(generic_file_llseek); | 118 | EXPORT_SYMBOL(generic_file_llseek); |
122 | 119 | ||
diff --git a/include/linux/fs.h b/include/linux/fs.h index c1884e974ff4..db85196f6308 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h | |||
@@ -964,7 +964,12 @@ struct file { | |||
964 | #define f_dentry f_path.dentry | 964 | #define f_dentry f_path.dentry |
965 | #define f_vfsmnt f_path.mnt | 965 | #define f_vfsmnt f_path.mnt |
966 | const struct file_operations *f_op; | 966 | const struct file_operations *f_op; |
967 | spinlock_t f_lock; /* f_ep_links, f_flags, no IRQ */ | 967 | |
968 | /* | ||
969 | * Protects f_ep_links, f_flags, f_pos vs i_size in lseek SEEK_CUR. | ||
970 | * Must not be taken from IRQ context. | ||
971 | */ | ||
972 | spinlock_t f_lock; | ||
968 | #ifdef CONFIG_SMP | 973 | #ifdef CONFIG_SMP |
969 | int f_sb_list_cpu; | 974 | int f_sb_list_cpu; |
970 | #endif | 975 | #endif |
@@ -2398,8 +2403,6 @@ file_ra_state_init(struct file_ra_state *ra, struct address_space *mapping); | |||
2398 | extern loff_t noop_llseek(struct file *file, loff_t offset, int origin); | 2403 | extern loff_t noop_llseek(struct file *file, loff_t offset, int origin); |
2399 | extern loff_t no_llseek(struct file *file, loff_t offset, int origin); | 2404 | extern loff_t no_llseek(struct file *file, loff_t offset, int origin); |
2400 | extern loff_t generic_file_llseek(struct file *file, loff_t offset, int origin); | 2405 | extern loff_t generic_file_llseek(struct file *file, loff_t offset, int origin); |
2401 | extern loff_t generic_file_llseek_unlocked(struct file *file, loff_t offset, | ||
2402 | int origin); | ||
2403 | extern int generic_file_open(struct inode * inode, struct file * filp); | 2406 | extern int generic_file_open(struct inode * inode, struct file * filp); |
2404 | extern int nonseekable_open(struct inode * inode, struct file * filp); | 2407 | extern int nonseekable_open(struct inode * inode, struct file * filp); |
2405 | 2408 | ||