Commit | Line | Data |
---|---|---|
2874c5fd | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
31143d5d DH |
2 | /* handling of writes to regular files and writing back to the server |
3 | * | |
4 | * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. | |
5 | * Written by David Howells (dhowells@redhat.com) | |
31143d5d | 6 | */ |
4343d008 | 7 | |
4af3c9cc | 8 | #include <linux/backing-dev.h> |
31143d5d DH |
9 | #include <linux/slab.h> |
10 | #include <linux/fs.h> | |
11 | #include <linux/pagemap.h> | |
12 | #include <linux/writeback.h> | |
13 | #include <linux/pagevec.h> | |
3003bbd0 | 14 | #include <linux/netfs.h> |
3560358a | 15 | #include <trace/events/netfs.h> |
31143d5d DH |
16 | #include "internal.h" |
17 | ||
a58823ac DH |
18 | /* |
19 | * completion of write to server | |
20 | */ | |
e87b03f5 | 21 | static void afs_pages_written_back(struct afs_vnode *vnode, loff_t start, unsigned int len) |
a58823ac | 22 | { |
e87b03f5 DH |
23 | _enter("{%llx:%llu},{%x @%llx}", |
24 | vnode->fid.vid, vnode->fid.vnode, len, start); | |
a58823ac | 25 | |
a58823ac DH |
26 | afs_prune_wb_keys(vnode); |
27 | _leave(""); | |
28 | } | |
29 | ||
d2ddc776 | 30 | /* |
e49c7b2f DH |
31 | * Find a key to use for the writeback. We cached the keys used to author the |
32 | * writes on the vnode. *_wbk will contain the last writeback key used or NULL | |
33 | * and we need to start from there if it's set. | |
d2ddc776 | 34 | */ |
e49c7b2f DH |
35 | static int afs_get_writeback_key(struct afs_vnode *vnode, |
36 | struct afs_wb_key **_wbk) | |
d2ddc776 | 37 | { |
4343d008 DH |
38 | struct afs_wb_key *wbk = NULL; |
39 | struct list_head *p; | |
40 | int ret = -ENOKEY, ret2; | |
d2ddc776 | 41 | |
4343d008 | 42 | spin_lock(&vnode->wb_lock); |
e49c7b2f DH |
43 | if (*_wbk) |
44 | p = (*_wbk)->vnode_link.next; | |
45 | else | |
46 | p = vnode->wb_keys.next; | |
4343d008 | 47 | |
4343d008 DH |
48 | while (p != &vnode->wb_keys) { |
49 | wbk = list_entry(p, struct afs_wb_key, vnode_link); | |
50 | _debug("wbk %u", key_serial(wbk->key)); | |
51 | ret2 = key_validate(wbk->key); | |
e49c7b2f DH |
52 | if (ret2 == 0) { |
53 | refcount_inc(&wbk->usage); | |
54 | _debug("USE WB KEY %u", key_serial(wbk->key)); | |
55 | break; | |
56 | } | |
57 | ||
58 | wbk = NULL; | |
4343d008 DH |
59 | if (ret == -ENOKEY) |
60 | ret = ret2; | |
61 | p = p->next; | |
62 | } | |
63 | ||
64 | spin_unlock(&vnode->wb_lock); | |
e49c7b2f DH |
65 | if (*_wbk) |
66 | afs_put_wb_key(*_wbk); | |
67 | *_wbk = wbk; | |
68 | return 0; | |
69 | } | |
4343d008 | 70 | |
e49c7b2f DH |
71 | static void afs_store_data_success(struct afs_operation *op) |
72 | { | |
73 | struct afs_vnode *vnode = op->file[0].vnode; | |
4343d008 | 74 | |
da8d0755 | 75 | op->ctime = op->file[0].scb.status.mtime_client; |
e49c7b2f | 76 | afs_vnode_commit_status(op, &op->file[0]); |
2de5599f | 77 | if (!afs_op_error(op)) { |
d73065e6 | 78 | afs_pages_written_back(vnode, op->store.pos, op->store.size); |
e49c7b2f | 79 | afs_stat_v(vnode, n_stores); |
bd80d8a8 | 80 | atomic_long_add(op->store.size, &afs_v2net(vnode)->n_store_bytes); |
e49c7b2f DH |
81 | } |
82 | } | |
4343d008 | 83 | |
e49c7b2f DH |
84 | static const struct afs_operation_ops afs_store_data_operation = { |
85 | .issue_afs_rpc = afs_fs_store_data, | |
86 | .issue_yfs_rpc = yfs_fs_store_data, | |
87 | .success = afs_store_data_success, | |
88 | }; | |
a58823ac | 89 | |
e49c7b2f DH |
90 | /* |
91 | * write to a file | |
92 | */ | |
d73065e6 | 93 | static int afs_store_data(struct afs_vnode *vnode, struct iov_iter *iter, loff_t pos) |
e49c7b2f | 94 | { |
e49c7b2f DH |
95 | struct afs_operation *op; |
96 | struct afs_wb_key *wbk = NULL; | |
ab487a4c | 97 | loff_t size = iov_iter_count(iter); |
bd80d8a8 | 98 | int ret = -ENOKEY; |
e49c7b2f | 99 | |
bd80d8a8 | 100 | _enter("%s{%llx:%llu.%u},%llx,%llx", |
e49c7b2f DH |
101 | vnode->volume->name, |
102 | vnode->fid.vid, | |
103 | vnode->fid.vnode, | |
104 | vnode->fid.unique, | |
bd80d8a8 | 105 | size, pos); |
d2ddc776 | 106 | |
e49c7b2f DH |
107 | ret = afs_get_writeback_key(vnode, &wbk); |
108 | if (ret) { | |
109 | _leave(" = %d [no keys]", ret); | |
110 | return ret; | |
d2ddc776 DH |
111 | } |
112 | ||
e49c7b2f DH |
113 | op = afs_alloc_operation(wbk->key, vnode->volume); |
114 | if (IS_ERR(op)) { | |
115 | afs_put_wb_key(wbk); | |
116 | return -ENOMEM; | |
117 | } | |
118 | ||
119 | afs_op_set_vnode(op, 0, vnode); | |
120 | op->file[0].dv_delta = 1; | |
22650f14 | 121 | op->file[0].modification = true; |
bd80d8a8 | 122 | op->store.pos = pos; |
bd80d8a8 | 123 | op->store.size = size; |
811f04ba | 124 | op->flags |= AFS_OPERATION_UNINTR; |
e49c7b2f DH |
125 | op->ops = &afs_store_data_operation; |
126 | ||
127 | try_next_key: | |
128 | afs_begin_vnode_operation(op); | |
03275585 DH |
129 | |
130 | op->store.write_iter = iter; | |
131 | op->store.i_size = max(pos + size, vnode->netfs.remote_i_size); | |
562ce1f7 | 132 | op->mtime = inode_get_mtime(&vnode->netfs.inode); |
03275585 | 133 | |
e49c7b2f DH |
134 | afs_wait_for_operation(op); |
135 | ||
2de5599f | 136 | switch (afs_op_error(op)) { |
4343d008 DH |
137 | case -EACCES: |
138 | case -EPERM: | |
139 | case -ENOKEY: | |
140 | case -EKEYEXPIRED: | |
141 | case -EKEYREJECTED: | |
142 | case -EKEYREVOKED: | |
143 | _debug("next"); | |
e49c7b2f DH |
144 | |
145 | ret = afs_get_writeback_key(vnode, &wbk); | |
146 | if (ret == 0) { | |
147 | key_put(op->key); | |
148 | op->key = key_get(wbk->key); | |
149 | goto try_next_key; | |
150 | } | |
151 | break; | |
4343d008 DH |
152 | } |
153 | ||
154 | afs_put_wb_key(wbk); | |
2de5599f | 155 | _leave(" = %d", afs_op_error(op)); |
e49c7b2f | 156 | return afs_put_operation(op); |
d2ddc776 DH |
157 | } |
158 | ||
3560358a | 159 | static void afs_upload_to_server(struct netfs_io_subrequest *subreq) |
31143d5d | 160 | { |
3560358a DH |
161 | struct afs_vnode *vnode = AFS_FS_I(subreq->rreq->inode); |
162 | ssize_t ret; | |
31143d5d | 163 | |
3560358a DH |
164 | _enter("%x[%x],%zx", |
165 | subreq->rreq->debug_id, subreq->debug_index, subreq->io_iter.count); | |
31143d5d | 166 | |
3560358a | 167 | trace_netfs_sreq(subreq, netfs_sreq_trace_submit); |
d73065e6 | 168 | ret = afs_store_data(vnode, &subreq->io_iter, subreq->start); |
3560358a DH |
169 | netfs_write_subrequest_terminated(subreq, ret < 0 ? ret : subreq->len, |
170 | false); | |
810caa3e DH |
171 | } |
172 | ||
3560358a | 173 | static void afs_upload_to_server_worker(struct work_struct *work) |
810caa3e | 174 | { |
3560358a DH |
175 | struct netfs_io_subrequest *subreq = |
176 | container_of(work, struct netfs_io_subrequest, work); | |
4343d008 | 177 | |
3560358a | 178 | afs_upload_to_server(subreq); |
31143d5d DH |
179 | } |
180 | ||
31143d5d | 181 | /* |
3560358a DH |
182 | * Set up write requests for a writeback slice. We need to add a write request |
183 | * for each write we want to make. | |
31143d5d | 184 | */ |
3560358a | 185 | void afs_create_write_requests(struct netfs_io_request *wreq, loff_t start, size_t len) |
31143d5d | 186 | { |
3560358a | 187 | struct netfs_io_subrequest *subreq; |
31143d5d | 188 | |
3560358a | 189 | _enter("%x,%llx-%llx", wreq->debug_id, start, start + len); |
31143d5d | 190 | |
3560358a DH |
191 | subreq = netfs_create_write_request(wreq, NETFS_UPLOAD_TO_SERVER, |
192 | start, len, afs_upload_to_server_worker); | |
193 | if (subreq) | |
194 | netfs_queue_write_request(subreq); | |
31143d5d DH |
195 | } |
196 | ||
197 | /* | |
198 | * write some of the pending data back to the server | |
199 | */ | |
3560358a | 200 | int afs_writepages(struct address_space *mapping, struct writeback_control *wbc) |
31143d5d | 201 | { |
ec0fa0b6 | 202 | struct afs_vnode *vnode = AFS_FS_I(mapping->host); |
31143d5d DH |
203 | int ret; |
204 | ||
ec0fa0b6 DH |
205 | /* We have to be careful as we can end up racing with setattr() |
206 | * truncating the pagecache since the caller doesn't take a lock here | |
207 | * to prevent it. | |
208 | */ | |
209 | if (wbc->sync_mode == WB_SYNC_ALL) | |
210 | down_read(&vnode->validate_lock); | |
211 | else if (!down_read_trylock(&vnode->validate_lock)) | |
212 | return 0; | |
213 | ||
3560358a | 214 | ret = netfs_writepages(mapping, wbc); |
ec0fa0b6 | 215 | up_read(&vnode->validate_lock); |
31143d5d DH |
216 | return ret; |
217 | } | |
218 | ||
31143d5d DH |
219 | /* |
220 | * flush any dirty pages for this process, and check for write errors. | |
221 | * - the return status from this call provides a reliable indication of | |
222 | * whether any write errors occurred for this process. | |
223 | */ | |
02c24a82 | 224 | int afs_fsync(struct file *file, loff_t start, loff_t end, int datasync) |
31143d5d | 225 | { |
3978d816 DH |
226 | struct afs_vnode *vnode = AFS_FS_I(file_inode(file)); |
227 | struct afs_file *af = file->private_data; | |
228 | int ret; | |
31143d5d | 229 | |
3b6492df | 230 | _enter("{%llx:%llu},{n=%pD},%d", |
3c981bfc | 231 | vnode->fid.vid, vnode->fid.vnode, file, |
31143d5d DH |
232 | datasync); |
233 | ||
3978d816 DH |
234 | ret = afs_validate(vnode, af->key); |
235 | if (ret < 0) | |
236 | return ret; | |
237 | ||
4343d008 | 238 | return file_write_and_wait_range(file, start, end); |
31143d5d | 239 | } |
9b3f26c9 DH |
240 | |
241 | /* | |
242 | * notification that a previously read-only page is about to become writable | |
243 | * - if it returns an error, the caller will deliver a bus error signal | |
244 | */ | |
0722f186 | 245 | vm_fault_t afs_page_mkwrite(struct vm_fault *vmf) |
9b3f26c9 | 246 | { |
1cf7a151 | 247 | struct file *file = vmf->vma->vm_file; |
3978d816 | 248 | |
3560358a DH |
249 | if (afs_validate(AFS_FS_I(file_inode(file)), afs_file_key(file)) < 0) |
250 | return VM_FAULT_SIGBUS; | |
251 | return netfs_page_mkwrite(vmf, NULL); | |
9b3f26c9 | 252 | } |
4343d008 DH |
253 | |
254 | /* | |
255 | * Prune the keys cached for writeback. The caller must hold vnode->wb_lock. | |
256 | */ | |
257 | void afs_prune_wb_keys(struct afs_vnode *vnode) | |
258 | { | |
259 | LIST_HEAD(graveyard); | |
260 | struct afs_wb_key *wbk, *tmp; | |
261 | ||
262 | /* Discard unused keys */ | |
263 | spin_lock(&vnode->wb_lock); | |
264 | ||
874c8ca1 DH |
265 | if (!mapping_tagged(&vnode->netfs.inode.i_data, PAGECACHE_TAG_WRITEBACK) && |
266 | !mapping_tagged(&vnode->netfs.inode.i_data, PAGECACHE_TAG_DIRTY)) { | |
4343d008 DH |
267 | list_for_each_entry_safe(wbk, tmp, &vnode->wb_keys, vnode_link) { |
268 | if (refcount_read(&wbk->usage) == 1) | |
269 | list_move(&wbk->vnode_link, &graveyard); | |
270 | } | |
271 | } | |
272 | ||
273 | spin_unlock(&vnode->wb_lock); | |
274 | ||
275 | while (!list_empty(&graveyard)) { | |
276 | wbk = list_entry(graveyard.next, struct afs_wb_key, vnode_link); | |
277 | list_del(&wbk->vnode_link); | |
278 | afs_put_wb_key(wbk); | |
279 | } | |
280 | } |