162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/* AFS fileserver list management.
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright (C) 2017 Red Hat, Inc. All Rights Reserved.
562306a36Sopenharmony_ci * Written by David Howells (dhowells@redhat.com)
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/kernel.h>
962306a36Sopenharmony_ci#include <linux/slab.h>
1062306a36Sopenharmony_ci#include "internal.h"
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_civoid afs_put_serverlist(struct afs_net *net, struct afs_server_list *slist)
1362306a36Sopenharmony_ci{
1462306a36Sopenharmony_ci	int i;
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci	if (slist && refcount_dec_and_test(&slist->usage)) {
1762306a36Sopenharmony_ci		for (i = 0; i < slist->nr_servers; i++)
1862306a36Sopenharmony_ci			afs_unuse_server(net, slist->servers[i].server,
1962306a36Sopenharmony_ci					 afs_server_trace_put_slist);
2062306a36Sopenharmony_ci		kfree_rcu(slist, rcu);
2162306a36Sopenharmony_ci	}
2262306a36Sopenharmony_ci}
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci/*
2562306a36Sopenharmony_ci * Build a server list from a VLDB record.
2662306a36Sopenharmony_ci */
2762306a36Sopenharmony_cistruct afs_server_list *afs_alloc_server_list(struct afs_cell *cell,
2862306a36Sopenharmony_ci					      struct key *key,
2962306a36Sopenharmony_ci					      struct afs_vldb_entry *vldb,
3062306a36Sopenharmony_ci					      u8 type_mask)
3162306a36Sopenharmony_ci{
3262306a36Sopenharmony_ci	struct afs_server_list *slist;
3362306a36Sopenharmony_ci	struct afs_server *server;
3462306a36Sopenharmony_ci	int ret = -ENOMEM, nr_servers = 0, i, j;
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	for (i = 0; i < vldb->nr_servers; i++)
3762306a36Sopenharmony_ci		if (vldb->fs_mask[i] & type_mask)
3862306a36Sopenharmony_ci			nr_servers++;
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci	slist = kzalloc(struct_size(slist, servers, nr_servers), GFP_KERNEL);
4162306a36Sopenharmony_ci	if (!slist)
4262306a36Sopenharmony_ci		goto error;
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	refcount_set(&slist->usage, 1);
4562306a36Sopenharmony_ci	rwlock_init(&slist->lock);
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	for (i = 0; i < AFS_MAXTYPES; i++)
4862306a36Sopenharmony_ci		slist->vids[i] = vldb->vid[i];
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	/* Make sure a records exists for each server in the list. */
5162306a36Sopenharmony_ci	for (i = 0; i < vldb->nr_servers; i++) {
5262306a36Sopenharmony_ci		if (!(vldb->fs_mask[i] & type_mask))
5362306a36Sopenharmony_ci			continue;
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci		server = afs_lookup_server(cell, key, &vldb->fs_server[i],
5662306a36Sopenharmony_ci					   vldb->addr_version[i]);
5762306a36Sopenharmony_ci		if (IS_ERR(server)) {
5862306a36Sopenharmony_ci			ret = PTR_ERR(server);
5962306a36Sopenharmony_ci			if (ret == -ENOENT ||
6062306a36Sopenharmony_ci			    ret == -ENOMEDIUM)
6162306a36Sopenharmony_ci				continue;
6262306a36Sopenharmony_ci			goto error_2;
6362306a36Sopenharmony_ci		}
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci		/* Insertion-sort by UUID */
6662306a36Sopenharmony_ci		for (j = 0; j < slist->nr_servers; j++)
6762306a36Sopenharmony_ci			if (memcmp(&slist->servers[j].server->uuid,
6862306a36Sopenharmony_ci				   &server->uuid,
6962306a36Sopenharmony_ci				   sizeof(server->uuid)) >= 0)
7062306a36Sopenharmony_ci				break;
7162306a36Sopenharmony_ci		if (j < slist->nr_servers) {
7262306a36Sopenharmony_ci			if (slist->servers[j].server == server) {
7362306a36Sopenharmony_ci				afs_put_server(cell->net, server,
7462306a36Sopenharmony_ci					       afs_server_trace_put_slist_isort);
7562306a36Sopenharmony_ci				continue;
7662306a36Sopenharmony_ci			}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci			memmove(slist->servers + j + 1,
7962306a36Sopenharmony_ci				slist->servers + j,
8062306a36Sopenharmony_ci				(slist->nr_servers - j) * sizeof(struct afs_server_entry));
8162306a36Sopenharmony_ci		}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci		slist->servers[j].server = server;
8462306a36Sopenharmony_ci		slist->nr_servers++;
8562306a36Sopenharmony_ci	}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	if (slist->nr_servers == 0) {
8862306a36Sopenharmony_ci		ret = -EDESTADDRREQ;
8962306a36Sopenharmony_ci		goto error_2;
9062306a36Sopenharmony_ci	}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	return slist;
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_cierror_2:
9562306a36Sopenharmony_ci	afs_put_serverlist(cell->net, slist);
9662306a36Sopenharmony_cierror:
9762306a36Sopenharmony_ci	return ERR_PTR(ret);
9862306a36Sopenharmony_ci}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci/*
10162306a36Sopenharmony_ci * Copy the annotations from an old server list to its potential replacement.
10262306a36Sopenharmony_ci */
10362306a36Sopenharmony_cibool afs_annotate_server_list(struct afs_server_list *new,
10462306a36Sopenharmony_ci			      struct afs_server_list *old)
10562306a36Sopenharmony_ci{
10662306a36Sopenharmony_ci	struct afs_server *cur;
10762306a36Sopenharmony_ci	int i, j;
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	if (old->nr_servers != new->nr_servers)
11062306a36Sopenharmony_ci		goto changed;
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	for (i = 0; i < old->nr_servers; i++)
11362306a36Sopenharmony_ci		if (old->servers[i].server != new->servers[i].server)
11462306a36Sopenharmony_ci			goto changed;
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	return false;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_cichanged:
11962306a36Sopenharmony_ci	/* Maintain the same preferred server as before if possible. */
12062306a36Sopenharmony_ci	cur = old->servers[old->preferred].server;
12162306a36Sopenharmony_ci	for (j = 0; j < new->nr_servers; j++) {
12262306a36Sopenharmony_ci		if (new->servers[j].server == cur) {
12362306a36Sopenharmony_ci			new->preferred = j;
12462306a36Sopenharmony_ci			break;
12562306a36Sopenharmony_ci		}
12662306a36Sopenharmony_ci	}
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	return true;
12962306a36Sopenharmony_ci}
130