diff options
author | Tejun Heo <tj@kernel.org> | 2013-11-28 14:54:16 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2013-11-29 20:33:46 -0500 |
commit | c2b19daf6760fae9d5db9e9d1683644728888293 (patch) | |
tree | 28baf98d636e4a111790e5a0f52b1f49709d86b3 /fs | |
parent | 93b2b8e4aa4317e3fe6414d117deb5f3c362e8bb (diff) |
sysfs, kernfs: prepare read path for kernfs
We're in the process of separating out core sysfs functionality into
kernfs which will deal with sysfs_dirents directly. This patch
rearranges read path so that the kernfs and sysfs parts are separate.
* Regular file read path is refactored such that
kernfs_seq_start/next/stop/show() handle all the boilerplate work
including locking and updating event count for poll, while
sysfs_kf_seq_show() deals with interaction with kobj show method.
* Bin file read path is refactored such that kernfs_file_direct_read()
handles all the boilerplate work including buffer management and
locking, while sysfs_kf_bin_read() deals with interaction with
bin_attribute read method.
kernfs_file_read() is added. It invokes either the seq_file or direct
read path depending on the file type. This will eventually allow
using the same file_operations for both file types, which is necessary
to separate out kernfs.
While this patch changes the order of some operations, it shouldn't
change any visible behavior.
v2: Dropped unnecessary zeroing of @count from sysfs_kf_seq_show().
Add comments explaining single_open() behavior. Both suggested by
Pavel.
v3: seq_stop() is called even after seq_start() failed.
kernfs_seq_start() updated so that it doesn't unlock
sysfs_open_file->mutex on failure so that kernfs_seq_stop()
doesn't try to unlock an already unlocked mutex. Reported by
Fengguang.
Signed-off-by: Tejun Heo <tj@kernel.org>
Cc: Pavel Machek <pavel@ucw.cz>
Cc: Fengguang Wu <fengguang.wu@intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/sysfs/file.c | 191 |
1 files changed, 126 insertions, 65 deletions
diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index 9b58d874c825..b695b8b229fc 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c | |||
@@ -86,13 +86,13 @@ static const struct sysfs_ops *sysfs_file_ops(struct sysfs_dirent *sd) | |||
86 | * details like buffering and seeking. The following function pipes | 86 | * details like buffering and seeking. The following function pipes |
87 | * sysfs_ops->show() result through seq_file. | 87 | * sysfs_ops->show() result through seq_file. |
88 | */ | 88 | */ |
89 | static int sysfs_seq_show(struct seq_file *sf, void *v) | 89 | static int sysfs_kf_seq_show(struct seq_file *sf, void *v) |
90 | { | 90 | { |
91 | struct sysfs_open_file *of = sf->private; | 91 | struct sysfs_open_file *of = sf->private; |
92 | struct kobject *kobj = of->sd->s_parent->priv; | 92 | struct kobject *kobj = of->sd->s_parent->priv; |
93 | const struct sysfs_ops *ops; | 93 | const struct sysfs_ops *ops = sysfs_file_ops(of->sd); |
94 | char *buf; | ||
95 | ssize_t count; | 94 | ssize_t count; |
95 | char *buf; | ||
96 | 96 | ||
97 | /* acquire buffer and ensure that it's >= PAGE_SIZE */ | 97 | /* acquire buffer and ensure that it's >= PAGE_SIZE */ |
98 | count = seq_get_buf(sf, &buf); | 98 | count = seq_get_buf(sf, &buf); |
@@ -102,33 +102,14 @@ static int sysfs_seq_show(struct seq_file *sf, void *v) | |||
102 | } | 102 | } |
103 | 103 | ||
104 | /* | 104 | /* |
105 | * Need @of->sd for attr and ops, its parent for kobj. @of->mutex | 105 | * Invoke show(). Control may reach here via seq file lseek even |
106 | * nests outside active ref and is just to ensure that the ops | 106 | * if @ops->show() isn't implemented. |
107 | * aren't called concurrently for the same open file. | ||
108 | */ | 107 | */ |
109 | mutex_lock(&of->mutex); | 108 | if (ops->show) { |
110 | if (!sysfs_get_active(of->sd)) { | ||
111 | mutex_unlock(&of->mutex); | ||
112 | return -ENODEV; | ||
113 | } | ||
114 | |||
115 | of->event = atomic_read(&of->sd->s_attr.open->event); | ||
116 | |||
117 | /* | ||
118 | * Lookup @ops and invoke show(). Control may reach here via seq | ||
119 | * file lseek even if @ops->show() isn't implemented. | ||
120 | */ | ||
121 | ops = sysfs_file_ops(of->sd); | ||
122 | if (ops->show) | ||
123 | count = ops->show(kobj, of->sd->priv, buf); | 109 | count = ops->show(kobj, of->sd->priv, buf); |
124 | else | 110 | if (count < 0) |
125 | count = 0; | 111 | return count; |
126 | 112 | } | |
127 | sysfs_put_active(of->sd); | ||
128 | mutex_unlock(&of->mutex); | ||
129 | |||
130 | if (count < 0) | ||
131 | return count; | ||
132 | 113 | ||
133 | /* | 114 | /* |
134 | * The code works fine with PAGE_SIZE return but it's likely to | 115 | * The code works fine with PAGE_SIZE return but it's likely to |
@@ -144,68 +125,146 @@ static int sysfs_seq_show(struct seq_file *sf, void *v) | |||
144 | return 0; | 125 | return 0; |
145 | } | 126 | } |
146 | 127 | ||
147 | /* | 128 | static ssize_t sysfs_kf_bin_read(struct sysfs_open_file *of, char *buf, |
148 | * Read method for bin files. As reading a bin file can have side-effects, | 129 | size_t count, loff_t pos) |
149 | * the exact offset and bytes specified in read(2) call should be passed to | ||
150 | * the read callback making it difficult to use seq_file. Implement | ||
151 | * simplistic custom buffering for bin files. | ||
152 | */ | ||
153 | static ssize_t sysfs_bin_read(struct file *file, char __user *userbuf, | ||
154 | size_t bytes, loff_t *off) | ||
155 | { | 130 | { |
156 | struct sysfs_open_file *of = sysfs_of(file); | ||
157 | struct bin_attribute *battr = of->sd->priv; | 131 | struct bin_attribute *battr = of->sd->priv; |
158 | struct kobject *kobj = of->sd->s_parent->priv; | 132 | struct kobject *kobj = of->sd->s_parent->priv; |
159 | loff_t size = file_inode(file)->i_size; | 133 | loff_t size = file_inode(of->file)->i_size; |
160 | int count = min_t(size_t, bytes, PAGE_SIZE); | ||
161 | loff_t offs = *off; | ||
162 | char *buf; | ||
163 | 134 | ||
164 | if (!bytes) | 135 | if (!count) |
165 | return 0; | 136 | return 0; |
166 | 137 | ||
167 | if (size) { | 138 | if (size) { |
168 | if (offs > size) | 139 | if (pos > size) |
169 | return 0; | 140 | return 0; |
170 | if (offs + count > size) | 141 | if (pos + count > size) |
171 | count = size - offs; | 142 | count = size - pos; |
172 | } | 143 | } |
173 | 144 | ||
174 | buf = kmalloc(count, GFP_KERNEL); | 145 | if (!battr->read) |
146 | return -EIO; | ||
147 | |||
148 | return battr->read(of->file, kobj, battr, buf, pos, count); | ||
149 | } | ||
150 | |||
151 | static void *kernfs_seq_start(struct seq_file *sf, loff_t *ppos) | ||
152 | { | ||
153 | struct sysfs_open_file *of = sf->private; | ||
154 | |||
155 | /* | ||
156 | * @of->mutex nests outside active ref and is just to ensure that | ||
157 | * the ops aren't called concurrently for the same open file. | ||
158 | */ | ||
159 | mutex_lock(&of->mutex); | ||
160 | if (!sysfs_get_active(of->sd)) | ||
161 | return ERR_PTR(-ENODEV); | ||
162 | |||
163 | /* | ||
164 | * The same behavior and code as single_open(). Returns !NULL if | ||
165 | * pos is at the beginning; otherwise, NULL. | ||
166 | */ | ||
167 | return NULL + !*ppos; | ||
168 | } | ||
169 | |||
170 | static void *kernfs_seq_next(struct seq_file *sf, void *v, loff_t *ppos) | ||
171 | { | ||
172 | /* | ||
173 | * The same behavior and code as single_open(), always terminate | ||
174 | * after the initial read. | ||
175 | */ | ||
176 | ++*ppos; | ||
177 | return NULL; | ||
178 | } | ||
179 | |||
180 | static void kernfs_seq_stop(struct seq_file *sf, void *v) | ||
181 | { | ||
182 | struct sysfs_open_file *of = sf->private; | ||
183 | |||
184 | sysfs_put_active(of->sd); | ||
185 | mutex_unlock(&of->mutex); | ||
186 | } | ||
187 | |||
188 | static int kernfs_seq_show(struct seq_file *sf, void *v) | ||
189 | { | ||
190 | struct sysfs_open_file *of = sf->private; | ||
191 | |||
192 | of->event = atomic_read(&of->sd->s_attr.open->event); | ||
193 | |||
194 | return sysfs_kf_seq_show(sf, v); | ||
195 | } | ||
196 | |||
197 | static const struct seq_operations kernfs_seq_ops = { | ||
198 | .start = kernfs_seq_start, | ||
199 | .next = kernfs_seq_next, | ||
200 | .stop = kernfs_seq_stop, | ||
201 | .show = kernfs_seq_show, | ||
202 | }; | ||
203 | |||
204 | /* | ||
205 | * As reading a bin file can have side-effects, the exact offset and bytes | ||
206 | * specified in read(2) call should be passed to the read callback making | ||
207 | * it difficult to use seq_file. Implement simplistic custom buffering for | ||
208 | * bin files. | ||
209 | */ | ||
210 | static ssize_t kernfs_file_direct_read(struct sysfs_open_file *of, | ||
211 | char __user *user_buf, size_t count, | ||
212 | loff_t *ppos) | ||
213 | { | ||
214 | ssize_t len = min_t(size_t, count, PAGE_SIZE); | ||
215 | char *buf; | ||
216 | |||
217 | buf = kmalloc(len, GFP_KERNEL); | ||
175 | if (!buf) | 218 | if (!buf) |
176 | return -ENOMEM; | 219 | return -ENOMEM; |
177 | 220 | ||
178 | /* need of->sd for battr, its parent for kobj */ | 221 | /* |
222 | * @of->mutex nests outside active ref and is just to ensure that | ||
223 | * the ops aren't called concurrently for the same open file. | ||
224 | */ | ||
179 | mutex_lock(&of->mutex); | 225 | mutex_lock(&of->mutex); |
180 | if (!sysfs_get_active(of->sd)) { | 226 | if (!sysfs_get_active(of->sd)) { |
181 | count = -ENODEV; | 227 | len = -ENODEV; |
182 | mutex_unlock(&of->mutex); | 228 | mutex_unlock(&of->mutex); |
183 | goto out_free; | 229 | goto out_free; |
184 | } | 230 | } |
185 | 231 | ||
186 | if (battr->read) | 232 | len = sysfs_kf_bin_read(of, buf, len, *ppos); |
187 | count = battr->read(file, kobj, battr, buf, offs, count); | ||
188 | else | ||
189 | count = -EIO; | ||
190 | 233 | ||
191 | sysfs_put_active(of->sd); | 234 | sysfs_put_active(of->sd); |
192 | mutex_unlock(&of->mutex); | 235 | mutex_unlock(&of->mutex); |
193 | 236 | ||
194 | if (count < 0) | 237 | if (len < 0) |
195 | goto out_free; | 238 | goto out_free; |
196 | 239 | ||
197 | if (copy_to_user(userbuf, buf, count)) { | 240 | if (copy_to_user(user_buf, buf, len)) { |
198 | count = -EFAULT; | 241 | len = -EFAULT; |
199 | goto out_free; | 242 | goto out_free; |
200 | } | 243 | } |
201 | 244 | ||
202 | pr_debug("offs = %lld, *off = %lld, count = %d\n", offs, *off, count); | 245 | *ppos += len; |
203 | |||
204 | *off = offs + count; | ||
205 | 246 | ||
206 | out_free: | 247 | out_free: |
207 | kfree(buf); | 248 | kfree(buf); |
208 | return count; | 249 | return len; |
250 | } | ||
251 | |||
252 | /** | ||
253 | * kernfs_file_read - kernfs vfs read callback | ||
254 | * @file: file pointer | ||
255 | * @user_buf: data to write | ||
256 | * @count: number of bytes | ||
257 | * @ppos: starting offset | ||
258 | */ | ||
259 | static ssize_t kernfs_file_read(struct file *file, char __user *user_buf, | ||
260 | size_t count, loff_t *ppos) | ||
261 | { | ||
262 | struct sysfs_open_file *of = sysfs_of(file); | ||
263 | |||
264 | if (sysfs_is_bin(of->sd)) | ||
265 | return kernfs_file_direct_read(of, user_buf, count, ppos); | ||
266 | else | ||
267 | return seq_read(file, user_buf, count, ppos); | ||
209 | } | 268 | } |
210 | 269 | ||
211 | /** | 270 | /** |
@@ -677,12 +736,14 @@ static int sysfs_open_file(struct inode *inode, struct file *file) | |||
677 | * and readable regular files are the vast majority anyway. | 736 | * and readable regular files are the vast majority anyway. |
678 | */ | 737 | */ |
679 | if (sysfs_is_bin(attr_sd)) | 738 | if (sysfs_is_bin(attr_sd)) |
680 | error = single_open(file, NULL, of); | 739 | error = seq_open(file, NULL); |
681 | else | 740 | else |
682 | error = single_open(file, sysfs_seq_show, of); | 741 | error = seq_open(file, &kernfs_seq_ops); |
683 | if (error) | 742 | if (error) |
684 | goto err_free; | 743 | goto err_free; |
685 | 744 | ||
745 | ((struct seq_file *)file->private_data)->private = of; | ||
746 | |||
686 | /* seq_file clears PWRITE unconditionally, restore it if WRITE */ | 747 | /* seq_file clears PWRITE unconditionally, restore it if WRITE */ |
687 | if (file->f_mode & FMODE_WRITE) | 748 | if (file->f_mode & FMODE_WRITE) |
688 | file->f_mode |= FMODE_PWRITE; | 749 | file->f_mode |= FMODE_PWRITE; |
@@ -697,7 +758,7 @@ static int sysfs_open_file(struct inode *inode, struct file *file) | |||
697 | return 0; | 758 | return 0; |
698 | 759 | ||
699 | err_close: | 760 | err_close: |
700 | single_release(inode, file); | 761 | seq_release(inode, file); |
701 | err_free: | 762 | err_free: |
702 | kfree(of); | 763 | kfree(of); |
703 | err_out: | 764 | err_out: |
@@ -711,7 +772,7 @@ static int sysfs_release(struct inode *inode, struct file *filp) | |||
711 | struct sysfs_open_file *of = sysfs_of(filp); | 772 | struct sysfs_open_file *of = sysfs_of(filp); |
712 | 773 | ||
713 | sysfs_put_open_dirent(sd, of); | 774 | sysfs_put_open_dirent(sd, of); |
714 | single_release(inode, filp); | 775 | seq_release(inode, filp); |
715 | kfree(of); | 776 | kfree(of); |
716 | 777 | ||
717 | return 0; | 778 | return 0; |
@@ -816,7 +877,7 @@ void sysfs_notify(struct kobject *k, const char *dir, const char *attr) | |||
816 | EXPORT_SYMBOL_GPL(sysfs_notify); | 877 | EXPORT_SYMBOL_GPL(sysfs_notify); |
817 | 878 | ||
818 | const struct file_operations sysfs_file_operations = { | 879 | const struct file_operations sysfs_file_operations = { |
819 | .read = seq_read, | 880 | .read = kernfs_file_read, |
820 | .write = sysfs_write_file, | 881 | .write = sysfs_write_file, |
821 | .llseek = generic_file_llseek, | 882 | .llseek = generic_file_llseek, |
822 | .open = sysfs_open_file, | 883 | .open = sysfs_open_file, |
@@ -825,7 +886,7 @@ const struct file_operations sysfs_file_operations = { | |||
825 | }; | 886 | }; |
826 | 887 | ||
827 | const struct file_operations sysfs_bin_operations = { | 888 | const struct file_operations sysfs_bin_operations = { |
828 | .read = sysfs_bin_read, | 889 | .read = kernfs_file_read, |
829 | .write = sysfs_write_file, | 890 | .write = sysfs_write_file, |
830 | .llseek = generic_file_llseek, | 891 | .llseek = generic_file_llseek, |
831 | .mmap = sysfs_bin_mmap, | 892 | .mmap = sysfs_bin_mmap, |