mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
synced 2024-12-28 16:52:18 +00:00
8942f9db28
Make FS.FetchData and YFS.FetchData an asynchronous operation in that the request is queued in AF_RXRPC and then we return to the caller rather than waiting. Processing of the returning packets is then done inline if it's a synchronous VFS/VM call (readdir, read_folio, sync DIO, prep for write) or offloaded to a workqueue if asynchronous VM calls (eg. readahead, async DIO). This reduces the chain of workqueues invoking workqueues and cuts out some of the overhead, driving rxrpc data extraction and netfslib read collection from a thread that's going to block to completion anyway if possible. The ->done() call op is also split with ->immediate_cancel() handling the cancellation on failure to begin the call and ->done() handling the rest. This means that the AFS async FetchData code doesn't try to terminate the netfs subrequest twice. Signed-off-by: David Howells <dhowells@redhat.com> Link: https://lore.kernel.org/r/20241108173236.1382366-28-dhowells@redhat.com cc: Marc Dionne <marc.dionne@auristor.com> cc: linux-afs@lists.infradead.org Signed-off-by: Christian Brauner <brauner@kernel.org>
794 lines
20 KiB
C
794 lines
20 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/* AFS Volume Location Service client
|
|
*
|
|
* Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
|
|
* Written by David Howells (dhowells@redhat.com)
|
|
*/
|
|
|
|
#include <linux/gfp.h>
|
|
#include <linux/init.h>
|
|
#include <linux/sched.h>
|
|
#include "afs_fs.h"
|
|
#include "internal.h"
|
|
|
|
/*
|
|
* Deliver reply data to a VL.GetEntryByNameU call.
|
|
*/
|
|
static int afs_deliver_vl_get_entry_by_name_u(struct afs_call *call)
|
|
{
|
|
struct afs_uvldbentry__xdr *uvldb;
|
|
struct afs_vldb_entry *entry;
|
|
u32 nr_servers, vlflags;
|
|
int i, ret;
|
|
|
|
_enter("");
|
|
|
|
ret = afs_transfer_reply(call);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
/* unmarshall the reply once we've received all of it */
|
|
uvldb = call->buffer;
|
|
entry = call->ret_vldb;
|
|
|
|
nr_servers = ntohl(uvldb->nServers);
|
|
if (nr_servers > AFS_NMAXNSERVERS)
|
|
nr_servers = AFS_NMAXNSERVERS;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(uvldb->name) - 1; i++)
|
|
entry->name[i] = (u8)ntohl(uvldb->name[i]);
|
|
entry->name[i] = 0;
|
|
entry->name_len = strlen(entry->name);
|
|
|
|
vlflags = ntohl(uvldb->flags);
|
|
for (i = 0; i < nr_servers; i++) {
|
|
struct afs_uuid__xdr *xdr;
|
|
struct afs_uuid *uuid;
|
|
u32 tmp = ntohl(uvldb->serverFlags[i]);
|
|
int j;
|
|
int n = entry->nr_servers;
|
|
|
|
if (tmp & AFS_VLSF_RWVOL) {
|
|
entry->fs_mask[n] |= AFS_VOL_VTM_RW;
|
|
if (vlflags & AFS_VLF_BACKEXISTS)
|
|
entry->fs_mask[n] |= AFS_VOL_VTM_BAK;
|
|
}
|
|
if (tmp & AFS_VLSF_ROVOL)
|
|
entry->fs_mask[n] |= AFS_VOL_VTM_RO;
|
|
if (!entry->fs_mask[n])
|
|
continue;
|
|
|
|
xdr = &uvldb->serverNumber[i];
|
|
uuid = (struct afs_uuid *)&entry->fs_server[n];
|
|
uuid->time_low = xdr->time_low;
|
|
uuid->time_mid = htons(ntohl(xdr->time_mid));
|
|
uuid->time_hi_and_version = htons(ntohl(xdr->time_hi_and_version));
|
|
uuid->clock_seq_hi_and_reserved = (u8)ntohl(xdr->clock_seq_hi_and_reserved);
|
|
uuid->clock_seq_low = (u8)ntohl(xdr->clock_seq_low);
|
|
for (j = 0; j < 6; j++)
|
|
uuid->node[j] = (u8)ntohl(xdr->node[j]);
|
|
|
|
entry->vlsf_flags[n] = tmp;
|
|
entry->addr_version[n] = ntohl(uvldb->serverUnique[i]);
|
|
entry->nr_servers++;
|
|
}
|
|
|
|
for (i = 0; i < AFS_MAXTYPES; i++)
|
|
entry->vid[i] = ntohl(uvldb->volumeId[i]);
|
|
|
|
if (vlflags & AFS_VLF_RWEXISTS)
|
|
__set_bit(AFS_VLDB_HAS_RW, &entry->flags);
|
|
if (vlflags & AFS_VLF_ROEXISTS)
|
|
__set_bit(AFS_VLDB_HAS_RO, &entry->flags);
|
|
if (vlflags & AFS_VLF_BACKEXISTS)
|
|
__set_bit(AFS_VLDB_HAS_BAK, &entry->flags);
|
|
|
|
if (!(vlflags & (AFS_VLF_RWEXISTS | AFS_VLF_ROEXISTS | AFS_VLF_BACKEXISTS))) {
|
|
entry->error = -ENOMEDIUM;
|
|
__set_bit(AFS_VLDB_QUERY_ERROR, &entry->flags);
|
|
}
|
|
|
|
__set_bit(AFS_VLDB_QUERY_VALID, &entry->flags);
|
|
_leave(" = 0 [done]");
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* VL.GetEntryByNameU operation type.
|
|
*/
|
|
static const struct afs_call_type afs_RXVLGetEntryByNameU = {
|
|
.name = "VL.GetEntryByNameU",
|
|
.op = afs_VL_GetEntryByNameU,
|
|
.deliver = afs_deliver_vl_get_entry_by_name_u,
|
|
.destructor = afs_flat_call_destructor,
|
|
};
|
|
|
|
/*
|
|
* Dispatch a get volume entry by name or ID operation (uuid variant). If the
|
|
* volname is a decimal number then it's a volume ID not a volume name.
|
|
*/
|
|
struct afs_vldb_entry *afs_vl_get_entry_by_name_u(struct afs_vl_cursor *vc,
|
|
const char *volname,
|
|
int volnamesz)
|
|
{
|
|
struct afs_vldb_entry *entry;
|
|
struct afs_call *call;
|
|
struct afs_net *net = vc->cell->net;
|
|
size_t reqsz, padsz;
|
|
__be32 *bp;
|
|
|
|
_enter("");
|
|
|
|
padsz = (4 - (volnamesz & 3)) & 3;
|
|
reqsz = 8 + volnamesz + padsz;
|
|
|
|
entry = kzalloc(sizeof(struct afs_vldb_entry), GFP_KERNEL);
|
|
if (!entry)
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
call = afs_alloc_flat_call(net, &afs_RXVLGetEntryByNameU, reqsz,
|
|
sizeof(struct afs_uvldbentry__xdr));
|
|
if (!call) {
|
|
kfree(entry);
|
|
return ERR_PTR(-ENOMEM);
|
|
}
|
|
|
|
call->key = vc->key;
|
|
call->ret_vldb = entry;
|
|
call->max_lifespan = AFS_VL_MAX_LIFESPAN;
|
|
call->peer = rxrpc_kernel_get_peer(vc->alist->addrs[vc->addr_index].peer);
|
|
call->service_id = vc->server->service_id;
|
|
|
|
/* Marshall the parameters */
|
|
bp = call->request;
|
|
*bp++ = htonl(VLGETENTRYBYNAMEU);
|
|
*bp++ = htonl(volnamesz);
|
|
memcpy(bp, volname, volnamesz);
|
|
if (padsz > 0)
|
|
memset((void *)bp + volnamesz, 0, padsz);
|
|
|
|
trace_afs_make_vl_call(call);
|
|
afs_make_call(call, GFP_KERNEL);
|
|
afs_wait_for_call_to_complete(call);
|
|
vc->call_abort_code = call->abort_code;
|
|
vc->call_error = call->error;
|
|
vc->call_responded = call->responded;
|
|
afs_put_call(call);
|
|
if (vc->call_error) {
|
|
kfree(entry);
|
|
return ERR_PTR(vc->call_error);
|
|
}
|
|
return entry;
|
|
}
|
|
|
|
/*
|
|
* Deliver reply data to a VL.GetAddrsU call.
|
|
*
|
|
* GetAddrsU(IN ListAddrByAttributes *inaddr,
|
|
* OUT afsUUID *uuidp1,
|
|
* OUT uint32_t *uniquifier,
|
|
* OUT uint32_t *nentries,
|
|
* OUT bulkaddrs *blkaddrs);
|
|
*/
|
|
static int afs_deliver_vl_get_addrs_u(struct afs_call *call)
|
|
{
|
|
struct afs_addr_list *alist;
|
|
__be32 *bp;
|
|
u32 uniquifier, nentries, count;
|
|
int i, ret;
|
|
|
|
_enter("{%u,%zu/%u}",
|
|
call->unmarshall, iov_iter_count(call->iter), call->count);
|
|
|
|
switch (call->unmarshall) {
|
|
case 0:
|
|
afs_extract_to_buf(call,
|
|
sizeof(struct afs_uuid__xdr) + 3 * sizeof(__be32));
|
|
call->unmarshall++;
|
|
|
|
/* Extract the returned uuid, uniquifier, nentries and
|
|
* blkaddrs size */
|
|
fallthrough;
|
|
case 1:
|
|
ret = afs_extract_data(call, true);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
bp = call->buffer + sizeof(struct afs_uuid__xdr);
|
|
uniquifier = ntohl(*bp++);
|
|
nentries = ntohl(*bp++);
|
|
count = ntohl(*bp);
|
|
|
|
nentries = min(nentries, count);
|
|
alist = afs_alloc_addrlist(nentries);
|
|
if (!alist)
|
|
return -ENOMEM;
|
|
alist->version = uniquifier;
|
|
call->ret_alist = alist;
|
|
call->count = count;
|
|
call->count2 = nentries;
|
|
call->unmarshall++;
|
|
|
|
more_entries:
|
|
count = min(call->count, 4U);
|
|
afs_extract_to_buf(call, count * sizeof(__be32));
|
|
|
|
fallthrough; /* and extract entries */
|
|
case 2:
|
|
ret = afs_extract_data(call, call->count > 4);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
alist = call->ret_alist;
|
|
bp = call->buffer;
|
|
count = min(call->count, 4U);
|
|
for (i = 0; i < count; i++) {
|
|
if (alist->nr_addrs < call->count2) {
|
|
ret = afs_merge_fs_addr4(call->net, alist, *bp++, AFS_FS_PORT);
|
|
if (ret < 0)
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
call->count -= count;
|
|
if (call->count > 0)
|
|
goto more_entries;
|
|
call->unmarshall++;
|
|
break;
|
|
}
|
|
|
|
_leave(" = 0 [done]");
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* VL.GetAddrsU operation type.
|
|
*/
|
|
static const struct afs_call_type afs_RXVLGetAddrsU = {
|
|
.name = "VL.GetAddrsU",
|
|
.op = afs_VL_GetAddrsU,
|
|
.deliver = afs_deliver_vl_get_addrs_u,
|
|
.destructor = afs_flat_call_destructor,
|
|
};
|
|
|
|
/*
|
|
* Dispatch an operation to get the addresses for a server, where the server is
|
|
* nominated by UUID.
|
|
*/
|
|
struct afs_addr_list *afs_vl_get_addrs_u(struct afs_vl_cursor *vc,
|
|
const uuid_t *uuid)
|
|
{
|
|
struct afs_ListAddrByAttributes__xdr *r;
|
|
struct afs_addr_list *alist;
|
|
const struct afs_uuid *u = (const struct afs_uuid *)uuid;
|
|
struct afs_call *call;
|
|
struct afs_net *net = vc->cell->net;
|
|
__be32 *bp;
|
|
int i;
|
|
|
|
_enter("");
|
|
|
|
call = afs_alloc_flat_call(net, &afs_RXVLGetAddrsU,
|
|
sizeof(__be32) + sizeof(struct afs_ListAddrByAttributes__xdr),
|
|
sizeof(struct afs_uuid__xdr) + 3 * sizeof(__be32));
|
|
if (!call)
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
call->key = vc->key;
|
|
call->ret_alist = NULL;
|
|
call->max_lifespan = AFS_VL_MAX_LIFESPAN;
|
|
call->peer = rxrpc_kernel_get_peer(vc->alist->addrs[vc->addr_index].peer);
|
|
call->service_id = vc->server->service_id;
|
|
|
|
/* Marshall the parameters */
|
|
bp = call->request;
|
|
*bp++ = htonl(VLGETADDRSU);
|
|
r = (struct afs_ListAddrByAttributes__xdr *)bp;
|
|
r->Mask = htonl(AFS_VLADDR_UUID);
|
|
r->ipaddr = 0;
|
|
r->index = 0;
|
|
r->spare = 0;
|
|
r->uuid.time_low = u->time_low;
|
|
r->uuid.time_mid = htonl(ntohs(u->time_mid));
|
|
r->uuid.time_hi_and_version = htonl(ntohs(u->time_hi_and_version));
|
|
r->uuid.clock_seq_hi_and_reserved = htonl(u->clock_seq_hi_and_reserved);
|
|
r->uuid.clock_seq_low = htonl(u->clock_seq_low);
|
|
for (i = 0; i < 6; i++)
|
|
r->uuid.node[i] = htonl(u->node[i]);
|
|
|
|
trace_afs_make_vl_call(call);
|
|
afs_make_call(call, GFP_KERNEL);
|
|
afs_wait_for_call_to_complete(call);
|
|
vc->call_abort_code = call->abort_code;
|
|
vc->call_error = call->error;
|
|
vc->call_responded = call->responded;
|
|
alist = call->ret_alist;
|
|
afs_put_call(call);
|
|
if (vc->call_error) {
|
|
afs_put_addrlist(alist, afs_alist_trace_put_getaddru);
|
|
return ERR_PTR(vc->call_error);
|
|
}
|
|
return alist;
|
|
}
|
|
|
|
/*
|
|
* Deliver reply data to an VL.GetCapabilities operation.
|
|
*/
|
|
static int afs_deliver_vl_get_capabilities(struct afs_call *call)
|
|
{
|
|
u32 count;
|
|
int ret;
|
|
|
|
_enter("{%u,%zu/%u}",
|
|
call->unmarshall, iov_iter_count(call->iter), call->count);
|
|
|
|
switch (call->unmarshall) {
|
|
case 0:
|
|
afs_extract_to_tmp(call);
|
|
call->unmarshall++;
|
|
|
|
fallthrough; /* and extract the capabilities word count */
|
|
case 1:
|
|
ret = afs_extract_data(call, true);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
count = ntohl(call->tmp);
|
|
call->count = count;
|
|
call->count2 = count;
|
|
|
|
call->unmarshall++;
|
|
afs_extract_discard(call, count * sizeof(__be32));
|
|
|
|
fallthrough; /* and extract capabilities words */
|
|
case 2:
|
|
ret = afs_extract_data(call, false);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
/* TODO: Examine capabilities */
|
|
|
|
call->unmarshall++;
|
|
break;
|
|
}
|
|
|
|
_leave(" = 0 [done]");
|
|
return 0;
|
|
}
|
|
|
|
static void afs_destroy_vl_get_capabilities(struct afs_call *call)
|
|
{
|
|
afs_put_addrlist(call->vl_probe, afs_alist_trace_put_vlgetcaps);
|
|
afs_put_vlserver(call->net, call->vlserver);
|
|
afs_flat_call_destructor(call);
|
|
}
|
|
|
|
/*
|
|
* VL.GetCapabilities operation type
|
|
*/
|
|
static const struct afs_call_type afs_RXVLGetCapabilities = {
|
|
.name = "VL.GetCapabilities",
|
|
.op = afs_VL_GetCapabilities,
|
|
.deliver = afs_deliver_vl_get_capabilities,
|
|
.immediate_cancel = afs_vlserver_probe_result,
|
|
.done = afs_vlserver_probe_result,
|
|
.destructor = afs_destroy_vl_get_capabilities,
|
|
};
|
|
|
|
/*
|
|
* Probe a volume server for the capabilities that it supports. This can
|
|
* return up to 196 words.
|
|
*
|
|
* We use this to probe for service upgrade to determine what the server at the
|
|
* other end supports.
|
|
*/
|
|
struct afs_call *afs_vl_get_capabilities(struct afs_net *net,
|
|
struct afs_addr_list *alist,
|
|
unsigned int addr_index,
|
|
struct key *key,
|
|
struct afs_vlserver *server,
|
|
unsigned int server_index)
|
|
{
|
|
struct afs_call *call;
|
|
__be32 *bp;
|
|
|
|
_enter("");
|
|
|
|
call = afs_alloc_flat_call(net, &afs_RXVLGetCapabilities, 1 * 4, 16 * 4);
|
|
if (!call)
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
call->key = key;
|
|
call->vlserver = afs_get_vlserver(server);
|
|
call->server_index = server_index;
|
|
call->peer = rxrpc_kernel_get_peer(alist->addrs[addr_index].peer);
|
|
call->vl_probe = afs_get_addrlist(alist, afs_alist_trace_get_vlgetcaps);
|
|
call->probe_index = addr_index;
|
|
call->service_id = server->service_id;
|
|
call->upgrade = true;
|
|
call->async = true;
|
|
call->max_lifespan = AFS_PROBE_MAX_LIFESPAN;
|
|
|
|
/* marshall the parameters */
|
|
bp = call->request;
|
|
*bp++ = htonl(VLGETCAPABILITIES);
|
|
|
|
/* Can't take a ref on server */
|
|
trace_afs_make_vl_call(call);
|
|
afs_make_call(call, GFP_KERNEL);
|
|
return call;
|
|
}
|
|
|
|
/*
|
|
* Deliver reply data to a YFSVL.GetEndpoints call.
|
|
*
|
|
* GetEndpoints(IN yfsServerAttributes *attr,
|
|
* OUT opr_uuid *uuid,
|
|
* OUT afs_int32 *uniquifier,
|
|
* OUT endpoints *fsEndpoints,
|
|
* OUT endpoints *volEndpoints)
|
|
*/
|
|
static int afs_deliver_yfsvl_get_endpoints(struct afs_call *call)
|
|
{
|
|
struct afs_addr_list *alist;
|
|
__be32 *bp;
|
|
u32 uniquifier, size;
|
|
int ret;
|
|
|
|
_enter("{%u,%zu,%u}",
|
|
call->unmarshall, iov_iter_count(call->iter), call->count2);
|
|
|
|
switch (call->unmarshall) {
|
|
case 0:
|
|
afs_extract_to_buf(call, sizeof(uuid_t) + 3 * sizeof(__be32));
|
|
call->unmarshall = 1;
|
|
|
|
/* Extract the returned uuid, uniquifier, fsEndpoints count and
|
|
* either the first fsEndpoint type or the volEndpoints
|
|
* count if there are no fsEndpoints. */
|
|
fallthrough;
|
|
case 1:
|
|
ret = afs_extract_data(call, true);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
bp = call->buffer + sizeof(uuid_t);
|
|
uniquifier = ntohl(*bp++);
|
|
call->count = ntohl(*bp++);
|
|
call->count2 = ntohl(*bp); /* Type or next count */
|
|
|
|
if (call->count > YFS_MAXENDPOINTS)
|
|
return afs_protocol_error(call, afs_eproto_yvl_fsendpt_num);
|
|
|
|
alist = afs_alloc_addrlist(call->count);
|
|
if (!alist)
|
|
return -ENOMEM;
|
|
alist->version = uniquifier;
|
|
call->ret_alist = alist;
|
|
|
|
if (call->count == 0)
|
|
goto extract_volendpoints;
|
|
|
|
next_fsendpoint:
|
|
switch (call->count2) {
|
|
case YFS_ENDPOINT_IPV4:
|
|
size = sizeof(__be32) * (1 + 1 + 1);
|
|
break;
|
|
case YFS_ENDPOINT_IPV6:
|
|
size = sizeof(__be32) * (1 + 4 + 1);
|
|
break;
|
|
default:
|
|
return afs_protocol_error(call, afs_eproto_yvl_fsendpt_type);
|
|
}
|
|
|
|
size += sizeof(__be32);
|
|
afs_extract_to_buf(call, size);
|
|
call->unmarshall = 2;
|
|
|
|
fallthrough; /* and extract fsEndpoints[] entries */
|
|
case 2:
|
|
ret = afs_extract_data(call, true);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
alist = call->ret_alist;
|
|
bp = call->buffer;
|
|
switch (call->count2) {
|
|
case YFS_ENDPOINT_IPV4:
|
|
if (ntohl(bp[0]) != sizeof(__be32) * 2)
|
|
return afs_protocol_error(
|
|
call, afs_eproto_yvl_fsendpt4_len);
|
|
ret = afs_merge_fs_addr4(call->net, alist, bp[1], ntohl(bp[2]));
|
|
if (ret < 0)
|
|
return ret;
|
|
bp += 3;
|
|
break;
|
|
case YFS_ENDPOINT_IPV6:
|
|
if (ntohl(bp[0]) != sizeof(__be32) * 5)
|
|
return afs_protocol_error(
|
|
call, afs_eproto_yvl_fsendpt6_len);
|
|
ret = afs_merge_fs_addr6(call->net, alist, bp + 1, ntohl(bp[5]));
|
|
if (ret < 0)
|
|
return ret;
|
|
bp += 6;
|
|
break;
|
|
default:
|
|
return afs_protocol_error(call, afs_eproto_yvl_fsendpt_type);
|
|
}
|
|
|
|
/* Got either the type of the next entry or the count of
|
|
* volEndpoints if no more fsEndpoints.
|
|
*/
|
|
call->count2 = ntohl(*bp++);
|
|
|
|
call->count--;
|
|
if (call->count > 0)
|
|
goto next_fsendpoint;
|
|
|
|
extract_volendpoints:
|
|
/* Extract the list of volEndpoints. */
|
|
call->count = call->count2;
|
|
if (!call->count)
|
|
goto end;
|
|
if (call->count > YFS_MAXENDPOINTS)
|
|
return afs_protocol_error(call, afs_eproto_yvl_vlendpt_type);
|
|
|
|
afs_extract_to_buf(call, 1 * sizeof(__be32));
|
|
call->unmarshall = 3;
|
|
|
|
/* Extract the type of volEndpoints[0]. Normally we would
|
|
* extract the type of the next endpoint when we extract the
|
|
* data of the current one, but this is the first...
|
|
*/
|
|
fallthrough;
|
|
case 3:
|
|
ret = afs_extract_data(call, true);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
bp = call->buffer;
|
|
|
|
next_volendpoint:
|
|
call->count2 = ntohl(*bp++);
|
|
switch (call->count2) {
|
|
case YFS_ENDPOINT_IPV4:
|
|
size = sizeof(__be32) * (1 + 1 + 1);
|
|
break;
|
|
case YFS_ENDPOINT_IPV6:
|
|
size = sizeof(__be32) * (1 + 4 + 1);
|
|
break;
|
|
default:
|
|
return afs_protocol_error(call, afs_eproto_yvl_vlendpt_type);
|
|
}
|
|
|
|
if (call->count > 1)
|
|
size += sizeof(__be32); /* Get next type too */
|
|
afs_extract_to_buf(call, size);
|
|
call->unmarshall = 4;
|
|
|
|
fallthrough; /* and extract volEndpoints[] entries */
|
|
case 4:
|
|
ret = afs_extract_data(call, true);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
bp = call->buffer;
|
|
switch (call->count2) {
|
|
case YFS_ENDPOINT_IPV4:
|
|
if (ntohl(bp[0]) != sizeof(__be32) * 2)
|
|
return afs_protocol_error(
|
|
call, afs_eproto_yvl_vlendpt4_len);
|
|
bp += 3;
|
|
break;
|
|
case YFS_ENDPOINT_IPV6:
|
|
if (ntohl(bp[0]) != sizeof(__be32) * 5)
|
|
return afs_protocol_error(
|
|
call, afs_eproto_yvl_vlendpt6_len);
|
|
bp += 6;
|
|
break;
|
|
default:
|
|
return afs_protocol_error(call, afs_eproto_yvl_vlendpt_type);
|
|
}
|
|
|
|
/* Got either the type of the next entry or the count of
|
|
* volEndpoints if no more fsEndpoints.
|
|
*/
|
|
call->count--;
|
|
if (call->count > 0)
|
|
goto next_volendpoint;
|
|
|
|
end:
|
|
afs_extract_discard(call, 0);
|
|
call->unmarshall = 5;
|
|
|
|
fallthrough; /* Done */
|
|
case 5:
|
|
ret = afs_extract_data(call, false);
|
|
if (ret < 0)
|
|
return ret;
|
|
call->unmarshall = 6;
|
|
fallthrough;
|
|
|
|
case 6:
|
|
break;
|
|
}
|
|
|
|
_leave(" = 0 [done]");
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* YFSVL.GetEndpoints operation type.
|
|
*/
|
|
static const struct afs_call_type afs_YFSVLGetEndpoints = {
|
|
.name = "YFSVL.GetEndpoints",
|
|
.op = afs_YFSVL_GetEndpoints,
|
|
.deliver = afs_deliver_yfsvl_get_endpoints,
|
|
.destructor = afs_flat_call_destructor,
|
|
};
|
|
|
|
/*
|
|
* Dispatch an operation to get the addresses for a server, where the server is
|
|
* nominated by UUID.
|
|
*/
|
|
struct afs_addr_list *afs_yfsvl_get_endpoints(struct afs_vl_cursor *vc,
|
|
const uuid_t *uuid)
|
|
{
|
|
struct afs_addr_list *alist;
|
|
struct afs_call *call;
|
|
struct afs_net *net = vc->cell->net;
|
|
__be32 *bp;
|
|
|
|
_enter("");
|
|
|
|
call = afs_alloc_flat_call(net, &afs_YFSVLGetEndpoints,
|
|
sizeof(__be32) * 2 + sizeof(*uuid),
|
|
sizeof(struct in6_addr) + sizeof(__be32) * 3);
|
|
if (!call)
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
call->key = vc->key;
|
|
call->ret_alist = NULL;
|
|
call->max_lifespan = AFS_VL_MAX_LIFESPAN;
|
|
call->peer = rxrpc_kernel_get_peer(vc->alist->addrs[vc->addr_index].peer);
|
|
call->service_id = vc->server->service_id;
|
|
|
|
/* Marshall the parameters */
|
|
bp = call->request;
|
|
*bp++ = htonl(YVLGETENDPOINTS);
|
|
*bp++ = htonl(YFS_SERVER_UUID);
|
|
memcpy(bp, uuid, sizeof(*uuid)); /* Type opr_uuid */
|
|
|
|
trace_afs_make_vl_call(call);
|
|
afs_make_call(call, GFP_KERNEL);
|
|
afs_wait_for_call_to_complete(call);
|
|
vc->call_abort_code = call->abort_code;
|
|
vc->call_error = call->error;
|
|
vc->call_responded = call->responded;
|
|
alist = call->ret_alist;
|
|
afs_put_call(call);
|
|
if (vc->call_error) {
|
|
afs_put_addrlist(alist, afs_alist_trace_put_getaddru);
|
|
return ERR_PTR(vc->call_error);
|
|
}
|
|
return alist;
|
|
}
|
|
|
|
/*
|
|
* Deliver reply data to a YFSVL.GetCellName operation.
|
|
*/
|
|
static int afs_deliver_yfsvl_get_cell_name(struct afs_call *call)
|
|
{
|
|
char *cell_name;
|
|
u32 namesz, paddedsz;
|
|
int ret;
|
|
|
|
_enter("{%u,%zu/%u}",
|
|
call->unmarshall, iov_iter_count(call->iter), call->count);
|
|
|
|
switch (call->unmarshall) {
|
|
case 0:
|
|
afs_extract_to_tmp(call);
|
|
call->unmarshall++;
|
|
|
|
fallthrough; /* and extract the cell name length */
|
|
case 1:
|
|
ret = afs_extract_data(call, true);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
namesz = ntohl(call->tmp);
|
|
if (namesz > AFS_MAXCELLNAME)
|
|
return afs_protocol_error(call, afs_eproto_cellname_len);
|
|
paddedsz = (namesz + 3) & ~3;
|
|
call->count = namesz;
|
|
call->count2 = paddedsz - namesz;
|
|
|
|
cell_name = kmalloc(namesz + 1, GFP_KERNEL);
|
|
if (!cell_name)
|
|
return -ENOMEM;
|
|
cell_name[namesz] = 0;
|
|
call->ret_str = cell_name;
|
|
|
|
afs_extract_begin(call, cell_name, namesz);
|
|
call->unmarshall++;
|
|
|
|
fallthrough; /* and extract cell name */
|
|
case 2:
|
|
ret = afs_extract_data(call, true);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
afs_extract_discard(call, call->count2);
|
|
call->unmarshall++;
|
|
|
|
fallthrough; /* and extract padding */
|
|
case 3:
|
|
ret = afs_extract_data(call, false);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
call->unmarshall++;
|
|
break;
|
|
}
|
|
|
|
_leave(" = 0 [done]");
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* VL.GetCapabilities operation type
|
|
*/
|
|
static const struct afs_call_type afs_YFSVLGetCellName = {
|
|
.name = "YFSVL.GetCellName",
|
|
.op = afs_YFSVL_GetCellName,
|
|
.deliver = afs_deliver_yfsvl_get_cell_name,
|
|
.destructor = afs_flat_call_destructor,
|
|
};
|
|
|
|
/*
|
|
* Probe a volume server for the capabilities that it supports. This can
|
|
* return up to 196 words.
|
|
*
|
|
* We use this to probe for service upgrade to determine what the server at the
|
|
* other end supports.
|
|
*/
|
|
char *afs_yfsvl_get_cell_name(struct afs_vl_cursor *vc)
|
|
{
|
|
struct afs_call *call;
|
|
struct afs_net *net = vc->cell->net;
|
|
__be32 *bp;
|
|
char *cellname;
|
|
|
|
_enter("");
|
|
|
|
call = afs_alloc_flat_call(net, &afs_YFSVLGetCellName, 1 * 4, 0);
|
|
if (!call)
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
call->key = vc->key;
|
|
call->ret_str = NULL;
|
|
call->max_lifespan = AFS_VL_MAX_LIFESPAN;
|
|
call->peer = rxrpc_kernel_get_peer(vc->alist->addrs[vc->addr_index].peer);
|
|
call->service_id = vc->server->service_id;
|
|
|
|
/* marshall the parameters */
|
|
bp = call->request;
|
|
*bp++ = htonl(YVLGETCELLNAME);
|
|
|
|
/* Can't take a ref on server */
|
|
trace_afs_make_vl_call(call);
|
|
afs_make_call(call, GFP_KERNEL);
|
|
afs_wait_for_call_to_complete(call);
|
|
vc->call_abort_code = call->abort_code;
|
|
vc->call_error = call->error;
|
|
vc->call_responded = call->responded;
|
|
cellname = call->ret_str;
|
|
afs_put_call(call);
|
|
if (vc->call_error) {
|
|
kfree(cellname);
|
|
return ERR_PTR(vc->call_error);
|
|
}
|
|
return cellname;
|
|
}
|