xref: /linux/fs/afs/vlclient.c (revision 94f699c9cdb11b8f53cb70624b69aeae16f26db2)
1ec26815aSDavid Howells /* AFS Volume Location Service client
21da177e4SLinus Torvalds  *
31da177e4SLinus Torvalds  * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
41da177e4SLinus Torvalds  * Written by David Howells (dhowells@redhat.com)
51da177e4SLinus Torvalds  *
61da177e4SLinus Torvalds  * This program is free software; you can redistribute it and/or
71da177e4SLinus Torvalds  * modify it under the terms of the GNU General Public License
81da177e4SLinus Torvalds  * as published by the Free Software Foundation; either version
91da177e4SLinus Torvalds  * 2 of the License, or (at your option) any later version.
101da177e4SLinus Torvalds  */
111da177e4SLinus Torvalds 
125a0e3ad6STejun Heo #include <linux/gfp.h>
131da177e4SLinus Torvalds #include <linux/init.h>
141da177e4SLinus Torvalds #include <linux/sched.h>
154d9df986SDavid Howells #include "afs_fs.h"
161da177e4SLinus Torvalds #include "internal.h"
171da177e4SLinus Torvalds 
1808e0e7c8SDavid Howells /*
19d2ddc776SDavid Howells  * Deliver reply data to a VL.GetEntryByNameU call.
201da177e4SLinus Torvalds  */
21d2ddc776SDavid Howells static int afs_deliver_vl_get_entry_by_name_u(struct afs_call *call)
221da177e4SLinus Torvalds {
23d2ddc776SDavid Howells 	struct afs_uvldbentry__xdr *uvldb;
24d2ddc776SDavid Howells 	struct afs_vldb_entry *entry;
25d2ddc776SDavid Howells 	bool new_only = false;
261fba5868SMarc Dionne 	u32 tmp, nr_servers, vlflags;
27d2ddc776SDavid Howells 	int i, ret;
281da177e4SLinus Torvalds 
29d001648eSDavid Howells 	_enter("");
301da177e4SLinus Torvalds 
31d001648eSDavid Howells 	ret = afs_transfer_reply(call);
32372ee163SDavid Howells 	if (ret < 0)
33372ee163SDavid Howells 		return ret;
341da177e4SLinus Torvalds 
3508e0e7c8SDavid Howells 	/* unmarshall the reply once we've received all of it */
36d2ddc776SDavid Howells 	uvldb = call->buffer;
3797e3043aSDavid Howells 	entry = call->reply[0];
381da177e4SLinus Torvalds 
3945df8462SDavid Howells 	nr_servers = ntohl(uvldb->nServers);
4045df8462SDavid Howells 	if (nr_servers > AFS_NMAXNSERVERS)
4145df8462SDavid Howells 		nr_servers = AFS_NMAXNSERVERS;
4245df8462SDavid Howells 
43d2ddc776SDavid Howells 	for (i = 0; i < ARRAY_SIZE(uvldb->name) - 1; i++)
44d2ddc776SDavid Howells 		entry->name[i] = (u8)ntohl(uvldb->name[i]);
45d2ddc776SDavid Howells 	entry->name[i] = 0;
46d2ddc776SDavid Howells 	entry->name_len = strlen(entry->name);
471da177e4SLinus Torvalds 
48d2ddc776SDavid Howells 	/* If there is a new replication site that we can use, ignore all the
49d2ddc776SDavid Howells 	 * sites that aren't marked as new.
50d2ddc776SDavid Howells 	 */
5145df8462SDavid Howells 	for (i = 0; i < nr_servers; i++) {
52d2ddc776SDavid Howells 		tmp = ntohl(uvldb->serverFlags[i]);
53d2ddc776SDavid Howells 		if (!(tmp & AFS_VLSF_DONTUSE) &&
54d2ddc776SDavid Howells 		    (tmp & AFS_VLSF_NEWREPSITE))
55d2ddc776SDavid Howells 			new_only = true;
564d9df986SDavid Howells 	}
571da177e4SLinus Torvalds 
581fba5868SMarc Dionne 	vlflags = ntohl(uvldb->flags);
5945df8462SDavid Howells 	for (i = 0; i < nr_servers; i++) {
60d2ddc776SDavid Howells 		struct afs_uuid__xdr *xdr;
61d2ddc776SDavid Howells 		struct afs_uuid *uuid;
62d2ddc776SDavid Howells 		int j;
631da177e4SLinus Torvalds 
64d2ddc776SDavid Howells 		tmp = ntohl(uvldb->serverFlags[i]);
65d2ddc776SDavid Howells 		if (tmp & AFS_VLSF_DONTUSE ||
66d2ddc776SDavid Howells 		    (new_only && !(tmp & AFS_VLSF_NEWREPSITE)))
67d2ddc776SDavid Howells 			continue;
681fba5868SMarc Dionne 		if (tmp & AFS_VLSF_RWVOL) {
69d2ddc776SDavid Howells 			entry->fs_mask[i] |= AFS_VOL_VTM_RW;
701fba5868SMarc Dionne 			if (vlflags & AFS_VLF_BACKEXISTS)
711fba5868SMarc Dionne 				entry->fs_mask[i] |= AFS_VOL_VTM_BAK;
721fba5868SMarc Dionne 		}
731da177e4SLinus Torvalds 		if (tmp & AFS_VLSF_ROVOL)
74d2ddc776SDavid Howells 			entry->fs_mask[i] |= AFS_VOL_VTM_RO;
75d2ddc776SDavid Howells 		if (!entry->fs_mask[i])
76d2ddc776SDavid Howells 			continue;
77d2ddc776SDavid Howells 
78d2ddc776SDavid Howells 		xdr = &uvldb->serverNumber[i];
79d2ddc776SDavid Howells 		uuid = (struct afs_uuid *)&entry->fs_server[i];
80d2ddc776SDavid Howells 		uuid->time_low			= xdr->time_low;
81d2ddc776SDavid Howells 		uuid->time_mid			= htons(ntohl(xdr->time_mid));
82d2ddc776SDavid Howells 		uuid->time_hi_and_version	= htons(ntohl(xdr->time_hi_and_version));
83d2ddc776SDavid Howells 		uuid->clock_seq_hi_and_reserved	= (u8)ntohl(xdr->clock_seq_hi_and_reserved);
84d2ddc776SDavid Howells 		uuid->clock_seq_low		= (u8)ntohl(xdr->clock_seq_low);
85d2ddc776SDavid Howells 		for (j = 0; j < 6; j++)
86d2ddc776SDavid Howells 			uuid->node[j] = (u8)ntohl(xdr->node[j]);
87d2ddc776SDavid Howells 
88d2ddc776SDavid Howells 		entry->nr_servers++;
891da177e4SLinus Torvalds 	}
901da177e4SLinus Torvalds 
91d2ddc776SDavid Howells 	for (i = 0; i < AFS_MAXTYPES; i++)
92d2ddc776SDavid Howells 		entry->vid[i] = ntohl(uvldb->volumeId[i]);
931da177e4SLinus Torvalds 
941fba5868SMarc Dionne 	if (vlflags & AFS_VLF_RWEXISTS)
95d2ddc776SDavid Howells 		__set_bit(AFS_VLDB_HAS_RW, &entry->flags);
961fba5868SMarc Dionne 	if (vlflags & AFS_VLF_ROEXISTS)
97d2ddc776SDavid Howells 		__set_bit(AFS_VLDB_HAS_RO, &entry->flags);
981fba5868SMarc Dionne 	if (vlflags & AFS_VLF_BACKEXISTS)
99d2ddc776SDavid Howells 		__set_bit(AFS_VLDB_HAS_BAK, &entry->flags);
1001da177e4SLinus Torvalds 
1011fba5868SMarc Dionne 	if (!(vlflags & (AFS_VLF_RWEXISTS | AFS_VLF_ROEXISTS | AFS_VLF_BACKEXISTS))) {
102d2ddc776SDavid Howells 		entry->error = -ENOMEDIUM;
103d2ddc776SDavid Howells 		__set_bit(AFS_VLDB_QUERY_ERROR, &entry->flags);
104d2ddc776SDavid Howells 	}
105d2ddc776SDavid Howells 
106d2ddc776SDavid Howells 	__set_bit(AFS_VLDB_QUERY_VALID, &entry->flags);
10708e0e7c8SDavid Howells 	_leave(" = 0 [done]");
10808e0e7c8SDavid Howells 	return 0;
109ec26815aSDavid Howells }
1101da177e4SLinus Torvalds 
111d2ddc776SDavid Howells static void afs_destroy_vl_get_entry_by_name_u(struct afs_call *call)
112d2ddc776SDavid Howells {
113d2ddc776SDavid Howells 	kfree(call->reply[0]);
114d2ddc776SDavid Howells 	afs_flat_call_destructor(call);
115d2ddc776SDavid Howells }
116d2ddc776SDavid Howells 
1171da177e4SLinus Torvalds /*
118d2ddc776SDavid Howells  * VL.GetEntryByNameU operation type.
1191da177e4SLinus Torvalds  */
120d2ddc776SDavid Howells static const struct afs_call_type afs_RXVLGetEntryByNameU = {
121d2ddc776SDavid Howells 	.name		= "VL.GetEntryByNameU",
122025db80cSDavid Howells 	.op		= afs_VL_GetEntryByNameU,
123d2ddc776SDavid Howells 	.deliver	= afs_deliver_vl_get_entry_by_name_u,
124d2ddc776SDavid Howells 	.destructor	= afs_destroy_vl_get_entry_by_name_u,
12508e0e7c8SDavid Howells };
12608e0e7c8SDavid Howells 
12708e0e7c8SDavid Howells /*
128d2ddc776SDavid Howells  * Dispatch a get volume entry by name or ID operation (uuid variant).  If the
129d2ddc776SDavid Howells  * volname is a decimal number then it's a volume ID not a volume name.
13008e0e7c8SDavid Howells  */
1310a5143f2SDavid Howells struct afs_vldb_entry *afs_vl_get_entry_by_name_u(struct afs_vl_cursor *vc,
13208e0e7c8SDavid Howells 						  const char *volname,
133d2ddc776SDavid Howells 						  int volnamesz)
13408e0e7c8SDavid Howells {
135d2ddc776SDavid Howells 	struct afs_vldb_entry *entry;
13608e0e7c8SDavid Howells 	struct afs_call *call;
1370a5143f2SDavid Howells 	struct afs_net *net = vc->cell->net;
138d2ddc776SDavid Howells 	size_t reqsz, padsz;
13908e0e7c8SDavid Howells 	__be32 *bp;
14008e0e7c8SDavid Howells 
14108e0e7c8SDavid Howells 	_enter("");
14208e0e7c8SDavid Howells 
14308e0e7c8SDavid Howells 	padsz = (4 - (volnamesz & 3)) & 3;
14408e0e7c8SDavid Howells 	reqsz = 8 + volnamesz + padsz;
14508e0e7c8SDavid Howells 
146d2ddc776SDavid Howells 	entry = kzalloc(sizeof(struct afs_vldb_entry), GFP_KERNEL);
147d2ddc776SDavid Howells 	if (!entry)
148d2ddc776SDavid Howells 		return ERR_PTR(-ENOMEM);
149d2ddc776SDavid Howells 
150d2ddc776SDavid Howells 	call = afs_alloc_flat_call(net, &afs_RXVLGetEntryByNameU, reqsz,
151d2ddc776SDavid Howells 				   sizeof(struct afs_uvldbentry__xdr));
152d2ddc776SDavid Howells 	if (!call) {
153d2ddc776SDavid Howells 		kfree(entry);
154d2ddc776SDavid Howells 		return ERR_PTR(-ENOMEM);
155d2ddc776SDavid Howells 	}
15608e0e7c8SDavid Howells 
1570a5143f2SDavid Howells 	call->key = vc->key;
15897e3043aSDavid Howells 	call->reply[0] = entry;
159d2ddc776SDavid Howells 	call->ret_reply0 = true;
160*94f699c9SDavid Howells 	call->max_lifespan = AFS_VL_MAX_LIFESPAN;
16108e0e7c8SDavid Howells 
162d2ddc776SDavid Howells 	/* Marshall the parameters */
16308e0e7c8SDavid Howells 	bp = call->request;
164d2ddc776SDavid Howells 	*bp++ = htonl(VLGETENTRYBYNAMEU);
16508e0e7c8SDavid Howells 	*bp++ = htonl(volnamesz);
16608e0e7c8SDavid Howells 	memcpy(bp, volname, volnamesz);
16708e0e7c8SDavid Howells 	if (padsz > 0)
16808e0e7c8SDavid Howells 		memset((void *)bp + volnamesz, 0, padsz);
16908e0e7c8SDavid Howells 
170025db80cSDavid Howells 	trace_afs_make_vl_call(call);
1710b9bf381SDavid Howells 	afs_make_call(&vc->ac, call, GFP_KERNEL);
1720b9bf381SDavid Howells 	return (struct afs_vldb_entry *)afs_wait_for_call_to_complete(call, &vc->ac);
17308e0e7c8SDavid Howells }
17408e0e7c8SDavid Howells 
17508e0e7c8SDavid Howells /*
176d2ddc776SDavid Howells  * Deliver reply data to a VL.GetAddrsU call.
177d2ddc776SDavid Howells  *
178d2ddc776SDavid Howells  *	GetAddrsU(IN ListAddrByAttributes *inaddr,
179d2ddc776SDavid Howells  *		  OUT afsUUID *uuidp1,
180d2ddc776SDavid Howells  *		  OUT uint32_t *uniquifier,
181d2ddc776SDavid Howells  *		  OUT uint32_t *nentries,
182d2ddc776SDavid Howells  *		  OUT bulkaddrs *blkaddrs);
18308e0e7c8SDavid Howells  */
184d2ddc776SDavid Howells static int afs_deliver_vl_get_addrs_u(struct afs_call *call)
185d2ddc776SDavid Howells {
186d2ddc776SDavid Howells 	struct afs_addr_list *alist;
187d2ddc776SDavid Howells 	__be32 *bp;
188d2ddc776SDavid Howells 	u32 uniquifier, nentries, count;
189d2ddc776SDavid Howells 	int i, ret;
190d2ddc776SDavid Howells 
19112bdcf33SDavid Howells 	_enter("{%u,%zu/%u}",
19212bdcf33SDavid Howells 	       call->unmarshall, iov_iter_count(call->_iter), call->count);
193d2ddc776SDavid Howells 
194d2ddc776SDavid Howells 	switch (call->unmarshall) {
195d2ddc776SDavid Howells 	case 0:
19612bdcf33SDavid Howells 		afs_extract_to_buf(call,
19712bdcf33SDavid Howells 				   sizeof(struct afs_uuid__xdr) + 3 * sizeof(__be32));
198d2ddc776SDavid Howells 		call->unmarshall++;
199d2ddc776SDavid Howells 
200e690c9e3SGustavo A. R. Silva 		/* Extract the returned uuid, uniquifier, nentries and
201e690c9e3SGustavo A. R. Silva 		 * blkaddrs size */
202e690c9e3SGustavo A. R. Silva 		/* Fall through */
203d2ddc776SDavid Howells 	case 1:
20412bdcf33SDavid Howells 		ret = afs_extract_data(call, true);
205d2ddc776SDavid Howells 		if (ret < 0)
206d2ddc776SDavid Howells 			return ret;
207d2ddc776SDavid Howells 
208d2ddc776SDavid Howells 		bp = call->buffer + sizeof(struct afs_uuid__xdr);
209d2ddc776SDavid Howells 		uniquifier	= ntohl(*bp++);
210d2ddc776SDavid Howells 		nentries	= ntohl(*bp++);
211d2ddc776SDavid Howells 		count		= ntohl(*bp);
212d2ddc776SDavid Howells 
213d2ddc776SDavid Howells 		nentries = min(nentries, count);
214d2ddc776SDavid Howells 		alist = afs_alloc_addrlist(nentries, FS_SERVICE, AFS_FS_PORT);
215d2ddc776SDavid Howells 		if (!alist)
216d2ddc776SDavid Howells 			return -ENOMEM;
217d2ddc776SDavid Howells 		alist->version = uniquifier;
218d2ddc776SDavid Howells 		call->reply[0] = alist;
219d2ddc776SDavid Howells 		call->count = count;
220d2ddc776SDavid Howells 		call->count2 = nentries;
221d2ddc776SDavid Howells 		call->unmarshall++;
222d2ddc776SDavid Howells 
22312bdcf33SDavid Howells 	more_entries:
22412bdcf33SDavid Howells 		count = min(call->count, 4U);
22512bdcf33SDavid Howells 		afs_extract_to_buf(call, count * sizeof(__be32));
22612bdcf33SDavid Howells 
227e690c9e3SGustavo A. R. Silva 		/* Fall through - and extract entries */
228d2ddc776SDavid Howells 	case 2:
22912bdcf33SDavid Howells 		ret = afs_extract_data(call, call->count > 4);
230d2ddc776SDavid Howells 		if (ret < 0)
231d2ddc776SDavid Howells 			return ret;
232d2ddc776SDavid Howells 
233d2ddc776SDavid Howells 		alist = call->reply[0];
234d2ddc776SDavid Howells 		bp = call->buffer;
23512bdcf33SDavid Howells 		count = min(call->count, 4U);
236d2ddc776SDavid Howells 		for (i = 0; i < count; i++)
237d2ddc776SDavid Howells 			if (alist->nr_addrs < call->count2)
238bf99a53cSDavid Howells 				afs_merge_fs_addr4(alist, *bp++, AFS_FS_PORT);
239d2ddc776SDavid Howells 
240d2ddc776SDavid Howells 		call->count -= count;
241d2ddc776SDavid Howells 		if (call->count > 0)
24212bdcf33SDavid Howells 			goto more_entries;
243d2ddc776SDavid Howells 		call->unmarshall++;
244d2ddc776SDavid Howells 		break;
245d2ddc776SDavid Howells 	}
246d2ddc776SDavid Howells 
247d2ddc776SDavid Howells 	_leave(" = 0 [done]");
248d2ddc776SDavid Howells 	return 0;
249d2ddc776SDavid Howells }
250d2ddc776SDavid Howells 
251d2ddc776SDavid Howells static void afs_vl_get_addrs_u_destructor(struct afs_call *call)
252d2ddc776SDavid Howells {
253d2ddc776SDavid Howells 	afs_put_server(call->net, (struct afs_server *)call->reply[0]);
254d2ddc776SDavid Howells 	kfree(call->reply[1]);
255d2ddc776SDavid Howells 	return afs_flat_call_destructor(call);
256d2ddc776SDavid Howells }
257d2ddc776SDavid Howells 
258d2ddc776SDavid Howells /*
259d2ddc776SDavid Howells  * VL.GetAddrsU operation type.
260d2ddc776SDavid Howells  */
261d2ddc776SDavid Howells static const struct afs_call_type afs_RXVLGetAddrsU = {
262d2ddc776SDavid Howells 	.name		= "VL.GetAddrsU",
263025db80cSDavid Howells 	.op		= afs_VL_GetAddrsU,
264d2ddc776SDavid Howells 	.deliver	= afs_deliver_vl_get_addrs_u,
265d2ddc776SDavid Howells 	.destructor	= afs_vl_get_addrs_u_destructor,
266d2ddc776SDavid Howells };
267d2ddc776SDavid Howells 
268d2ddc776SDavid Howells /*
269d2ddc776SDavid Howells  * Dispatch an operation to get the addresses for a server, where the server is
270d2ddc776SDavid Howells  * nominated by UUID.
271d2ddc776SDavid Howells  */
2720a5143f2SDavid Howells struct afs_addr_list *afs_vl_get_addrs_u(struct afs_vl_cursor *vc,
273d2ddc776SDavid Howells 					 const uuid_t *uuid)
2741da177e4SLinus Torvalds {
275d2ddc776SDavid Howells 	struct afs_ListAddrByAttributes__xdr *r;
276d2ddc776SDavid Howells 	const struct afs_uuid *u = (const struct afs_uuid *)uuid;
27708e0e7c8SDavid Howells 	struct afs_call *call;
2780a5143f2SDavid Howells 	struct afs_net *net = vc->cell->net;
2791da177e4SLinus Torvalds 	__be32 *bp;
280d2ddc776SDavid Howells 	int i;
2811da177e4SLinus Torvalds 
28208e0e7c8SDavid Howells 	_enter("");
2831da177e4SLinus Torvalds 
284d2ddc776SDavid Howells 	call = afs_alloc_flat_call(net, &afs_RXVLGetAddrsU,
285d2ddc776SDavid Howells 				   sizeof(__be32) + sizeof(struct afs_ListAddrByAttributes__xdr),
286d2ddc776SDavid Howells 				   sizeof(struct afs_uuid__xdr) + 3 * sizeof(__be32));
28708e0e7c8SDavid Howells 	if (!call)
288d2ddc776SDavid Howells 		return ERR_PTR(-ENOMEM);
2891da177e4SLinus Torvalds 
2900a5143f2SDavid Howells 	call->key = vc->key;
291d2ddc776SDavid Howells 	call->reply[0] = NULL;
292d2ddc776SDavid Howells 	call->ret_reply0 = true;
293*94f699c9SDavid Howells 	call->max_lifespan = AFS_VL_MAX_LIFESPAN;
2941da177e4SLinus Torvalds 
295d2ddc776SDavid Howells 	/* Marshall the parameters */
29608e0e7c8SDavid Howells 	bp = call->request;
297d2ddc776SDavid Howells 	*bp++ = htonl(VLGETADDRSU);
298d2ddc776SDavid Howells 	r = (struct afs_ListAddrByAttributes__xdr *)bp;
299d2ddc776SDavid Howells 	r->Mask		= htonl(AFS_VLADDR_UUID);
300d2ddc776SDavid Howells 	r->ipaddr	= 0;
301d2ddc776SDavid Howells 	r->index	= 0;
302d2ddc776SDavid Howells 	r->spare	= 0;
303d2ddc776SDavid Howells 	r->uuid.time_low			= u->time_low;
304d2ddc776SDavid Howells 	r->uuid.time_mid			= htonl(ntohs(u->time_mid));
305d2ddc776SDavid Howells 	r->uuid.time_hi_and_version		= htonl(ntohs(u->time_hi_and_version));
306d2ddc776SDavid Howells 	r->uuid.clock_seq_hi_and_reserved 	= htonl(u->clock_seq_hi_and_reserved);
307d2ddc776SDavid Howells 	r->uuid.clock_seq_low			= htonl(u->clock_seq_low);
308d2ddc776SDavid Howells 	for (i = 0; i < 6; i++)
309fe342cf7SDavid Howells 		r->uuid.node[i] = htonl(u->node[i]);
3101da177e4SLinus Torvalds 
311025db80cSDavid Howells 	trace_afs_make_vl_call(call);
3120b9bf381SDavid Howells 	afs_make_call(&vc->ac, call, GFP_KERNEL);
3130b9bf381SDavid Howells 	return (struct afs_addr_list *)afs_wait_for_call_to_complete(call, &vc->ac);
314ec26815aSDavid Howells }
315bf99a53cSDavid Howells 
316bf99a53cSDavid Howells /*
317bf99a53cSDavid Howells  * Deliver reply data to an VL.GetCapabilities operation.
318bf99a53cSDavid Howells  */
319bf99a53cSDavid Howells static int afs_deliver_vl_get_capabilities(struct afs_call *call)
320bf99a53cSDavid Howells {
321bf99a53cSDavid Howells 	u32 count;
322bf99a53cSDavid Howells 	int ret;
323bf99a53cSDavid Howells 
32412bdcf33SDavid Howells 	_enter("{%u,%zu/%u}",
32512bdcf33SDavid Howells 	       call->unmarshall, iov_iter_count(call->_iter), call->count);
326bf99a53cSDavid Howells 
327bf99a53cSDavid Howells 	switch (call->unmarshall) {
328bf99a53cSDavid Howells 	case 0:
32912bdcf33SDavid Howells 		afs_extract_to_tmp(call);
330bf99a53cSDavid Howells 		call->unmarshall++;
331bf99a53cSDavid Howells 
332e690c9e3SGustavo A. R. Silva 		/* Fall through - and extract the capabilities word count */
333bf99a53cSDavid Howells 	case 1:
33412bdcf33SDavid Howells 		ret = afs_extract_data(call, true);
335bf99a53cSDavid Howells 		if (ret < 0)
336bf99a53cSDavid Howells 			return ret;
337bf99a53cSDavid Howells 
338bf99a53cSDavid Howells 		count = ntohl(call->tmp);
339bf99a53cSDavid Howells 		call->count = count;
340bf99a53cSDavid Howells 		call->count2 = count;
34112bdcf33SDavid Howells 
342bf99a53cSDavid Howells 		call->unmarshall++;
34312bdcf33SDavid Howells 		afs_extract_discard(call, count * sizeof(__be32));
344bf99a53cSDavid Howells 
345e690c9e3SGustavo A. R. Silva 		/* Fall through - and extract capabilities words */
346bf99a53cSDavid Howells 	case 2:
34712bdcf33SDavid Howells 		ret = afs_extract_data(call, false);
348bf99a53cSDavid Howells 		if (ret < 0)
349bf99a53cSDavid Howells 			return ret;
350bf99a53cSDavid Howells 
351bf99a53cSDavid Howells 		/* TODO: Examine capabilities */
352bf99a53cSDavid Howells 
353bf99a53cSDavid Howells 		call->unmarshall++;
354bf99a53cSDavid Howells 		break;
355bf99a53cSDavid Howells 	}
356bf99a53cSDavid Howells 
357bf99a53cSDavid Howells 	_leave(" = 0 [done]");
358bf99a53cSDavid Howells 	return 0;
359bf99a53cSDavid Howells }
360bf99a53cSDavid Howells 
3613bf0fb6fSDavid Howells static void afs_destroy_vl_get_capabilities(struct afs_call *call)
3623bf0fb6fSDavid Howells {
3633bf0fb6fSDavid Howells 	struct afs_vlserver *server = call->reply[0];
3643bf0fb6fSDavid Howells 
3653bf0fb6fSDavid Howells 	afs_put_vlserver(call->net, server);
3663bf0fb6fSDavid Howells 	afs_flat_call_destructor(call);
3673bf0fb6fSDavid Howells }
3683bf0fb6fSDavid Howells 
369bf99a53cSDavid Howells /*
370bf99a53cSDavid Howells  * VL.GetCapabilities operation type
371bf99a53cSDavid Howells  */
372bf99a53cSDavid Howells static const struct afs_call_type afs_RXVLGetCapabilities = {
373bf99a53cSDavid Howells 	.name		= "VL.GetCapabilities",
374025db80cSDavid Howells 	.op		= afs_VL_GetCapabilities,
375bf99a53cSDavid Howells 	.deliver	= afs_deliver_vl_get_capabilities,
3763bf0fb6fSDavid Howells 	.done		= afs_vlserver_probe_result,
3773bf0fb6fSDavid Howells 	.destructor	= afs_destroy_vl_get_capabilities,
378bf99a53cSDavid Howells };
379bf99a53cSDavid Howells 
380bf99a53cSDavid Howells /*
3810a5143f2SDavid Howells  * Probe a volume server for the capabilities that it supports.  This can
382bf99a53cSDavid Howells  * return up to 196 words.
383bf99a53cSDavid Howells  *
384bf99a53cSDavid Howells  * We use this to probe for service upgrade to determine what the server at the
385bf99a53cSDavid Howells  * other end supports.
386bf99a53cSDavid Howells  */
3870b9bf381SDavid Howells struct afs_call *afs_vl_get_capabilities(struct afs_net *net,
3883bf0fb6fSDavid Howells 					 struct afs_addr_cursor *ac,
3893bf0fb6fSDavid Howells 					 struct key *key,
3903bf0fb6fSDavid Howells 					 struct afs_vlserver *server,
3910b9bf381SDavid Howells 					 unsigned int server_index)
392bf99a53cSDavid Howells {
393bf99a53cSDavid Howells 	struct afs_call *call;
394bf99a53cSDavid Howells 	__be32 *bp;
395bf99a53cSDavid Howells 
396bf99a53cSDavid Howells 	_enter("");
397bf99a53cSDavid Howells 
398bf99a53cSDavid Howells 	call = afs_alloc_flat_call(net, &afs_RXVLGetCapabilities, 1 * 4, 16 * 4);
399bf99a53cSDavid Howells 	if (!call)
4000b9bf381SDavid Howells 		return ERR_PTR(-ENOMEM);
401bf99a53cSDavid Howells 
402bf99a53cSDavid Howells 	call->key = key;
4033bf0fb6fSDavid Howells 	call->reply[0] = afs_get_vlserver(server);
4043bf0fb6fSDavid Howells 	call->reply[1] = (void *)(long)server_index;
4053bf0fb6fSDavid Howells 	call->upgrade = true;
4063bf0fb6fSDavid Howells 	call->want_reply_time = true;
4070b9bf381SDavid Howells 	call->async = true;
408*94f699c9SDavid Howells 	call->max_lifespan = AFS_PROBE_MAX_LIFESPAN;
409bf99a53cSDavid Howells 
410bf99a53cSDavid Howells 	/* marshall the parameters */
411bf99a53cSDavid Howells 	bp = call->request;
412bf99a53cSDavid Howells 	*bp++ = htonl(VLGETCAPABILITIES);
413bf99a53cSDavid Howells 
414bf99a53cSDavid Howells 	/* Can't take a ref on server */
415025db80cSDavid Howells 	trace_afs_make_vl_call(call);
4160b9bf381SDavid Howells 	afs_make_call(ac, call, GFP_KERNEL);
4170b9bf381SDavid Howells 	return call;
418bf99a53cSDavid Howells }
419bf99a53cSDavid Howells 
420bf99a53cSDavid Howells /*
421bf99a53cSDavid Howells  * Deliver reply data to a YFSVL.GetEndpoints call.
422bf99a53cSDavid Howells  *
423bf99a53cSDavid Howells  *	GetEndpoints(IN yfsServerAttributes *attr,
424bf99a53cSDavid Howells  *		     OUT opr_uuid *uuid,
425bf99a53cSDavid Howells  *		     OUT afs_int32 *uniquifier,
426bf99a53cSDavid Howells  *		     OUT endpoints *fsEndpoints,
427bf99a53cSDavid Howells  *		     OUT endpoints *volEndpoints)
428bf99a53cSDavid Howells  */
429bf99a53cSDavid Howells static int afs_deliver_yfsvl_get_endpoints(struct afs_call *call)
430bf99a53cSDavid Howells {
431bf99a53cSDavid Howells 	struct afs_addr_list *alist;
432bf99a53cSDavid Howells 	__be32 *bp;
433bf99a53cSDavid Howells 	u32 uniquifier, size;
434bf99a53cSDavid Howells 	int ret;
435bf99a53cSDavid Howells 
43612bdcf33SDavid Howells 	_enter("{%u,%zu,%u}",
43712bdcf33SDavid Howells 	       call->unmarshall, iov_iter_count(call->_iter), call->count2);
438bf99a53cSDavid Howells 
439bf99a53cSDavid Howells 	switch (call->unmarshall) {
440bf99a53cSDavid Howells 	case 0:
44112bdcf33SDavid Howells 		afs_extract_to_buf(call, sizeof(uuid_t) + 3 * sizeof(__be32));
442bf99a53cSDavid Howells 		call->unmarshall = 1;
443bf99a53cSDavid Howells 
444bf99a53cSDavid Howells 		/* Extract the returned uuid, uniquifier, fsEndpoints count and
445bf99a53cSDavid Howells 		 * either the first fsEndpoint type or the volEndpoints
446bf99a53cSDavid Howells 		 * count if there are no fsEndpoints. */
447e690c9e3SGustavo A. R. Silva 		/* Fall through */
448bf99a53cSDavid Howells 	case 1:
44912bdcf33SDavid Howells 		ret = afs_extract_data(call, true);
450bf99a53cSDavid Howells 		if (ret < 0)
451bf99a53cSDavid Howells 			return ret;
452bf99a53cSDavid Howells 
453bf99a53cSDavid Howells 		bp = call->buffer + sizeof(uuid_t);
454bf99a53cSDavid Howells 		uniquifier	= ntohl(*bp++);
455bf99a53cSDavid Howells 		call->count	= ntohl(*bp++);
456bf99a53cSDavid Howells 		call->count2	= ntohl(*bp); /* Type or next count */
457bf99a53cSDavid Howells 
458bf99a53cSDavid Howells 		if (call->count > YFS_MAXENDPOINTS)
459160cb957SDavid Howells 			return afs_protocol_error(call, -EBADMSG,
460160cb957SDavid Howells 						  afs_eproto_yvl_fsendpt_num);
461bf99a53cSDavid Howells 
462bf99a53cSDavid Howells 		alist = afs_alloc_addrlist(call->count, FS_SERVICE, AFS_FS_PORT);
463bf99a53cSDavid Howells 		if (!alist)
464bf99a53cSDavid Howells 			return -ENOMEM;
465bf99a53cSDavid Howells 		alist->version = uniquifier;
466bf99a53cSDavid Howells 		call->reply[0] = alist;
467bf99a53cSDavid Howells 
468bf99a53cSDavid Howells 		if (call->count == 0)
469bf99a53cSDavid Howells 			goto extract_volendpoints;
470bf99a53cSDavid Howells 
47112bdcf33SDavid Howells 	next_fsendpoint:
472bf99a53cSDavid Howells 		switch (call->count2) {
473bf99a53cSDavid Howells 		case YFS_ENDPOINT_IPV4:
474bf99a53cSDavid Howells 			size = sizeof(__be32) * (1 + 1 + 1);
475bf99a53cSDavid Howells 			break;
476bf99a53cSDavid Howells 		case YFS_ENDPOINT_IPV6:
477bf99a53cSDavid Howells 			size = sizeof(__be32) * (1 + 4 + 1);
478bf99a53cSDavid Howells 			break;
479bf99a53cSDavid Howells 		default:
480160cb957SDavid Howells 			return afs_protocol_error(call, -EBADMSG,
481160cb957SDavid Howells 						  afs_eproto_yvl_fsendpt_type);
482bf99a53cSDavid Howells 		}
483bf99a53cSDavid Howells 
484bf99a53cSDavid Howells 		size += sizeof(__be32);
48512bdcf33SDavid Howells 		afs_extract_to_buf(call, size);
48612bdcf33SDavid Howells 		call->unmarshall = 2;
48712bdcf33SDavid Howells 
488e690c9e3SGustavo A. R. Silva 		/* Fall through - and extract fsEndpoints[] entries */
48912bdcf33SDavid Howells 	case 2:
49012bdcf33SDavid Howells 		ret = afs_extract_data(call, true);
491bf99a53cSDavid Howells 		if (ret < 0)
492bf99a53cSDavid Howells 			return ret;
493bf99a53cSDavid Howells 
494bf99a53cSDavid Howells 		alist = call->reply[0];
495bf99a53cSDavid Howells 		bp = call->buffer;
496bf99a53cSDavid Howells 		switch (call->count2) {
497bf99a53cSDavid Howells 		case YFS_ENDPOINT_IPV4:
498bf99a53cSDavid Howells 			if (ntohl(bp[0]) != sizeof(__be32) * 2)
499160cb957SDavid Howells 				return afs_protocol_error(call, -EBADMSG,
500160cb957SDavid Howells 							  afs_eproto_yvl_fsendpt4_len);
501bf99a53cSDavid Howells 			afs_merge_fs_addr4(alist, bp[1], ntohl(bp[2]));
502bf99a53cSDavid Howells 			bp += 3;
503bf99a53cSDavid Howells 			break;
504bf99a53cSDavid Howells 		case YFS_ENDPOINT_IPV6:
505bf99a53cSDavid Howells 			if (ntohl(bp[0]) != sizeof(__be32) * 5)
506160cb957SDavid Howells 				return afs_protocol_error(call, -EBADMSG,
507160cb957SDavid Howells 							  afs_eproto_yvl_fsendpt6_len);
508bf99a53cSDavid Howells 			afs_merge_fs_addr6(alist, bp + 1, ntohl(bp[5]));
509bf99a53cSDavid Howells 			bp += 6;
510bf99a53cSDavid Howells 			break;
511bf99a53cSDavid Howells 		default:
512160cb957SDavid Howells 			return afs_protocol_error(call, -EBADMSG,
513160cb957SDavid Howells 						  afs_eproto_yvl_fsendpt_type);
514bf99a53cSDavid Howells 		}
515bf99a53cSDavid Howells 
516bf99a53cSDavid Howells 		/* Got either the type of the next entry or the count of
517bf99a53cSDavid Howells 		 * volEndpoints if no more fsEndpoints.
518bf99a53cSDavid Howells 		 */
519fe342cf7SDavid Howells 		call->count2 = ntohl(*bp++);
520bf99a53cSDavid Howells 
521bf99a53cSDavid Howells 		call->count--;
522bf99a53cSDavid Howells 		if (call->count > 0)
52312bdcf33SDavid Howells 			goto next_fsendpoint;
524bf99a53cSDavid Howells 
525bf99a53cSDavid Howells 	extract_volendpoints:
526bf99a53cSDavid Howells 		/* Extract the list of volEndpoints. */
527bf99a53cSDavid Howells 		call->count = call->count2;
528bf99a53cSDavid Howells 		if (!call->count)
529bf99a53cSDavid Howells 			goto end;
530bf99a53cSDavid Howells 		if (call->count > YFS_MAXENDPOINTS)
531160cb957SDavid Howells 			return afs_protocol_error(call, -EBADMSG,
532160cb957SDavid Howells 						  afs_eproto_yvl_vlendpt_type);
533bf99a53cSDavid Howells 
53412bdcf33SDavid Howells 		afs_extract_to_buf(call, 1 * sizeof(__be32));
535bf99a53cSDavid Howells 		call->unmarshall = 3;
536bf99a53cSDavid Howells 
537bf99a53cSDavid Howells 		/* Extract the type of volEndpoints[0].  Normally we would
538bf99a53cSDavid Howells 		 * extract the type of the next endpoint when we extract the
539bf99a53cSDavid Howells 		 * data of the current one, but this is the first...
540bf99a53cSDavid Howells 		 */
541e690c9e3SGustavo A. R. Silva 		/* Fall through */
542bf99a53cSDavid Howells 	case 3:
54312bdcf33SDavid Howells 		ret = afs_extract_data(call, true);
544bf99a53cSDavid Howells 		if (ret < 0)
545bf99a53cSDavid Howells 			return ret;
546bf99a53cSDavid Howells 
547bf99a53cSDavid Howells 		bp = call->buffer;
548bf99a53cSDavid Howells 
54912bdcf33SDavid Howells 	next_volendpoint:
55012bdcf33SDavid Howells 		call->count2 = ntohl(*bp++);
551bf99a53cSDavid Howells 		switch (call->count2) {
552bf99a53cSDavid Howells 		case YFS_ENDPOINT_IPV4:
553bf99a53cSDavid Howells 			size = sizeof(__be32) * (1 + 1 + 1);
554bf99a53cSDavid Howells 			break;
555bf99a53cSDavid Howells 		case YFS_ENDPOINT_IPV6:
556bf99a53cSDavid Howells 			size = sizeof(__be32) * (1 + 4 + 1);
557bf99a53cSDavid Howells 			break;
558bf99a53cSDavid Howells 		default:
559160cb957SDavid Howells 			return afs_protocol_error(call, -EBADMSG,
560160cb957SDavid Howells 						  afs_eproto_yvl_vlendpt_type);
561bf99a53cSDavid Howells 		}
562bf99a53cSDavid Howells 
563bf99a53cSDavid Howells 		if (call->count > 1)
56412bdcf33SDavid Howells 			size += sizeof(__be32); /* Get next type too */
56512bdcf33SDavid Howells 		afs_extract_to_buf(call, size);
56612bdcf33SDavid Howells 		call->unmarshall = 4;
56712bdcf33SDavid Howells 
568e690c9e3SGustavo A. R. Silva 		/* Fall through - and extract volEndpoints[] entries */
56912bdcf33SDavid Howells 	case 4:
57012bdcf33SDavid Howells 		ret = afs_extract_data(call, true);
571bf99a53cSDavid Howells 		if (ret < 0)
572bf99a53cSDavid Howells 			return ret;
573bf99a53cSDavid Howells 
574bf99a53cSDavid Howells 		bp = call->buffer;
575bf99a53cSDavid Howells 		switch (call->count2) {
576bf99a53cSDavid Howells 		case YFS_ENDPOINT_IPV4:
577bf99a53cSDavid Howells 			if (ntohl(bp[0]) != sizeof(__be32) * 2)
578160cb957SDavid Howells 				return afs_protocol_error(call, -EBADMSG,
579160cb957SDavid Howells 							  afs_eproto_yvl_vlendpt4_len);
580bf99a53cSDavid Howells 			bp += 3;
581bf99a53cSDavid Howells 			break;
582bf99a53cSDavid Howells 		case YFS_ENDPOINT_IPV6:
583bf99a53cSDavid Howells 			if (ntohl(bp[0]) != sizeof(__be32) * 5)
584160cb957SDavid Howells 				return afs_protocol_error(call, -EBADMSG,
585160cb957SDavid Howells 							  afs_eproto_yvl_vlendpt6_len);
586bf99a53cSDavid Howells 			bp += 6;
587bf99a53cSDavid Howells 			break;
588bf99a53cSDavid Howells 		default:
589160cb957SDavid Howells 			return afs_protocol_error(call, -EBADMSG,
590160cb957SDavid Howells 						  afs_eproto_yvl_vlendpt_type);
591bf99a53cSDavid Howells 		}
592bf99a53cSDavid Howells 
593bf99a53cSDavid Howells 		/* Got either the type of the next entry or the count of
594bf99a53cSDavid Howells 		 * volEndpoints if no more fsEndpoints.
595bf99a53cSDavid Howells 		 */
596bf99a53cSDavid Howells 		call->count--;
59712bdcf33SDavid Howells 		if (call->count > 0)
59812bdcf33SDavid Howells 			goto next_volendpoint;
599bf99a53cSDavid Howells 
600bf99a53cSDavid Howells 	end:
60112bdcf33SDavid Howells 		afs_extract_discard(call, 0);
602bf99a53cSDavid Howells 		call->unmarshall = 5;
603bf99a53cSDavid Howells 
604e690c9e3SGustavo A. R. Silva 		/* Fall through - Done */
605bf99a53cSDavid Howells 	case 5:
60612bdcf33SDavid Howells 		ret = afs_extract_data(call, false);
607bf99a53cSDavid Howells 		if (ret < 0)
608bf99a53cSDavid Howells 			return ret;
609bf99a53cSDavid Howells 		call->unmarshall = 6;
610bf99a53cSDavid Howells 
611bf99a53cSDavid Howells 	case 6:
612bf99a53cSDavid Howells 		break;
613bf99a53cSDavid Howells 	}
614bf99a53cSDavid Howells 
615bf99a53cSDavid Howells 	alist = call->reply[0];
616bf99a53cSDavid Howells 	_leave(" = 0 [done]");
617bf99a53cSDavid Howells 	return 0;
618bf99a53cSDavid Howells }
619bf99a53cSDavid Howells 
620bf99a53cSDavid Howells /*
621bf99a53cSDavid Howells  * YFSVL.GetEndpoints operation type.
622bf99a53cSDavid Howells  */
623bf99a53cSDavid Howells static const struct afs_call_type afs_YFSVLGetEndpoints = {
624025db80cSDavid Howells 	.name		= "YFSVL.GetEndpoints",
625025db80cSDavid Howells 	.op		= afs_YFSVL_GetEndpoints,
626bf99a53cSDavid Howells 	.deliver	= afs_deliver_yfsvl_get_endpoints,
627bf99a53cSDavid Howells 	.destructor	= afs_vl_get_addrs_u_destructor,
628bf99a53cSDavid Howells };
629bf99a53cSDavid Howells 
630bf99a53cSDavid Howells /*
631bf99a53cSDavid Howells  * Dispatch an operation to get the addresses for a server, where the server is
632bf99a53cSDavid Howells  * nominated by UUID.
633bf99a53cSDavid Howells  */
6340a5143f2SDavid Howells struct afs_addr_list *afs_yfsvl_get_endpoints(struct afs_vl_cursor *vc,
635bf99a53cSDavid Howells 					      const uuid_t *uuid)
636bf99a53cSDavid Howells {
637bf99a53cSDavid Howells 	struct afs_call *call;
6380a5143f2SDavid Howells 	struct afs_net *net = vc->cell->net;
639bf99a53cSDavid Howells 	__be32 *bp;
640bf99a53cSDavid Howells 
641bf99a53cSDavid Howells 	_enter("");
642bf99a53cSDavid Howells 
643bf99a53cSDavid Howells 	call = afs_alloc_flat_call(net, &afs_YFSVLGetEndpoints,
644bf99a53cSDavid Howells 				   sizeof(__be32) * 2 + sizeof(*uuid),
645bf99a53cSDavid Howells 				   sizeof(struct in6_addr) + sizeof(__be32) * 3);
646bf99a53cSDavid Howells 	if (!call)
647bf99a53cSDavid Howells 		return ERR_PTR(-ENOMEM);
648bf99a53cSDavid Howells 
6490a5143f2SDavid Howells 	call->key = vc->key;
650bf99a53cSDavid Howells 	call->reply[0] = NULL;
651bf99a53cSDavid Howells 	call->ret_reply0 = true;
652*94f699c9SDavid Howells 	call->max_lifespan = AFS_VL_MAX_LIFESPAN;
653bf99a53cSDavid Howells 
654bf99a53cSDavid Howells 	/* Marshall the parameters */
655bf99a53cSDavid Howells 	bp = call->request;
656bf99a53cSDavid Howells 	*bp++ = htonl(YVLGETENDPOINTS);
657bf99a53cSDavid Howells 	*bp++ = htonl(YFS_SERVER_UUID);
658bf99a53cSDavid Howells 	memcpy(bp, uuid, sizeof(*uuid)); /* Type opr_uuid */
659bf99a53cSDavid Howells 
660025db80cSDavid Howells 	trace_afs_make_vl_call(call);
6610b9bf381SDavid Howells 	afs_make_call(&vc->ac, call, GFP_KERNEL);
6620b9bf381SDavid Howells 	return (struct afs_addr_list *)afs_wait_for_call_to_complete(call, &vc->ac);
663bf99a53cSDavid Howells }
664