callback.c 8.4 KB
Newer Older
L
Linus Torvalds 已提交
1
/*
2
 * Copyright (c) 2002, 2007 Red Hat, Inc. All rights reserved.
L
Linus Torvalds 已提交
3 4 5 6 7 8 9 10
 *
 * This software may be freely redistributed under the terms of the
 * GNU General Public License.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
11
 * Authors: David Woodhouse <dwmw2@infradead.org>
L
Linus Torvalds 已提交
12 13 14 15 16 17 18
 *          David Howells <dhowells@redhat.com>
 *
 */

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
19
#include <linux/circ_buf.h>
20
#include <linux/sched.h>
L
Linus Torvalds 已提交
21
#include "internal.h"
22

23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
/*
 * Create volume and callback interests on a server.
 */
static struct afs_cb_interest *afs_create_interest(struct afs_server *server,
						   struct afs_vnode *vnode)
{
	struct afs_vol_interest *new_vi, *vi;
	struct afs_cb_interest *new;
	struct hlist_node **pp;

	new_vi = kzalloc(sizeof(struct afs_vol_interest), GFP_KERNEL);
	if (!new_vi)
		return NULL;

	new = kzalloc(sizeof(struct afs_cb_interest), GFP_KERNEL);
	if (!new) {
		kfree(new_vi);
		return NULL;
	}

	new_vi->usage = 1;
	new_vi->vid = vnode->volume->vid;
	INIT_HLIST_NODE(&new_vi->srv_link);
	INIT_HLIST_HEAD(&new_vi->cb_interests);

	refcount_set(&new->usage, 1);
	new->sb = vnode->vfs_inode.i_sb;
	new->vid = vnode->volume->vid;
51
	new->server = afs_get_server(server, afs_server_trace_get_new_cbi);
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
	INIT_HLIST_NODE(&new->cb_vlink);

	write_lock(&server->cb_break_lock);

	for (pp = &server->cb_volumes.first; *pp; pp = &(*pp)->next) {
		vi = hlist_entry(*pp, struct afs_vol_interest, srv_link);
		if (vi->vid < new_vi->vid)
			continue;
		if (vi->vid > new_vi->vid)
			break;
		vi->usage++;
		goto found_vi;
	}

	new_vi->srv_link.pprev = pp;
	new_vi->srv_link.next = *pp;
	if (*pp)
		(*pp)->pprev = &new_vi->srv_link.next;
	*pp = &new_vi->srv_link;
	vi = new_vi;
	new_vi = NULL;
found_vi:

	new->vol_interest = vi;
	hlist_add_head(&new->cb_vlink, &vi->cb_interests);

	write_unlock(&server->cb_break_lock);
	kfree(new_vi);
	return new;
}

L
Linus Torvalds 已提交
83
/*
84 85
 * Set up an interest-in-callbacks record for a volume on a server and
 * register it with the server.
86
 * - Called with vnode->io_lock held.
L
Linus Torvalds 已提交
87
 */
88
int afs_register_server_cb_interest(struct afs_vnode *vnode,
89 90
				    struct afs_server_list *slist,
				    unsigned int index)
L
Linus Torvalds 已提交
91
{
92 93
	struct afs_server_entry *entry = &slist->servers[index];
	struct afs_cb_interest *cbi, *vcbi, *new, *old;
94
	struct afs_server *server = entry->server;
95 96

again:
97 98 99
	vcbi = rcu_dereference_protected(vnode->cb_interest,
					 lockdep_is_held(&vnode->io_lock));
	if (vcbi && likely(vcbi == entry->cb_interest))
100 101 102 103 104 105
		return 0;

	read_lock(&slist->lock);
	cbi = afs_get_cb_interest(entry->cb_interest);
	read_unlock(&slist->lock);

106
	if (vcbi) {
107 108
		if (vcbi == cbi) {
			afs_put_cb_interest(afs_v2net(vnode), cbi);
109
			return 0;
110
		}
111

112 113 114
		/* Use a new interest in the server list for the same server
		 * rather than an old one that's still attached to a vnode.
		 */
115 116
		if (cbi && vcbi->server == cbi->server) {
			write_seqlock(&vnode->cb_lock);
117 118 119
			old = rcu_dereference_protected(vnode->cb_interest,
							lockdep_is_held(&vnode->cb_lock.lock));
			rcu_assign_pointer(vnode->cb_interest, cbi);
120
			write_sequnlock(&vnode->cb_lock);
121
			afs_put_cb_interest(afs_v2net(vnode), old);
122 123
			return 0;
		}
L
Linus Torvalds 已提交
124

125
		/* Re-use the one attached to the vnode. */
126
		if (!cbi && vcbi->server == server) {
127 128 129 130
			write_lock(&slist->lock);
			if (entry->cb_interest) {
				write_unlock(&slist->lock);
				afs_put_cb_interest(afs_v2net(vnode), cbi);
131 132
				goto again;
			}
133 134 135

			entry->cb_interest = cbi;
			write_unlock(&slist->lock);
136 137 138
			return 0;
		}
	}
L
Linus Torvalds 已提交
139

140
	if (!cbi) {
141
		new = afs_create_interest(server, vnode);
142 143 144
		if (!new)
			return -ENOMEM;

145 146 147
		write_lock(&slist->lock);
		if (!entry->cb_interest) {
			entry->cb_interest = afs_get_cb_interest(new);
148
			cbi = new;
149
			new = NULL;
150
		} else {
151
			cbi = afs_get_cb_interest(entry->cb_interest);
152
		}
153 154
		write_unlock(&slist->lock);
		afs_put_cb_interest(afs_v2net(vnode), new);
155
	}
L
Linus Torvalds 已提交
156

157 158 159 160 161 162 163
	ASSERT(cbi);

	/* Change the server the vnode is using.  This entails scrubbing any
	 * interest the vnode had in the previous server it was using.
	 */
	write_seqlock(&vnode->cb_lock);

164 165 166
	old = rcu_dereference_protected(vnode->cb_interest,
					lockdep_is_held(&vnode->cb_lock.lock));
	rcu_assign_pointer(vnode->cb_interest, cbi);
167
	vnode->cb_s_break = cbi->server->cb_s_break;
168
	vnode->cb_v_break = vnode->volume->cb_v_break;
169 170 171
	clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags);

	write_sequnlock(&vnode->cb_lock);
172
	afs_put_cb_interest(afs_v2net(vnode), old);
173
	return 0;
174
}
L
Linus Torvalds 已提交
175

176 177 178 179 180
/*
 * Remove an interest on a server.
 */
void afs_put_cb_interest(struct afs_net *net, struct afs_cb_interest *cbi)
{
181 182
	struct afs_vol_interest *vi;

183
	if (cbi && refcount_dec_and_test(&cbi->usage)) {
184
		if (!hlist_unhashed(&cbi->cb_vlink)) {
185
			write_lock(&cbi->server->cb_break_lock);
186 187 188 189 190 191 192 193 194

			hlist_del_init(&cbi->cb_vlink);
			vi = cbi->vol_interest;
			cbi->vol_interest = NULL;
			if (--vi->usage == 0)
				hlist_del(&vi->srv_link);
			else
				vi = NULL;

195
			write_unlock(&cbi->server->cb_break_lock);
196 197
			if (vi)
				kfree_rcu(vi, rcu);
198
			afs_put_server(net, cbi->server, afs_server_trace_put_cbi);
199
		}
200
		kfree_rcu(cbi, rcu);
201
	}
202 203 204 205 206 207 208
}

/*
 * allow the fileserver to request callback state (re-)initialisation
 */
void afs_init_callback_state(struct afs_server *server)
{
209
	server->cb_s_break++;
210 211 212 213 214
}

/*
 * actually break a callback
 */
215
void __afs_break_callback(struct afs_vnode *vnode, enum afs_cb_break_reason reason)
216 217 218
{
	_enter("");

219
	clear_bit(AFS_VNODE_NEW_CONTENT, &vnode->flags);
220 221 222
	if (test_and_clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags)) {
		vnode->cb_break++;
		afs_clear_permits(vnode);
223

224
		if (vnode->lock_state == AFS_VNODE_LOCK_WAITING_FOR_CB)
225
			afs_lock_may_be_available(vnode);
226 227 228 229

		trace_afs_cb_break(&vnode->fid, vnode->cb_break, reason, true);
	} else {
		trace_afs_cb_break(&vnode->fid, vnode->cb_break, reason, false);
230
	}
231
}
232

233
void afs_break_callback(struct afs_vnode *vnode, enum afs_cb_break_reason reason)
234 235
{
	write_seqlock(&vnode->cb_lock);
236
	__afs_break_callback(vnode, reason);
237
	write_sequnlock(&vnode->cb_lock);
238 239 240 241 242 243 244 245 246 247 248
}

/*
 * allow the fileserver to explicitly break one callback
 * - happens when
 *   - the backing file is changed
 *   - a lock is released
 */
static void afs_break_one_callback(struct afs_server *server,
				   struct afs_fid *fid)
{
249
	struct afs_vol_interest *vi;
250 251
	struct afs_cb_interest *cbi;
	struct afs_iget_data data;
252
	struct afs_vnode *vnode;
253
	struct inode *inode;
254

255
	read_lock(&server->cb_break_lock);
256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271
	hlist_for_each_entry(vi, &server->cb_volumes, srv_link) {
		if (vi->vid < fid->vid)
			continue;
		if (vi->vid > fid->vid) {
			vi = NULL;
			break;
		}
		//atomic_inc(&vi->usage);
		break;
	}

	/* TODO: Find all matching volumes if we couldn't match the server and
	 * break them anyway.
	 */
	if (!vi)
		goto out;
272

273 274 275
	/* Step through all interested superblocks.  There may be more than one
	 * because of cell aliasing.
	 */
276
	hlist_for_each_entry(cbi, &vi->cb_interests, cb_vlink) {
277 278 279 280 281
		if (fid->vnode == 0 && fid->unique == 0) {
			/* The callback break applies to an entire volume. */
			struct afs_super_info *as = AFS_FS_S(cbi->sb);
			struct afs_volume *volume = as->volume;

282
			write_lock(&volume->cb_v_break_lock);
283
			volume->cb_v_break++;
284 285
			trace_afs_cb_break(fid, volume->cb_v_break,
					   afs_cb_break_for_volume_callback, false);
286
			write_unlock(&volume->cb_v_break_lock);
287 288 289 290 291 292 293
		} else {
			data.volume = NULL;
			data.fid = *fid;
			inode = ilookup5_nowait(cbi->sb, fid->vnode,
						afs_iget5_test, &data);
			if (inode) {
				vnode = AFS_FS_I(inode);
294
				afs_break_callback(vnode, afs_cb_break_for_callback);
295
				iput(inode);
296 297
			} else {
				trace_afs_cb_miss(fid, afs_cb_break_for_callback);
298
			}
299 300
		}
	}
301

302
out:
303
	read_unlock(&server->cb_break_lock);
304
}
L
Linus Torvalds 已提交
305 306 307 308

/*
 * allow the fileserver to break callback promises
 */
309
void afs_break_callbacks(struct afs_server *server, size_t count,
310
			 struct afs_callback_break *callbacks)
L
Linus Torvalds 已提交
311
{
312
	_enter("%p,%zu,", server, count);
L
Linus Torvalds 已提交
313

314
	ASSERT(server != NULL);
L
Linus Torvalds 已提交
315

316 317
	/* TODO: Sort the callback break list by volume ID */

318
	for (; count > 0; callbacks++, count--) {
319
		_debug("- Fid { vl=%08llx n=%llu u=%u }",
L
Linus Torvalds 已提交
320 321
		       callbacks->fid.vid,
		       callbacks->fid.vnode,
322
		       callbacks->fid.unique);
323 324 325 326 327 328
		afs_break_one_callback(server, &callbacks->fid);
	}

	_leave("");
	return;
}
L
Linus Torvalds 已提交
329

330
/*
331
 * Clear the callback interests in a server list.
332
 */
333
void afs_clear_callback_interests(struct afs_net *net, struct afs_server_list *slist)
334
{
335
	int i;
336

337 338 339
	for (i = 0; i < slist->nr_servers; i++) {
		afs_put_cb_interest(net, slist->servers[i].cb_interest);
		slist->servers[i].cb_interest = NULL;
340 341
	}
}
新手
引导
客服 返回
顶部