Commit 72904d7b authored by David Howells's avatar David Howells
Browse files

rxrpc, afs: Allow afs to pin rxrpc_peer objects



Change rxrpc's API such that:

 (1) A new function, rxrpc_kernel_lookup_peer(), is provided to look up an
     rxrpc_peer record for a remote address and a corresponding function,
     rxrpc_kernel_put_peer(), is provided to dispose of it again.

 (2) When setting up a call, the rxrpc_peer object used during a call is
     now passed in rather than being set up by rxrpc_connect_call().  For
     afs, this meenat passing it to rxrpc_kernel_begin_call() rather than
     the full address (the service ID then has to be passed in as a
     separate parameter).

 (3) A new function, rxrpc_kernel_remote_addr(), is added so that afs can
     get a pointer to the transport address for display purposed, and
     another, rxrpc_kernel_remote_srx(), to gain a pointer to the full
     rxrpc address.

 (4) The function to retrieve the RTT from a call, rxrpc_kernel_get_srtt(),
     is then altered to take a peer.  This now returns the RTT or -1 if
     there are insufficient samples.

 (5) Rename rxrpc_kernel_get_peer() to rxrpc_kernel_call_get_peer().

 (6) Provide a new function, rxrpc_kernel_get_peer(), to get a ref on a
     peer the caller already has.

This allows the afs filesystem to pin the rxrpc_peer records that it is
using, allowing faster lookups and pointer comparisons rather than
comparing sockaddr_rxrpc contents.  It also makes it easier to get hold of
the RTT.  The following changes are made to afs:

 (1) The addr_list struct's addrs[] elements now hold a peer struct pointer
     and a service ID rather than a sockaddr_rxrpc.

 (2) When displaying the transport address, rxrpc_kernel_remote_addr() is
     used.

 (3) The port arg is removed from afs_alloc_addrlist() since it's always
     overridden.

 (4) afs_merge_fs_addr4() and afs_merge_fs_addr6() do peer lookup and may
     now return an error that must be handled.

 (5) afs_find_server() now takes a peer pointer to specify the address.

 (6) afs_find_server(), afs_compare_fs_alists() and afs_merge_fs_addr[46]{}
     now do peer pointer comparison rather than address comparison.

Signed-off-by: default avatarDavid Howells <dhowells@redhat.com>
cc: Marc Dionne <marc.dionne@auristor.com>
cc: linux-afs@lists.infradead.org
parent 07f3502b
Loading
Loading
Loading
Loading
+66 −59
Original line number Diff line number Diff line
@@ -13,26 +13,33 @@
#include "internal.h"
#include "afs_fs.h"

static void afs_free_addrlist(struct rcu_head *rcu)
{
	struct afs_addr_list *alist = container_of(rcu, struct afs_addr_list, rcu);
	unsigned int i;

	for (i = 0; i < alist->nr_addrs; i++)
		rxrpc_kernel_put_peer(alist->addrs[i].peer);
}

/*
 * Release an address list.
 */
void afs_put_addrlist(struct afs_addr_list *alist)
{
	if (alist && refcount_dec_and_test(&alist->usage))
		kfree_rcu(alist, rcu);
		call_rcu(&alist->rcu, afs_free_addrlist);
}

/*
 * Allocate an address list.
 */
struct afs_addr_list *afs_alloc_addrlist(unsigned int nr,
					 unsigned short service,
					 unsigned short port)
struct afs_addr_list *afs_alloc_addrlist(unsigned int nr, u16 service_id)
{
	struct afs_addr_list *alist;
	unsigned int i;

	_enter("%u,%u,%u", nr, service, port);
	_enter("%u,%u", nr, service_id);

	if (nr > AFS_MAX_ADDRESSES)
		nr = AFS_MAX_ADDRESSES;
@@ -44,16 +51,8 @@ struct afs_addr_list *afs_alloc_addrlist(unsigned int nr,
	refcount_set(&alist->usage, 1);
	alist->max_addrs = nr;

	for (i = 0; i < nr; i++) {
		struct sockaddr_rxrpc *srx = &alist->addrs[i].srx;
		srx->srx_family			= AF_RXRPC;
		srx->srx_service		= service;
		srx->transport_type		= SOCK_DGRAM;
		srx->transport_len		= sizeof(srx->transport.sin6);
		srx->transport.sin6.sin6_family	= AF_INET6;
		srx->transport.sin6.sin6_port	= htons(port);
	}

	for (i = 0; i < nr; i++)
		alist->addrs[i].service_id = service_id;
	return alist;
}

@@ -126,7 +125,7 @@ struct afs_vlserver_list *afs_parse_text_addrs(struct afs_net *net,
	if (!vllist->servers[0].server)
		goto error_vl;

	alist = afs_alloc_addrlist(nr, service, AFS_VL_PORT);
	alist = afs_alloc_addrlist(nr, service);
	if (!alist)
		goto error;

@@ -197,9 +196,11 @@ struct afs_vlserver_list *afs_parse_text_addrs(struct afs_net *net,
		}

		if (family == AF_INET)
			afs_merge_fs_addr4(alist, x[0], xport);
			ret = afs_merge_fs_addr4(net, alist, x[0], xport);
		else
			afs_merge_fs_addr6(alist, x, xport);
			ret = afs_merge_fs_addr6(net, alist, x, xport);
		if (ret < 0)
			goto error;

	} while (p < end);

@@ -271,25 +272,33 @@ struct afs_vlserver_list *afs_dns_query(struct afs_cell *cell, time64_t *_expiry
/*
 * Merge an IPv4 entry into a fileserver address list.
 */
void afs_merge_fs_addr4(struct afs_addr_list *alist, __be32 xdr, u16 port)
int afs_merge_fs_addr4(struct afs_net *net, struct afs_addr_list *alist,
		       __be32 xdr, u16 port)
{
	struct sockaddr_rxrpc *srx;
	u32 addr = ntohl(xdr);
	struct sockaddr_rxrpc srx;
	struct rxrpc_peer *peer;
	int i;

	if (alist->nr_addrs >= alist->max_addrs)
		return;
		return 0;

	for (i = 0; i < alist->nr_ipv4; i++) {
		struct sockaddr_in *a = &alist->addrs[i].srx.transport.sin;
		u32 a_addr = ntohl(a->sin_addr.s_addr);
		u16 a_port = ntohs(a->sin_port);
	srx.srx_family = AF_RXRPC;
	srx.transport_type = SOCK_DGRAM;
	srx.transport_len = sizeof(srx.transport.sin);
	srx.transport.sin.sin_family = AF_INET;
	srx.transport.sin.sin_port = htons(port);
	srx.transport.sin.sin_addr.s_addr = xdr;

		if (addr == a_addr && port == a_port)
			return;
		if (addr == a_addr && port < a_port)
			break;
		if (addr < a_addr)
	peer = rxrpc_kernel_lookup_peer(net->socket, &srx, GFP_KERNEL);
	if (!peer)
		return -ENOMEM;

	for (i = 0; i < alist->nr_ipv4; i++) {
		if (peer == alist->addrs[i].peer) {
			rxrpc_kernel_put_peer(peer);
			return 0;
		}
		if (peer <= alist->addrs[i].peer)
			break;
	}

@@ -298,38 +307,42 @@ void afs_merge_fs_addr4(struct afs_addr_list *alist, __be32 xdr, u16 port)
			alist->addrs + i,
			sizeof(alist->addrs[0]) * (alist->nr_addrs - i));

	srx = &alist->addrs[i].srx;
	srx->srx_family = AF_RXRPC;
	srx->transport_type = SOCK_DGRAM;
	srx->transport_len = sizeof(srx->transport.sin);
	srx->transport.sin.sin_family = AF_INET;
	srx->transport.sin.sin_port = htons(port);
	srx->transport.sin.sin_addr.s_addr = xdr;
	alist->addrs[i].peer = peer;
	alist->nr_ipv4++;
	alist->nr_addrs++;
	return 0;
}

/*
 * Merge an IPv6 entry into a fileserver address list.
 */
void afs_merge_fs_addr6(struct afs_addr_list *alist, __be32 *xdr, u16 port)
int afs_merge_fs_addr6(struct afs_net *net, struct afs_addr_list *alist,
		       __be32 *xdr, u16 port)
{
	struct sockaddr_rxrpc *srx;
	int i, diff;
	struct sockaddr_rxrpc srx;
	struct rxrpc_peer *peer;
	int i;

	if (alist->nr_addrs >= alist->max_addrs)
		return;
		return 0;

	for (i = alist->nr_ipv4; i < alist->nr_addrs; i++) {
		struct sockaddr_in6 *a = &alist->addrs[i].srx.transport.sin6;
		u16 a_port = ntohs(a->sin6_port);
	srx.srx_family = AF_RXRPC;
	srx.transport_type = SOCK_DGRAM;
	srx.transport_len = sizeof(srx.transport.sin6);
	srx.transport.sin6.sin6_family = AF_INET6;
	srx.transport.sin6.sin6_port = htons(port);
	memcpy(&srx.transport.sin6.sin6_addr, xdr, 16);

		diff = memcmp(xdr, &a->sin6_addr, 16);
		if (diff == 0 && port == a_port)
			return;
		if (diff == 0 && port < a_port)
			break;
		if (diff < 0)
	peer = rxrpc_kernel_lookup_peer(net->socket, &srx, GFP_KERNEL);
	if (!peer)
		return -ENOMEM;

	for (i = alist->nr_ipv4; i < alist->nr_addrs; i++) {
		if (peer == alist->addrs[i].peer) {
			rxrpc_kernel_put_peer(peer);
			return 0;
		}
		if (peer <= alist->addrs[i].peer)
			break;
	}

@@ -337,15 +350,9 @@ void afs_merge_fs_addr6(struct afs_addr_list *alist, __be32 *xdr, u16 port)
		memmove(alist->addrs + i + 1,
			alist->addrs + i,
			sizeof(alist->addrs[0]) * (alist->nr_addrs - i));

	srx = &alist->addrs[i].srx;
	srx->srx_family = AF_RXRPC;
	srx->transport_type = SOCK_DGRAM;
	srx->transport_len = sizeof(srx->transport.sin6);
	srx->transport.sin6.sin6_family = AF_INET6;
	srx->transport.sin6.sin6_port = htons(port);
	memcpy(&srx->transport.sin6.sin6_addr, xdr, 16);
	alist->addrs[i].peer = peer;
	alist->nr_addrs++;
	return 0;
}

/*
+3 −2
Original line number Diff line number Diff line
@@ -146,10 +146,11 @@ static int afs_find_cm_server_by_peer(struct afs_call *call)
{
	struct sockaddr_rxrpc srx;
	struct afs_server *server;
	struct rxrpc_peer *peer;

	rxrpc_kernel_get_peer(call->net->socket, call->rxcall, &srx);
	peer = rxrpc_kernel_get_call_peer(call->net->socket, call->rxcall);

	server = afs_find_server(call->net, &srx);
	server = afs_find_server(call->net, peer);
	if (!server) {
		trace_afs_cm_no_server(call, &srx);
		return 0;
+6 −5
Original line number Diff line number Diff line
@@ -101,6 +101,7 @@ static void afs_fs_probe_not_done(struct afs_net *net,
void afs_fileserver_probe_result(struct afs_call *call)
{
	struct afs_addr_list *alist = call->alist;
	struct afs_address *addr = &alist->addrs[call->addr_ix];
	struct afs_server *server = call->server;
	unsigned int index = call->addr_ix;
	unsigned int rtt_us = 0, cap0;
@@ -153,12 +154,12 @@ void afs_fileserver_probe_result(struct afs_call *call)
	if (call->service_id == YFS_FS_SERVICE) {
		server->probe.is_yfs = true;
		set_bit(AFS_SERVER_FL_IS_YFS, &server->flags);
		alist->addrs[index].srx.srx_service = call->service_id;
		addr->service_id = call->service_id;
	} else {
		server->probe.not_yfs = true;
		if (!server->probe.is_yfs) {
			clear_bit(AFS_SERVER_FL_IS_YFS, &server->flags);
			alist->addrs[index].srx.srx_service = call->service_id;
			addr->service_id = call->service_id;
		}
		cap0 = ntohl(call->tmp);
		if (cap0 & AFS3_VICED_CAPABILITY_64BITFILES)
@@ -167,7 +168,7 @@ void afs_fileserver_probe_result(struct afs_call *call)
			clear_bit(AFS_SERVER_FL_HAS_FS64, &server->flags);
	}

	rxrpc_kernel_get_srtt(call->net->socket, call->rxcall, &rtt_us);
	rtt_us = rxrpc_kernel_get_srtt(addr->peer);
	if (rtt_us < server->probe.rtt) {
		server->probe.rtt = rtt_us;
		server->rtt = rtt_us;
@@ -181,8 +182,8 @@ void afs_fileserver_probe_result(struct afs_call *call)
out:
	spin_unlock(&server->probe_lock);

	_debug("probe %pU [%u] %pISpc rtt=%u ret=%d",
	       &server->uuid, index, &alist->addrs[index].srx.transport,
	_debug("probe %pU [%u] %pISpc rtt=%d ret=%d",
	       &server->uuid, index, rxrpc_kernel_remote_addr(alist->addrs[index].peer),
	       rtt_us, ret);

	return afs_done_one_fs_probe(call->net, server);
+14 −12
Original line number Diff line number Diff line
@@ -72,6 +72,11 @@ enum afs_call_state {
	AFS_CALL_COMPLETE,		/* Completed or failed */
};

struct afs_address {
	struct rxrpc_peer	*peer;
	u16			service_id;
};

/*
 * List of server addresses.
 */
@@ -87,9 +92,7 @@ struct afs_addr_list {
	enum dns_lookup_status	status:8;
	unsigned long		failed;		/* Mask of addrs that failed locally/ICMP */
	unsigned long		responded;	/* Mask of addrs that responded */
	struct {
		struct sockaddr_rxrpc	srx;
	} addrs[] __counted_by(max_addrs);
	struct afs_address	addrs[] __counted_by(max_addrs);
#define AFS_MAX_ADDRESSES ((unsigned int)(sizeof(unsigned long) * 8))
};

@@ -420,7 +423,7 @@ struct afs_vlserver {
	atomic_t		probe_outstanding;
	spinlock_t		probe_lock;
	struct {
		unsigned int	rtt;		/* RTT in uS */
		unsigned int	rtt;		/* Best RTT in uS (or UINT_MAX) */
		u32		abort_code;
		short		error;
		unsigned short	flags;
@@ -537,7 +540,7 @@ struct afs_server {
	atomic_t		probe_outstanding;
	spinlock_t		probe_lock;
	struct {
		unsigned int	rtt;		/* RTT in uS */
		unsigned int	rtt;		/* Best RTT in uS (or UINT_MAX) */
		u32		abort_code;
		short		error;
		bool		responded:1;
@@ -964,9 +967,7 @@ static inline struct afs_addr_list *afs_get_addrlist(struct afs_addr_list *alist
		refcount_inc(&alist->usage);
	return alist;
}
extern struct afs_addr_list *afs_alloc_addrlist(unsigned int,
						unsigned short,
						unsigned short);
extern struct afs_addr_list *afs_alloc_addrlist(unsigned int nr, u16 service_id);
extern void afs_put_addrlist(struct afs_addr_list *);
extern struct afs_vlserver_list *afs_parse_text_addrs(struct afs_net *,
						      const char *, size_t, char,
@@ -977,8 +978,10 @@ extern struct afs_vlserver_list *afs_dns_query(struct afs_cell *, time64_t *);
extern bool afs_iterate_addresses(struct afs_addr_cursor *);
extern int afs_end_cursor(struct afs_addr_cursor *);

extern void afs_merge_fs_addr4(struct afs_addr_list *, __be32, u16);
extern void afs_merge_fs_addr6(struct afs_addr_list *, __be32 *, u16);
extern int afs_merge_fs_addr4(struct afs_net *net, struct afs_addr_list *addr,
			      __be32 xdr, u16 port);
extern int afs_merge_fs_addr6(struct afs_net *net, struct afs_addr_list *addr,
			      __be32 *xdr, u16 port);

/*
 * callback.c
@@ -1405,8 +1408,7 @@ extern void __exit afs_clean_up_permit_cache(void);
 */
extern spinlock_t afs_server_peer_lock;

extern struct afs_server *afs_find_server(struct afs_net *,
					  const struct sockaddr_rxrpc *);
extern struct afs_server *afs_find_server(struct afs_net *, const struct rxrpc_peer *);
extern struct afs_server *afs_find_server_by_uuid(struct afs_net *, const uuid_t *);
extern struct afs_server *afs_lookup_server(struct afs_cell *, struct key *, const uuid_t *, u32);
extern struct afs_server *afs_get_server(struct afs_server *, enum afs_server_trace);
+5 −4
Original line number Diff line number Diff line
@@ -307,7 +307,7 @@ static int afs_proc_cell_vlservers_show(struct seq_file *m, void *v)
		for (i = 0; i < alist->nr_addrs; i++)
			seq_printf(m, " %c %pISpc\n",
				   alist->preferred == i ? '>' : '-',
				   &alist->addrs[i].srx.transport);
				   rxrpc_kernel_remote_addr(alist->addrs[i].peer));
	}
	seq_printf(m, " info: fl=%lx rtt=%d\n", vlserver->flags, vlserver->rtt);
	seq_printf(m, " probe: fl=%x e=%d ac=%d out=%d\n",
@@ -398,9 +398,10 @@ static int afs_proc_servers_show(struct seq_file *m, void *v)
	seq_printf(m, "  - ALIST v=%u rsp=%lx f=%lx\n",
		   alist->version, alist->responded, alist->failed);
	for (i = 0; i < alist->nr_addrs; i++)
		seq_printf(m, "    [%x] %pISpc%s\n",
			   i, &alist->addrs[i].srx.transport,
			   alist->preferred == i ? "*" : "");
		seq_printf(m, "    [%x] %pISpc%s rtt=%d\n",
			   i, rxrpc_kernel_remote_addr(alist->addrs[i].peer),
			   alist->preferred == i ? "*" : "",
			   rxrpc_kernel_get_srtt(alist->addrs[i].peer));
	return 0;
}

Loading