callback.c 8.0 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>
A
Alexey Dobriyan 已提交
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 51 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
/*
 * 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;
	new->server = afs_get_server(server);
	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 100 101 102 103 104
	if (vnode->cb_interest &&
	    likely(vnode->cb_interest == entry->cb_interest))
		return 0;

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

105 106
	vcbi = vnode->cb_interest;
	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
			old = vnode->cb_interest;
			vnode->cb_interest = cbi;
119
			write_sequnlock(&vnode->cb_lock);
120
			afs_put_cb_interest(afs_v2net(vnode), old);
121 122
			return 0;
		}
L
Linus Torvalds 已提交
123

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

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

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

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

156 157 158 159 160 161 162
	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);

163 164
	old = vnode->cb_interest;
	vnode->cb_interest = cbi;
165
	vnode->cb_s_break = cbi->server->cb_s_break;
166
	vnode->cb_v_break = vnode->volume->cb_v_break;
167 168 169
	clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags);

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

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

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

			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;

193
			write_unlock(&cbi->server->cb_break_lock);
194
			kfree(vi);
195 196 197
			afs_put_server(net, cbi->server);
		}
		kfree(cbi);
198
	}
199 200 201 202 203 204 205
}

/*
 * allow the fileserver to request callback state (re-)initialisation
 */
void afs_init_callback_state(struct afs_server *server)
{
206
	if (!test_and_clear_bit(AFS_SERVER_FL_NEW, &server->flags))
207
		server->cb_s_break++;
208 209 210 211 212
}

/*
 * actually break a callback
 */
213
void afs_break_callback(struct afs_vnode *vnode)
214 215 216
{
	_enter("");

217 218
	write_seqlock(&vnode->cb_lock);

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 225 226 227

		spin_lock(&vnode->lock);

		_debug("break callback");

D
David Howells 已提交
228 229 230
		if (list_empty(&vnode->granted_locks) &&
		    !list_empty(&vnode->pending_locks))
			afs_lock_may_be_available(vnode);
231 232
		spin_unlock(&vnode->lock);
	}
233 234

	write_sequnlock(&vnode->cb_lock);
235 236 237 238 239 240 241 242 243 244 245
}

/*
 * 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)
{
246
	struct afs_vol_interest *vi;
247 248
	struct afs_cb_interest *cbi;
	struct afs_iget_data data;
249
	struct afs_vnode *vnode;
250
	struct inode *inode;
251

252
	read_lock(&server->cb_break_lock);
253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268
	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;
269

270 271 272
	/* Step through all interested superblocks.  There may be more than one
	 * because of cell aliasing.
	 */
273
	hlist_for_each_entry(cbi, &vi->cb_interests, cb_vlink) {
274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291
		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;

			write_lock(&volume->cb_break_lock);
			volume->cb_v_break++;
			write_unlock(&volume->cb_break_lock);
		} 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);
				afs_break_callback(vnode);
				iput(inode);
			}
292 293
		}
	}
294

295
out:
296
	read_unlock(&server->cb_break_lock);
D
David Howells 已提交
297
}
L
Linus Torvalds 已提交
298 299 300 301

/*
 * allow the fileserver to break callback promises
 */
302
void afs_break_callbacks(struct afs_server *server, size_t count,
303
			 struct afs_callback_break *callbacks)
L
Linus Torvalds 已提交
304
{
305
	_enter("%p,%zu,", server, count);
L
Linus Torvalds 已提交
306

307 308
	ASSERT(server != NULL);
	ASSERTCMP(count, <=, AFSCBMAX);
L
Linus Torvalds 已提交
309

310 311
	/* TODO: Sort the callback break list by volume ID */

312
	for (; count > 0; callbacks++, count--) {
L
Linus Torvalds 已提交
313 314 315 316
		_debug("- Fid { vl=%08x n=%u u=%u }  CB { v=%u x=%u t=%u }",
		       callbacks->fid.vid,
		       callbacks->fid.vnode,
		       callbacks->fid.unique,
317 318 319
		       callbacks->cb.version,
		       callbacks->cb.expiry,
		       callbacks->cb.type
L
Linus Torvalds 已提交
320
		       );
321 322 323 324 325 326
		afs_break_one_callback(server, &callbacks->fid);
	}

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

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

335 336 337
	for (i = 0; i < slist->nr_servers; i++) {
		afs_put_cb_interest(net, slist->servers[i].cb_interest);
		slist->servers[i].cb_interest = NULL;
338 339
	}
}