1 | /* |
2 | * Copyright (c) 2002, 2007 Red Hat, Inc. All rights reserved. |
3 | * |
4 | * This software may be freely redistributed under the terms of the |
5 | * GNU General Public License. |
6 | * |
7 | * You should have received a copy of the GNU General Public License |
8 | * along with this program; if not, write to the Free Software |
9 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
10 | * |
11 | * Authors: David Woodhouse <dwmw2@infradead.org> |
12 | * David Howells <dhowells@redhat.com> |
13 | * |
14 | */ |
15 | |
16 | #include <linux/kernel.h> |
17 | #include <linux/module.h> |
18 | #include <linux/init.h> |
19 | #include <linux/circ_buf.h> |
20 | #include <linux/sched.h> |
21 | #include "internal.h" |
22 | |
23 | /* |
24 | * Handle invalidation of an mmap'd file. We invalidate all the PTEs referring |
25 | * to the pages in this file's pagecache, forcing the kernel to go through |
26 | * ->fault() or ->page_mkwrite() - at which point we can handle invalidation |
27 | * more fully. |
28 | */ |
29 | void afs_invalidate_mmap_work(struct work_struct *work) |
30 | { |
31 | struct afs_vnode *vnode = container_of(work, struct afs_vnode, cb_work); |
32 | |
33 | unmap_mapping_pages(mapping: vnode->netfs.inode.i_mapping, start: 0, nr: 0, even_cows: false); |
34 | } |
35 | |
36 | void afs_server_init_callback_work(struct work_struct *work) |
37 | { |
38 | struct afs_server *server = container_of(work, struct afs_server, initcb_work); |
39 | struct afs_vnode *vnode; |
40 | struct afs_cell *cell = server->cell; |
41 | |
42 | down_read(sem: &cell->fs_open_mmaps_lock); |
43 | |
44 | list_for_each_entry(vnode, &cell->fs_open_mmaps, cb_mmap_link) { |
45 | if (vnode->cb_server == server) { |
46 | clear_bit(AFS_VNODE_CB_PROMISED, addr: &vnode->flags); |
47 | queue_work(wq: system_unbound_wq, work: &vnode->cb_work); |
48 | } |
49 | } |
50 | |
51 | up_read(sem: &cell->fs_open_mmaps_lock); |
52 | } |
53 | |
54 | /* |
55 | * Allow the fileserver to request callback state (re-)initialisation. |
56 | * Unfortunately, UUIDs are not guaranteed unique. |
57 | */ |
58 | void afs_init_callback_state(struct afs_server *server) |
59 | { |
60 | rcu_read_lock(); |
61 | do { |
62 | server->cb_s_break++; |
63 | atomic_inc(v: &server->cell->fs_s_break); |
64 | if (!list_empty(head: &server->cell->fs_open_mmaps)) |
65 | queue_work(wq: system_unbound_wq, work: &server->initcb_work); |
66 | |
67 | } while ((server = rcu_dereference(server->uuid_next))); |
68 | rcu_read_unlock(); |
69 | } |
70 | |
71 | /* |
72 | * actually break a callback |
73 | */ |
74 | void __afs_break_callback(struct afs_vnode *vnode, enum afs_cb_break_reason reason) |
75 | { |
76 | _enter("" ); |
77 | |
78 | clear_bit(AFS_VNODE_NEW_CONTENT, addr: &vnode->flags); |
79 | if (test_and_clear_bit(AFS_VNODE_CB_PROMISED, addr: &vnode->flags)) { |
80 | vnode->cb_break++; |
81 | vnode->cb_v_break = vnode->volume->cb_v_break; |
82 | afs_clear_permits(vnode); |
83 | |
84 | if (vnode->lock_state == AFS_VNODE_LOCK_WAITING_FOR_CB) |
85 | afs_lock_may_be_available(vnode); |
86 | |
87 | if (reason != afs_cb_break_for_deleted && |
88 | vnode->status.type == AFS_FTYPE_FILE && |
89 | atomic_read(v: &vnode->cb_nr_mmap)) |
90 | queue_work(wq: system_unbound_wq, work: &vnode->cb_work); |
91 | |
92 | trace_afs_cb_break(fid: &vnode->fid, cb_break: vnode->cb_break, reason, skipped: true); |
93 | } else { |
94 | trace_afs_cb_break(fid: &vnode->fid, cb_break: vnode->cb_break, reason, skipped: false); |
95 | } |
96 | } |
97 | |
98 | void afs_break_callback(struct afs_vnode *vnode, enum afs_cb_break_reason reason) |
99 | { |
100 | write_seqlock(sl: &vnode->cb_lock); |
101 | __afs_break_callback(vnode, reason); |
102 | write_sequnlock(sl: &vnode->cb_lock); |
103 | } |
104 | |
105 | /* |
106 | * Look up a volume by volume ID under RCU conditions. |
107 | */ |
108 | static struct afs_volume *afs_lookup_volume_rcu(struct afs_cell *cell, |
109 | afs_volid_t vid) |
110 | { |
111 | struct afs_volume *volume = NULL; |
112 | struct rb_node *p; |
113 | int seq = 0; |
114 | |
115 | do { |
116 | /* Unfortunately, rbtree walking doesn't give reliable results |
117 | * under just the RCU read lock, so we have to check for |
118 | * changes. |
119 | */ |
120 | read_seqbegin_or_lock(lock: &cell->volume_lock, seq: &seq); |
121 | |
122 | p = rcu_dereference_raw(cell->volumes.rb_node); |
123 | while (p) { |
124 | volume = rb_entry(p, struct afs_volume, cell_node); |
125 | |
126 | if (volume->vid < vid) |
127 | p = rcu_dereference_raw(p->rb_left); |
128 | else if (volume->vid > vid) |
129 | p = rcu_dereference_raw(p->rb_right); |
130 | else |
131 | break; |
132 | volume = NULL; |
133 | } |
134 | |
135 | } while (need_seqretry(lock: &cell->volume_lock, seq)); |
136 | |
137 | done_seqretry(lock: &cell->volume_lock, seq); |
138 | return volume; |
139 | } |
140 | |
141 | /* |
142 | * allow the fileserver to explicitly break one callback |
143 | * - happens when |
144 | * - the backing file is changed |
145 | * - a lock is released |
146 | */ |
147 | static void afs_break_one_callback(struct afs_volume *volume, |
148 | struct afs_fid *fid) |
149 | { |
150 | struct super_block *sb; |
151 | struct afs_vnode *vnode; |
152 | struct inode *inode; |
153 | |
154 | if (fid->vnode == 0 && fid->unique == 0) { |
155 | /* The callback break applies to an entire volume. */ |
156 | write_lock(&volume->cb_v_break_lock); |
157 | volume->cb_v_break++; |
158 | trace_afs_cb_break(fid, cb_break: volume->cb_v_break, |
159 | reason: afs_cb_break_for_volume_callback, skipped: false); |
160 | write_unlock(&volume->cb_v_break_lock); |
161 | return; |
162 | } |
163 | |
164 | /* See if we can find a matching inode - even an I_NEW inode needs to |
165 | * be marked as it can have its callback broken before we finish |
166 | * setting up the local inode. |
167 | */ |
168 | sb = rcu_dereference(volume->sb); |
169 | if (!sb) |
170 | return; |
171 | |
172 | inode = find_inode_rcu(sb, fid->vnode, afs_ilookup5_test_by_fid, fid); |
173 | if (inode) { |
174 | vnode = AFS_FS_I(inode); |
175 | afs_break_callback(vnode, reason: afs_cb_break_for_callback); |
176 | } else { |
177 | trace_afs_cb_miss(fid, reason: afs_cb_break_for_callback); |
178 | } |
179 | } |
180 | |
181 | static void afs_break_some_callbacks(struct afs_server *server, |
182 | struct afs_callback_break *cbb, |
183 | size_t *_count) |
184 | { |
185 | struct afs_callback_break *residue = cbb; |
186 | struct afs_volume *volume; |
187 | afs_volid_t vid = cbb->fid.vid; |
188 | size_t i; |
189 | |
190 | volume = afs_lookup_volume_rcu(cell: server->cell, vid); |
191 | |
192 | /* TODO: Find all matching volumes if we couldn't match the server and |
193 | * break them anyway. |
194 | */ |
195 | |
196 | for (i = *_count; i > 0; cbb++, i--) { |
197 | if (cbb->fid.vid == vid) { |
198 | _debug("- Fid { vl=%08llx n=%llu u=%u }" , |
199 | cbb->fid.vid, |
200 | cbb->fid.vnode, |
201 | cbb->fid.unique); |
202 | --*_count; |
203 | if (volume) |
204 | afs_break_one_callback(volume, fid: &cbb->fid); |
205 | } else { |
206 | *residue++ = *cbb; |
207 | } |
208 | } |
209 | } |
210 | |
211 | /* |
212 | * allow the fileserver to break callback promises |
213 | */ |
214 | void afs_break_callbacks(struct afs_server *server, size_t count, |
215 | struct afs_callback_break *callbacks) |
216 | { |
217 | _enter("%p,%zu," , server, count); |
218 | |
219 | ASSERT(server != NULL); |
220 | |
221 | rcu_read_lock(); |
222 | |
223 | while (count > 0) |
224 | afs_break_some_callbacks(server, cbb: callbacks, count: &count); |
225 | |
226 | rcu_read_unlock(); |
227 | return; |
228 | } |
229 | |