18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Copyright (c) 2002, 2007 Red Hat, Inc. All rights reserved.
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * This software may be freely redistributed under the terms of the
58c2ecf20Sopenharmony_ci * GNU General Public License.
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * You should have received a copy of the GNU General Public License
88c2ecf20Sopenharmony_ci * along with this program; if not, write to the Free Software
98c2ecf20Sopenharmony_ci * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci * Authors: David Woodhouse <dwmw2@infradead.org>
128c2ecf20Sopenharmony_ci *          David Howells <dhowells@redhat.com>
138c2ecf20Sopenharmony_ci *
148c2ecf20Sopenharmony_ci */
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include <linux/kernel.h>
178c2ecf20Sopenharmony_ci#include <linux/module.h>
188c2ecf20Sopenharmony_ci#include <linux/init.h>
198c2ecf20Sopenharmony_ci#include <linux/circ_buf.h>
208c2ecf20Sopenharmony_ci#include <linux/sched.h>
218c2ecf20Sopenharmony_ci#include "internal.h"
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci/*
248c2ecf20Sopenharmony_ci * Allow the fileserver to request callback state (re-)initialisation.
258c2ecf20Sopenharmony_ci * Unfortunately, UUIDs are not guaranteed unique.
268c2ecf20Sopenharmony_ci */
278c2ecf20Sopenharmony_civoid afs_init_callback_state(struct afs_server *server)
288c2ecf20Sopenharmony_ci{
298c2ecf20Sopenharmony_ci	rcu_read_lock();
308c2ecf20Sopenharmony_ci	do {
318c2ecf20Sopenharmony_ci		server->cb_s_break++;
328c2ecf20Sopenharmony_ci		server = rcu_dereference(server->uuid_next);
338c2ecf20Sopenharmony_ci	} while (0);
348c2ecf20Sopenharmony_ci	rcu_read_unlock();
358c2ecf20Sopenharmony_ci}
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci/*
388c2ecf20Sopenharmony_ci * actually break a callback
398c2ecf20Sopenharmony_ci */
408c2ecf20Sopenharmony_civoid __afs_break_callback(struct afs_vnode *vnode, enum afs_cb_break_reason reason)
418c2ecf20Sopenharmony_ci{
428c2ecf20Sopenharmony_ci	_enter("");
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	clear_bit(AFS_VNODE_NEW_CONTENT, &vnode->flags);
458c2ecf20Sopenharmony_ci	if (test_and_clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags)) {
468c2ecf20Sopenharmony_ci		vnode->cb_break++;
478c2ecf20Sopenharmony_ci		afs_clear_permits(vnode);
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci		if (vnode->lock_state == AFS_VNODE_LOCK_WAITING_FOR_CB)
508c2ecf20Sopenharmony_ci			afs_lock_may_be_available(vnode);
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci		trace_afs_cb_break(&vnode->fid, vnode->cb_break, reason, true);
538c2ecf20Sopenharmony_ci	} else {
548c2ecf20Sopenharmony_ci		trace_afs_cb_break(&vnode->fid, vnode->cb_break, reason, false);
558c2ecf20Sopenharmony_ci	}
568c2ecf20Sopenharmony_ci}
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_civoid afs_break_callback(struct afs_vnode *vnode, enum afs_cb_break_reason reason)
598c2ecf20Sopenharmony_ci{
608c2ecf20Sopenharmony_ci	write_seqlock(&vnode->cb_lock);
618c2ecf20Sopenharmony_ci	__afs_break_callback(vnode, reason);
628c2ecf20Sopenharmony_ci	write_sequnlock(&vnode->cb_lock);
638c2ecf20Sopenharmony_ci}
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci/*
668c2ecf20Sopenharmony_ci * Look up a volume by volume ID under RCU conditions.
678c2ecf20Sopenharmony_ci */
688c2ecf20Sopenharmony_cistatic struct afs_volume *afs_lookup_volume_rcu(struct afs_cell *cell,
698c2ecf20Sopenharmony_ci						afs_volid_t vid)
708c2ecf20Sopenharmony_ci{
718c2ecf20Sopenharmony_ci	struct afs_volume *volume = NULL;
728c2ecf20Sopenharmony_ci	struct rb_node *p;
738c2ecf20Sopenharmony_ci	int seq = 1;
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	do {
768c2ecf20Sopenharmony_ci		/* Unfortunately, rbtree walking doesn't give reliable results
778c2ecf20Sopenharmony_ci		 * under just the RCU read lock, so we have to check for
788c2ecf20Sopenharmony_ci		 * changes.
798c2ecf20Sopenharmony_ci		 */
808c2ecf20Sopenharmony_ci		seq++; /* 2 on the 1st/lockless path, otherwise odd */
818c2ecf20Sopenharmony_ci		read_seqbegin_or_lock(&cell->volume_lock, &seq);
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci		p = rcu_dereference_raw(cell->volumes.rb_node);
848c2ecf20Sopenharmony_ci		while (p) {
858c2ecf20Sopenharmony_ci			volume = rb_entry(p, struct afs_volume, cell_node);
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci			if (volume->vid < vid)
888c2ecf20Sopenharmony_ci				p = rcu_dereference_raw(p->rb_left);
898c2ecf20Sopenharmony_ci			else if (volume->vid > vid)
908c2ecf20Sopenharmony_ci				p = rcu_dereference_raw(p->rb_right);
918c2ecf20Sopenharmony_ci			else
928c2ecf20Sopenharmony_ci				break;
938c2ecf20Sopenharmony_ci			volume = NULL;
948c2ecf20Sopenharmony_ci		}
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	} while (need_seqretry(&cell->volume_lock, seq));
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	done_seqretry(&cell->volume_lock, seq);
998c2ecf20Sopenharmony_ci	return volume;
1008c2ecf20Sopenharmony_ci}
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci/*
1038c2ecf20Sopenharmony_ci * allow the fileserver to explicitly break one callback
1048c2ecf20Sopenharmony_ci * - happens when
1058c2ecf20Sopenharmony_ci *   - the backing file is changed
1068c2ecf20Sopenharmony_ci *   - a lock is released
1078c2ecf20Sopenharmony_ci */
1088c2ecf20Sopenharmony_cistatic void afs_break_one_callback(struct afs_volume *volume,
1098c2ecf20Sopenharmony_ci				   struct afs_fid *fid)
1108c2ecf20Sopenharmony_ci{
1118c2ecf20Sopenharmony_ci	struct super_block *sb;
1128c2ecf20Sopenharmony_ci	struct afs_vnode *vnode;
1138c2ecf20Sopenharmony_ci	struct inode *inode;
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	if (fid->vnode == 0 && fid->unique == 0) {
1168c2ecf20Sopenharmony_ci		/* The callback break applies to an entire volume. */
1178c2ecf20Sopenharmony_ci		write_lock(&volume->cb_v_break_lock);
1188c2ecf20Sopenharmony_ci		volume->cb_v_break++;
1198c2ecf20Sopenharmony_ci		trace_afs_cb_break(fid, volume->cb_v_break,
1208c2ecf20Sopenharmony_ci				   afs_cb_break_for_volume_callback, false);
1218c2ecf20Sopenharmony_ci		write_unlock(&volume->cb_v_break_lock);
1228c2ecf20Sopenharmony_ci		return;
1238c2ecf20Sopenharmony_ci	}
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	/* See if we can find a matching inode - even an I_NEW inode needs to
1268c2ecf20Sopenharmony_ci	 * be marked as it can have its callback broken before we finish
1278c2ecf20Sopenharmony_ci	 * setting up the local inode.
1288c2ecf20Sopenharmony_ci	 */
1298c2ecf20Sopenharmony_ci	sb = rcu_dereference(volume->sb);
1308c2ecf20Sopenharmony_ci	if (!sb)
1318c2ecf20Sopenharmony_ci		return;
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	inode = find_inode_rcu(sb, fid->vnode, afs_ilookup5_test_by_fid, fid);
1348c2ecf20Sopenharmony_ci	if (inode) {
1358c2ecf20Sopenharmony_ci		vnode = AFS_FS_I(inode);
1368c2ecf20Sopenharmony_ci		afs_break_callback(vnode, afs_cb_break_for_callback);
1378c2ecf20Sopenharmony_ci	} else {
1388c2ecf20Sopenharmony_ci		trace_afs_cb_miss(fid, afs_cb_break_for_callback);
1398c2ecf20Sopenharmony_ci	}
1408c2ecf20Sopenharmony_ci}
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_cistatic void afs_break_some_callbacks(struct afs_server *server,
1438c2ecf20Sopenharmony_ci				     struct afs_callback_break *cbb,
1448c2ecf20Sopenharmony_ci				     size_t *_count)
1458c2ecf20Sopenharmony_ci{
1468c2ecf20Sopenharmony_ci	struct afs_callback_break *residue = cbb;
1478c2ecf20Sopenharmony_ci	struct afs_volume *volume;
1488c2ecf20Sopenharmony_ci	afs_volid_t vid = cbb->fid.vid;
1498c2ecf20Sopenharmony_ci	size_t i;
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	volume = afs_lookup_volume_rcu(server->cell, vid);
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	/* TODO: Find all matching volumes if we couldn't match the server and
1548c2ecf20Sopenharmony_ci	 * break them anyway.
1558c2ecf20Sopenharmony_ci	 */
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	for (i = *_count; i > 0; cbb++, i--) {
1588c2ecf20Sopenharmony_ci		if (cbb->fid.vid == vid) {
1598c2ecf20Sopenharmony_ci			_debug("- Fid { vl=%08llx n=%llu u=%u }",
1608c2ecf20Sopenharmony_ci			       cbb->fid.vid,
1618c2ecf20Sopenharmony_ci			       cbb->fid.vnode,
1628c2ecf20Sopenharmony_ci			       cbb->fid.unique);
1638c2ecf20Sopenharmony_ci			--*_count;
1648c2ecf20Sopenharmony_ci			if (volume)
1658c2ecf20Sopenharmony_ci				afs_break_one_callback(volume, &cbb->fid);
1668c2ecf20Sopenharmony_ci		} else {
1678c2ecf20Sopenharmony_ci			*residue++ = *cbb;
1688c2ecf20Sopenharmony_ci		}
1698c2ecf20Sopenharmony_ci	}
1708c2ecf20Sopenharmony_ci}
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci/*
1738c2ecf20Sopenharmony_ci * allow the fileserver to break callback promises
1748c2ecf20Sopenharmony_ci */
1758c2ecf20Sopenharmony_civoid afs_break_callbacks(struct afs_server *server, size_t count,
1768c2ecf20Sopenharmony_ci			 struct afs_callback_break *callbacks)
1778c2ecf20Sopenharmony_ci{
1788c2ecf20Sopenharmony_ci	_enter("%p,%zu,", server, count);
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	ASSERT(server != NULL);
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	rcu_read_lock();
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	while (count > 0)
1858c2ecf20Sopenharmony_ci		afs_break_some_callbacks(server, callbacks, &count);
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	rcu_read_unlock();
1888c2ecf20Sopenharmony_ci	return;
1898c2ecf20Sopenharmony_ci}
190