aboutsummaryrefslogtreecommitdiffstats
path: root/fs/sysfs/file.c
diff options
context:
space:
mode:
authorNeilBrown <neilb@suse.de>2014-10-13 01:41:28 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2014-11-07 13:53:25 -0500
commit2b75869bba676c248d8d25ae6d2bd9221dfffdb6 (patch)
treebcbf58a3e4da882e9cc4dda7da4b65f18a01fa81 /fs/sysfs/file.c
parent0936896056365349afa867c16e9f9100a6707cbf (diff)
sysfs/kernfs: allow attributes to request write buffer be pre-allocated.
md/raid allows metadata management to be performed in user-space. A various times, particularly on device failure, the metadata needs to be updated before further writes can be permitted. This means that the user-space program which updates metadata much not block on writeout, and so must not allocate memory. mlockall(MCL_CURRENT|MCL_FUTURE) and pre-allocation can avoid all memory allocation issues for user-memory, but that does not help kernel memory. Several kernel objects can be pre-allocated. e.g. files opened before any writes to the array are permitted. However some kernel allocation happens in places that cannot be pre-allocated. In particular, writes to sysfs files (to tell md that it can now allow writes to the array) allocate a buffer using GFP_KERNEL. This patch allows attributes to be marked as "PREALLOC". In that case the maximal buffer is allocated when the file is opened, and then used on each write instead of allocating a new buffer. As the same buffer is now shared for all writes on the same file description, the mutex is extended to cover full use of the buffer including the copy_from_user(). The new __ATTR_PREALLOC() 'or's a new flag in to the 'mode', which is inspected by sysfs_add_file_mode_ns() to determine if the file should be marked as requiring prealloc. Despite the comment, we *do* use ->seq_show together with ->prealloc in this patch. The next patch fixes that. Signed-off-by: NeilBrown <neilb@suse.de> Reviewed-by: Tejun Heo <tj@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'fs/sysfs/file.c')
-rw-r--r--fs/sysfs/file.c31
1 files changed, 24 insertions, 7 deletions
diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c
index 589abee16a39..4ad3721a991c 100644
--- a/fs/sysfs/file.c
+++ b/fs/sysfs/file.c
@@ -184,6 +184,17 @@ static const struct kernfs_ops sysfs_file_kfops_rw = {
184 .write = sysfs_kf_write, 184 .write = sysfs_kf_write,
185}; 185};
186 186
187static const struct kernfs_ops sysfs_prealloc_kfops_wo = {
188 .write = sysfs_kf_write,
189 .prealloc = true,
190};
191
192static const struct kernfs_ops sysfs_prealloc_kfops_rw = {
193 .seq_show = sysfs_kf_seq_show,
194 .write = sysfs_kf_write,
195 .prealloc = true,
196};
197
187static const struct kernfs_ops sysfs_bin_kfops_ro = { 198static const struct kernfs_ops sysfs_bin_kfops_ro = {
188 .read = sysfs_kf_bin_read, 199 .read = sysfs_kf_bin_read,
189}; 200};
@@ -222,13 +233,19 @@ int sysfs_add_file_mode_ns(struct kernfs_node *parent,
222 kobject_name(kobj))) 233 kobject_name(kobj)))
223 return -EINVAL; 234 return -EINVAL;
224 235
225 if (sysfs_ops->show && sysfs_ops->store) 236 if (sysfs_ops->show && sysfs_ops->store) {
226 ops = &sysfs_file_kfops_rw; 237 if (mode & SYSFS_PREALLOC)
227 else if (sysfs_ops->show) 238 ops = &sysfs_prealloc_kfops_rw;
239 else
240 ops = &sysfs_file_kfops_rw;
241 } else if (sysfs_ops->show)
228 ops = &sysfs_file_kfops_ro; 242 ops = &sysfs_file_kfops_ro;
229 else if (sysfs_ops->store) 243 else if (sysfs_ops->store) {
230 ops = &sysfs_file_kfops_wo; 244 if (mode & SYSFS_PREALLOC)
231 else 245 ops = &sysfs_prealloc_kfops_wo;
246 else
247 ops = &sysfs_file_kfops_wo;
248 } else
232 ops = &sysfs_file_kfops_empty; 249 ops = &sysfs_file_kfops_empty;
233 250
234 size = PAGE_SIZE; 251 size = PAGE_SIZE;
@@ -253,7 +270,7 @@ int sysfs_add_file_mode_ns(struct kernfs_node *parent,
253 if (!attr->ignore_lockdep) 270 if (!attr->ignore_lockdep)
254 key = attr->key ?: (struct lock_class_key *)&attr->skey; 271 key = attr->key ?: (struct lock_class_key *)&attr->skey;
255#endif 272#endif
256 kn = __kernfs_create_file(parent, attr->name, mode, size, ops, 273 kn = __kernfs_create_file(parent, attr->name, mode & 0777, size, ops,
257 (void *)attr, ns, true, key); 274 (void *)attr, ns, true, key);
258 if (IS_ERR(kn)) { 275 if (IS_ERR(kn)) {
259 if (PTR_ERR(kn) == -EEXIST) 276 if (PTR_ERR(kn) == -EEXIST)