aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorTejun Heo <tj@kernel.org>2013-11-28 14:54:16 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-11-29 20:33:46 -0500
commitc2b19daf6760fae9d5db9e9d1683644728888293 (patch)
tree28baf98d636e4a111790e5a0f52b1f49709d86b3 /fs
parent93b2b8e4aa4317e3fe6414d117deb5f3c362e8bb (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.c191
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 */
89static int sysfs_seq_show(struct seq_file *sf, void *v) 89static 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/* 128static 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 */
153static 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
151static 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
170static 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
180static 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
188static 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
197static 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 */
210static 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 */
259static 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
699err_close: 760err_close:
700 single_release(inode, file); 761 seq_release(inode, file);
701err_free: 762err_free:
702 kfree(of); 763 kfree(of);
703err_out: 764err_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)
816EXPORT_SYMBOL_GPL(sysfs_notify); 877EXPORT_SYMBOL_GPL(sysfs_notify);
817 878
818const struct file_operations sysfs_file_operations = { 879const 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
827const struct file_operations sysfs_bin_operations = { 888const 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,