Commit ca0e79a4 authored by David Howells's avatar David Howells
Browse files

afs: Make it possible to find the volumes that are using a server



Make it possible to find the afs_volume structs that are using an
afs_server struct to aid in breaking volume callbacks.

The way this is done is that each afs_volume already has an array of
afs_server_entry records that point to the servers where that volume might
be found.  An afs_volume backpointer and a list node is added to each entry
and each entry is then added to an RCU-traversable list on the afs_server
to which it points.

Signed-off-by: default avatarDavid Howells <dhowells@redhat.com>
cc: Marc Dionne <marc.dionne@auristor.com>
cc: linux-afs@lists.infradead.org
parent 21c1f410
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -161,6 +161,7 @@ static struct afs_cell *afs_alloc_cell(struct afs_net *net,
	refcount_set(&cell->ref, 1);
	atomic_set(&cell->active, 0);
	INIT_WORK(&cell->manager, afs_manage_cell_work);
	spin_lock_init(&cell->vs_lock);
	cell->volumes = RB_ROOT;
	INIT_HLIST_HEAD(&cell->proc_volumes);
	seqlock_init(&cell->volume_lock);
+15 −8
Original line number Diff line number Diff line
@@ -414,6 +414,7 @@ struct afs_cell {
	unsigned int		debug_id;

	/* The volumes belonging to this cell */
	spinlock_t		vs_lock;	/* Lock for server->volumes */
	struct rb_root		volumes;	/* Tree of volumes on this server */
	struct hlist_head	proc_volumes;	/* procfs volume list */
	seqlock_t		volume_lock;	/* For volumes */
@@ -564,6 +565,7 @@ struct afs_server {
	struct hlist_node	addr4_link;	/* Link in net->fs_addresses4 */
	struct hlist_node	addr6_link;	/* Link in net->fs_addresses6 */
	struct hlist_node	proc_link;	/* Link in net->fs_proc */
	struct list_head	volumes;	/* RCU list of afs_server_entry objects */
	struct work_struct	initcb_work;	/* Work for CB.InitCallBackState* */
	struct afs_server	*gc_next;	/* Next server in manager's list */
	time64_t		unuse_time;	/* Time at which last unused */
@@ -605,12 +607,14 @@ struct afs_server {
 */
struct afs_server_entry {
	struct afs_server	*server;
	struct afs_volume	*volume;
	struct list_head	slink;		/* Link in server->volumes */
};

struct afs_server_list {
	struct rcu_head		rcu;
	afs_volid_t		vids[AFS_MAXTYPES]; /* Volume IDs */
	refcount_t		usage;
	bool			attached;	/* T if attached to servers */
	unsigned char		nr_servers;
	unsigned char		preferred;	/* Preferred server */
	unsigned short		vnovol_mask;	/* Servers to be skipped due to VNOVOL */
@@ -623,10 +627,9 @@ struct afs_server_list {
 * Live AFS volume management.
 */
struct afs_volume {
	union {
	struct rcu_head	rcu;
		afs_volid_t	vid;		/* volume ID */
	};
	afs_volid_t		vid;		/* The volume ID of this volume */
	afs_volid_t		vids[AFS_MAXTYPES]; /* All associated volume IDs */
	refcount_t		ref;
	time64_t		update_at;	/* Time at which to next update */
	struct afs_cell		*cell;		/* Cell to which belongs (pins ref) */
@@ -1528,10 +1531,14 @@ static inline struct afs_server_list *afs_get_serverlist(struct afs_server_list
}

extern void afs_put_serverlist(struct afs_net *, struct afs_server_list *);
extern struct afs_server_list *afs_alloc_server_list(struct afs_cell *, struct key *,
						     struct afs_vldb_entry *,
						     u8);
struct afs_server_list *afs_alloc_server_list(struct afs_volume *volume,
					      struct key *key,
					      struct afs_vldb_entry *vldb);
extern bool afs_annotate_server_list(struct afs_server_list *, struct afs_server_list *);
void afs_attach_volume_to_servers(struct afs_volume *volume, struct afs_server_list *slist);
void afs_reattach_volume_to_servers(struct afs_volume *volume, struct afs_server_list *slist,
				    struct afs_server_list *old);
void afs_detach_volume_from_servers(struct afs_volume *volume, struct afs_server_list *slist);

/*
 * super.c
+1 −0
Original line number Diff line number Diff line
@@ -217,6 +217,7 @@ static struct afs_server *afs_alloc_server(struct afs_cell *cell,
	server->addr_version = alist->version;
	server->uuid = *uuid;
	rwlock_init(&server->fs_lock);
	INIT_LIST_HEAD(&server->volumes);
	INIT_WORK(&server->initcb_work, afs_server_init_callback_work);
	init_waitqueue_head(&server->probe_wq);
	INIT_LIST_HEAD(&server->probe_link);
+103 −9
Original line number Diff line number Diff line
@@ -24,13 +24,13 @@ void afs_put_serverlist(struct afs_net *net, struct afs_server_list *slist)
/*
 * Build a server list from a VLDB record.
 */
struct afs_server_list *afs_alloc_server_list(struct afs_cell *cell,
struct afs_server_list *afs_alloc_server_list(struct afs_volume *volume,
					      struct key *key,
					      struct afs_vldb_entry *vldb,
					      u8 type_mask)
					      struct afs_vldb_entry *vldb)
{
	struct afs_server_list *slist;
	struct afs_server *server;
	unsigned int type_mask = 1 << volume->type;
	int ret = -ENOMEM, nr_servers = 0, i, j;

	for (i = 0; i < vldb->nr_servers; i++)
@@ -44,15 +44,12 @@ struct afs_server_list *afs_alloc_server_list(struct afs_cell *cell,
	refcount_set(&slist->usage, 1);
	rwlock_init(&slist->lock);

	for (i = 0; i < AFS_MAXTYPES; i++)
		slist->vids[i] = vldb->vid[i];

	/* Make sure a records exists for each server in the list. */
	for (i = 0; i < vldb->nr_servers; i++) {
		if (!(vldb->fs_mask[i] & type_mask))
			continue;

		server = afs_lookup_server(cell, key, &vldb->fs_server[i],
		server = afs_lookup_server(volume->cell, key, &vldb->fs_server[i],
					   vldb->addr_version[i]);
		if (IS_ERR(server)) {
			ret = PTR_ERR(server);
@@ -70,7 +67,7 @@ struct afs_server_list *afs_alloc_server_list(struct afs_cell *cell,
				break;
		if (j < slist->nr_servers) {
			if (slist->servers[j].server == server) {
				afs_put_server(cell->net, server,
				afs_put_server(volume->cell->net, server,
					       afs_server_trace_put_slist_isort);
				continue;
			}
@@ -81,6 +78,7 @@ struct afs_server_list *afs_alloc_server_list(struct afs_cell *cell,
		}

		slist->servers[j].server = server;
		slist->servers[j].volume = volume;
		slist->nr_servers++;
	}

@@ -92,7 +90,7 @@ struct afs_server_list *afs_alloc_server_list(struct afs_cell *cell,
	return slist;

error_2:
	afs_put_serverlist(cell->net, slist);
	afs_put_serverlist(volume->cell->net, slist);
error:
	return ERR_PTR(ret);
}
@@ -127,3 +125,99 @@ bool afs_annotate_server_list(struct afs_server_list *new,

	return true;
}

/*
 * Attach a volume to the servers it is going to use.
 */
void afs_attach_volume_to_servers(struct afs_volume *volume, struct afs_server_list *slist)
{
	struct afs_server_entry *se, *pe;
	struct afs_server *server;
	struct list_head *p;
	unsigned int i;

	spin_lock(&volume->cell->vs_lock);

	for (i = 0; i < slist->nr_servers; i++) {
		se = &slist->servers[i];
		server = se->server;

		list_for_each(p, &server->volumes) {
			pe = list_entry(p, struct afs_server_entry, slink);
			if (volume->vid <= pe->volume->vid)
				break;
		}
		list_add_tail_rcu(&se->slink, p);
	}

	slist->attached = true;
	spin_unlock(&volume->cell->vs_lock);
}

/*
 * Reattach a volume to the servers it is going to use when server list is
 * replaced.  We try to switch the attachment points to avoid rewalking the
 * lists.
 */
void afs_reattach_volume_to_servers(struct afs_volume *volume, struct afs_server_list *new,
				    struct afs_server_list *old)
{
	unsigned int n = 0, o = 0;

	spin_lock(&volume->cell->vs_lock);

	while (n < new->nr_servers || o < old->nr_servers) {
		struct afs_server_entry *pn = n < new->nr_servers ? &new->servers[n] : NULL;
		struct afs_server_entry *po = o < old->nr_servers ? &old->servers[o] : NULL;
		struct afs_server_entry *s;
		struct list_head *p;
		int diff;

		if (pn && po && pn->server == po->server) {
			list_replace_rcu(&po->slink, &pn->slink);
			n++;
			o++;
			continue;
		}

		if (pn && po)
			diff = memcmp(&pn->server->uuid, &po->server->uuid,
				      sizeof(pn->server->uuid));
		else
			diff = pn ? -1 : 1;

		if (diff < 0) {
			list_for_each(p, &pn->server->volumes) {
				s = list_entry(p, struct afs_server_entry, slink);
				if (volume->vid <= s->volume->vid)
					break;
			}
			list_add_tail_rcu(&pn->slink, p);
			n++;
		} else {
			list_del_rcu(&po->slink);
			o++;
		}
	}

	spin_unlock(&volume->cell->vs_lock);
}

/*
 * Detach a volume from the servers it has been using.
 */
void afs_detach_volume_from_servers(struct afs_volume *volume, struct afs_server_list *slist)
{
	unsigned int i;

	if (!slist->attached)
		return;

	spin_lock(&volume->cell->vs_lock);

	for (i = 0; i < slist->nr_servers; i++)
		list_del_rcu(&slist->servers[i].slink);

	slist->attached = false;
	spin_unlock(&volume->cell->vs_lock);
}
+1 −1
Original line number Diff line number Diff line
@@ -77,7 +77,7 @@ static int afs_compare_volume_slists(const struct afs_volume *vol_a,
	lb = rcu_dereference(vol_b->servers);

	for (i = 0; i < AFS_MAXTYPES; i++)
		if (la->vids[i] != lb->vids[i])
		if (vol_a->vids[i] != vol_b->vids[i])
			return 0;

	while (a < la->nr_servers && b < lb->nr_servers) {
Loading