Skip to content

Commit 64d7e82

Browse files
Started work on implementing NFS 4.1 with session trunking (IBM#2)
Signed-off-by: Peter-Jan Gootzen <[email protected]>
1 parent 780c45f commit 64d7e82

File tree

5 files changed

+318
-32
lines changed

5 files changed

+318
-32
lines changed

.gitattributes

-1
Original file line numberDiff line numberDiff line change
@@ -1 +0,0 @@
1-
*.svg filter=lfs diff=lfs merge=lfs -text

virtionfs/nfs_v4.c

+114-28
Original file line numberDiff line numberDiff line change
@@ -92,45 +92,131 @@ int nfs4_fill_create_attrs(struct fuse_in_header *in_hdr, uint32_t mode, fattr4
9292
return 0;
9393
}
9494

95+
bool nfs4_check_session_trunking_allowed(EXCHANGE_ID4resok *l, EXCHANGE_ID4resok *r) {
96+
return l->eir_clientid == r->eir_clientid
97+
&& l->eir_server_owner.so_major_id.so_major_id_len == r->eir_server_owner.so_major_id.so_major_id_len
98+
&& memcmp(l->eir_server_owner.so_major_id.so_major_id_val, r->eir_server_owner
99+
.so_major_id.so_major_id_val, l->eir_server_owner.so_major_id.so_major_id_len) == 0
100+
&& l->eir_server_owner.so_minor_id == r->eir_server_owner.so_minor_id
101+
&& l->eir_server_scope.eir_server_scope_len == r->eir_server_scope.eir_server_scope_len
102+
&& memcmp(l->eir_server_scope.eir_server_scope_val, r->eir_server_scope
103+
.eir_server_scope_val, l->eir_server_scope.eir_server_scope_len) == 0;
104+
}
105+
106+
int nfs4_op_createsession(nfs_argop4 *op, clientid4 clientid)
107+
{
108+
CREATE_SESSION4args *arg;
109+
op[0].argop = OP_BIND_CONN_TO_SESSION;
110+
arg = &op[0].nfs_argop4_u.opcreatesession;
111+
112+
arg->csa_clientid = clientid;
113+
arg->csa_flags = 0;
114+
arg->csa_cb_program = 0;
115+
arg->csa_sec_parms.csa_sec_parms_val = NULL;
116+
arg->csa_sec_parms.csa_sec_parms_len = 0;
117+
118+
// Currently no caching support
119+
arg->csa_fore_chan_attrs.ca_maxresponsesize_cached = 0;
120+
arg->csa_fore_chan_attrs.ca_maxrequests = NFS4_MAXREQUESTSIZE;
121+
arg->csa_fore_chan_attrs.ca_maxresponsesize = NFS4_MAXRESPONSESIZE;
122+
arg->csa_fore_chan_attrs.ca_maxrequestsize = NFS4_MAXREQUESTSIZE;
123+
// We have too little control over libnfs to properly use this
124+
arg->csa_fore_chan_attrs.ca_headerpadsize = 0;
125+
// Magic from the Linux kernel, 8 seems about right
126+
arg->csa_fore_chan_attrs.ca_maxoperations = NFS4_MAX_OPS;
127+
// No RDMA here
128+
arg->csa_fore_chan_attrs.ca_rdma_ird.ca_rdma_ird_val = NULL;
129+
arg->csa_fore_chan_attrs.ca_rdma_ird.ca_rdma_ird_len = 0;
130+
131+
// Currently no caching support
132+
arg->csa_back_chan_attrs.ca_maxresponsesize_cached = 0;
133+
arg->csa_back_chan_attrs.ca_maxrequests = NFS4_MAXREQUESTSIZE;
134+
arg->csa_back_chan_attrs.ca_maxresponsesize = NFS4_MAXRESPONSESIZE;
135+
arg->csa_back_chan_attrs.ca_maxrequestsize = NFS4_MAXREQUESTSIZE;
136+
// We have too little control over libnfs to properly use this
137+
arg->csa_back_chan_attrs.ca_headerpadsize = 0;
138+
// Magic from the Linux kernel, 8 seems about right
139+
arg->csa_back_chan_attrs.ca_maxoperations = NFS4_MAX_OPS;
140+
// No RDMA here
141+
arg->csa_back_chan_attrs.ca_rdma_ird.ca_rdma_ird_val = NULL;
142+
arg->csa_back_chan_attrs.ca_rdma_ird.ca_rdma_ird_len = 0;
143+
144+
return 1;
145+
}
146+
147+
148+
int nfs4_op_bindconntosession(nfs_argop4 *op, sessionid4 *sessionid, channel_dir_from_client4 channel, bool rdma)
149+
{
150+
BIND_CONN_TO_SESSION4args *bindargs;
151+
op[0].argop = OP_BIND_CONN_TO_SESSION;
152+
bindargs = &op[0].nfs_argop4_u.opbindconntosession;
153+
154+
memcpy(&bindargs->bctsa_sessid, sessionid, sizeof(sessionid4));
155+
bindargs->bctsa_dir = CDFC4_FORE_OR_BOTH;
156+
bindargs->bctsa_use_conn_in_rdma_mode = rdma;
157+
158+
return 1;
159+
}
160+
161+
int nfs4_op_exchangeid(nfs_argop4 *op, verifier4 verifier, const char *client_name)
162+
{
163+
EXCHANGE_ID4args *exidargs;
164+
op[0].argop = OP_EXCHANGE_ID;
165+
exidargs = &op[0].nfs_argop4_u.opexchangeid;
166+
167+
memcpy(exidargs->eia_clientowner.co_verifier, verifier, sizeof(verifier4));
168+
exidargs->eia_clientowner.co_ownerid.co_ownerid_val = (char *) client_name;
169+
exidargs->eia_clientowner.co_ownerid.co_ownerid_len = strlen(client_name);
170+
171+
exidargs->eia_state_protect.spa_how = SP4_NONE;
172+
173+
exidargs->eia_flags = 0;
174+
175+
exidargs->eia_client_impl_id.eia_client_impl_id_val = NULL;
176+
exidargs->eia_client_impl_id.eia_client_impl_id_len = 0;
177+
178+
return 1;
179+
}
180+
95181
/* Functions taken and modified from libnfs/lib/nfs_v4.c
96182
* commit: 2678dfecd9c797991b7768490929b1478f339809 */
97183

98184
int nfs4_op_setclientid(nfs_argop4 *op, verifier4 verifier, const char *client_name)
99185
{
100-
SETCLIENTID4args *scidargs;
101-
102-
op[0].argop = OP_SETCLIENTID;
103-
scidargs = &op[0].nfs_argop4_u.opsetclientid;
104-
memcpy(scidargs->client.verifier, verifier, sizeof(verifier4));
105-
scidargs->client.id.id_len = strlen(client_name);
106-
scidargs->client.id.id_val = (char *) client_name;
107-
/* TODO: Decide what we should do here. As long as we only
108-
* expose a single FD to the application we will not be able to
109-
* do NFSv4 callbacks easily.
110-
* Just give it garbage for now until we figure out how we should
111-
* solve this. Until then we will just have to avoid doing things
112-
* that require a callback.
113-
* ( Clients (i.e. Linux) ignore this anyway and just call back to
114-
* the originating address and program anyway. )
115-
*/
116-
scidargs->callback.cb_program = 0; /* NFS4_CALLBACK */
117-
scidargs->callback.cb_location.r_netid = "tcp";
118-
scidargs->callback.cb_location.r_addr = "0.0.0.0.0.0";
119-
scidargs->callback_ident = 0x00000001;
120-
121-
return 1;
186+
SETCLIENTID4args *scidargs;
187+
op[0].argop = OP_SETCLIENTID;
188+
scidargs = &op[0].nfs_argop4_u.opsetclientid;
189+
190+
memcpy(scidargs->client.verifier, verifier, sizeof(verifier4));
191+
scidargs->client.id.id_len = strlen(client_name);
192+
scidargs->client.id.id_val = (char *) client_name;
193+
/* TODO: Decide what we should do here. As long as we only
194+
* expose a single FD to the application we will not be able to
195+
* do NFSv4 callbacks easily.
196+
* Just give it garbage for now until we figure out how we should
197+
* solve this. Until then we will just have to avoid doing things
198+
* that require a callback.
199+
* ( Clients (i.e. Linux) ignore this anyway and just call back to
200+
* the originating address and program anyway. )
201+
*/
202+
scidargs->callback.cb_program = 0; /* NFS4_CALLBACK */
203+
scidargs->callback.cb_location.r_netid = "tcp";
204+
scidargs->callback.cb_location.r_addr = "0.0.0.0.0.0";
205+
scidargs->callback_ident = 0x00000001;
206+
207+
return 1;
122208
}
123209

124210
int nfs4_op_setclientid_confirm(struct nfs_argop4 *op, uint64_t clientid, verifier4 verifier)
125211
{
126-
SETCLIENTID_CONFIRM4args *scidcargs;
212+
SETCLIENTID_CONFIRM4args *scidcargs;
127213

128-
op[0].argop = OP_SETCLIENTID_CONFIRM;
129-
scidcargs = &op[0].nfs_argop4_u.opsetclientid_confirm;
130-
scidcargs->clientid = clientid;
131-
memcpy(scidcargs->setclientid_confirm, verifier, NFS4_VERIFIER_SIZE);
214+
op[0].argop = OP_SETCLIENTID_CONFIRM;
215+
scidcargs = &op[0].nfs_argop4_u.opsetclientid_confirm;
216+
scidcargs->clientid = clientid;
217+
memcpy(scidcargs->setclientid_confirm, verifier, NFS4_VERIFIER_SIZE);
132218

133-
return 1;
219+
return 1;
134220
}
135221

136222
int nfs4_find_op(COMPOUND4res *res, int op)

virtionfs/nfs_v4.h

+41
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,15 @@
33
# Copyright 2022- IBM Inc. All rights reserved
44
# SPDX-License-Identifier: LGPL-2.1-or-later
55
#
6+
# GPL-2.0 WITH Linux-syscall-note
7+
# from https://elixir.bootlin.com/linux/v6.0.12/source/include/uapi/linux/nfs4.h
8+
#
69
*/
710

811
#ifndef NFS_V4_H
912
#define NFS_V4_H
1013

14+
#include <stdbool.h>
1115
#include <linux/fuse.h>
1216
#include <nfsc/libnfs-raw-nfs4.h>
1317

@@ -16,6 +20,38 @@
1620
// Empirically verified with Linux kernel 5.11
1721
#define NFS_ROOT_FILEID 2
1822

23+
// 1MB is max read/write size in Linux, + some overhead
24+
#define NFS4_MAXRESPONSESIZE (1 << 20)
25+
#define NFS4_MAXREQUESTSIZE (1 << 20)
26+
27+
#define NFS4_MAX_OUTSTANDING_REQUESTS 64
28+
29+
/*
30+
* linux/include/linux/nfs4.h
31+
*
32+
* NFSv4 protocol definitions.
33+
*
34+
* Copyright (c) 2002 The Regents of the University of Michigan.
35+
* All rights reserved.
36+
*
37+
* Kendrick Smith <[email protected]>
38+
* Andy Adamson <[email protected]>
39+
*/
40+
41+
/* An NFS4 sessions server must support at least NFS4_MAX_OPS operations.
42+
* If a compound requires more operations, adjust NFS4_MAX_OPS accordingly.
43+
*/
44+
#define NFS4_MAX_OPS 8
45+
46+
/* Our NFS4 client back channel server only wants the cb_sequene and the
47+
* actual operation per compound
48+
*/
49+
#define NFS4_MAX_BACK_CHANNEL_OPS 2
50+
51+
/*
52+
* End of Linux header
53+
*/
54+
1955
// No malloc needed
2056
struct vnfs_fh4 {
2157
u_int len;
@@ -26,6 +62,11 @@ typedef struct vnfs_fh4 vnfs_fh4;
2662
int nfs4_clone_fh(vnfs_fh4 *dst, nfs_fh4 *src);
2763
int nfs4_find_op(COMPOUND4res *res, int op);
2864
int nfs4_fill_create_attrs(struct fuse_in_header *in_hdr, uint32_t flags, fattr4 *attr);
65+
bool nfs4_check_session_trunking_allowed(EXCHANGE_ID4resok *l, EXCHANGE_ID4resok *r);
66+
// Supply the clientid received from EXCHANGE_ID
67+
int nfs4_op_createsession(nfs_argop4 *op, clientid4 clientid);
68+
int nfs4_op_bindconntosession(nfs_argop4 *op, sessionid4 *sessionid, channel_dir_from_client4 channel, bool rdma);
69+
int nfs4_op_exchangeid(nfs_argop4 *op, verifier4 verifier, const char *client_name);
2970
int nfs4_op_setclientid(nfs_argop4 *op, verifier4 verifier, const char *client_name);
3071
int nfs4_op_setclientid_confirm(struct nfs_argop4 *op, uint64_t clientid, verifier4 verifier);
3172
int nfs4_op_getattr(nfs_argop4 *op, uint32_t *attributes, int count);

virtionfs/virtionfs.c

+132-2
Original file line numberDiff line numberDiff line change
@@ -1309,6 +1309,137 @@ int getattr(struct fuse_session *se, struct virtionfs *vnfs,
13091309
return EWOULDBLOCK;
13101310
}
13111311

1312+
static void create_session_cb(struct rpc_context *rpc, int status, void *data, void *private_data)
1313+
{
1314+
struct virtionfs *vnfs = private_data;
1315+
COMPOUND4res *res = data;
1316+
1317+
if (status != RPC_STATUS_SUCCESS) {
1318+
fprintf(stderr, "RPC with NFS:CREATE_SESSION unsuccessful: rpc error=%d\n", status);
1319+
return;
1320+
}
1321+
if (res->status != NFS4_OK) {
1322+
fprintf(stderr, "NFS:CREATE_SESSION unsuccessful: nfs error=%d\n", res->status);
1323+
return;
1324+
}
1325+
}
1326+
1327+
static int create_session(struct virtionfs *vnfs)
1328+
{
1329+
COMPOUND4args args;
1330+
nfs_argop4 op[1];
1331+
memset(&args, 0, sizeof(args));
1332+
args.argarray.argarray_len = sizeof(op) / sizeof(nfs_argop4);
1333+
args.argarray.argarray_val = op;
1334+
memset(op, 0, sizeof(op));
1335+
1336+
nfs4_op_createsession(&op[0], &vnfs->sessionid, CDFC4_FORE_OR_BOTH, false);
1337+
1338+
if (rpc_nfs4_compound_async(vnfs->rpc, create_session_cb, &args, vnfs) != 0) {
1339+
fprintf(stderr, "Failed to send NFS:create_session request\n");
1340+
return -1;
1341+
}
1342+
1343+
return 0;
1344+
}
1345+
1346+
static void bind_conn_cb(struct rpc_context *rpc, int status, void *data, void *private_data)
1347+
{
1348+
struct virtionfs *vnfs = private_data;
1349+
COMPOUND4res *res = data;
1350+
1351+
if (status != RPC_STATUS_SUCCESS) {
1352+
fprintf(stderr, "RPC with NFS:bind_conn_to_session unsuccessful: rpc error=%d\n", status);
1353+
return;
1354+
}
1355+
if (res->status != NFS4_OK) {
1356+
fprintf(stderr, "NFS:bind_conn_to_session unsuccessful: nfs error=%d\n", res->status);
1357+
return;
1358+
}
1359+
1360+
BIND_CONN_TO_SESSION4resok *ok = &res->resarray.resarray_val[0].nfs_resop4_u.opbindconntosession.BIND_CONN_TO_SESSION4res_u.bctsr_resok4;
1361+
1362+
if (memcmp(ok->bctsr_sessid, vnfs->sessionid, sizeof(sessionid4)) == 0) {
1363+
vnfs->connections++;
1364+
if (vnfs->connections < vnfs->nthreads) {
1365+
// Another thread and mount
1366+
}
1367+
}
1368+
}
1369+
1370+
static int bind_conn(struct virtionfs *vnfs)
1371+
{
1372+
COMPOUND4args args;
1373+
nfs_argop4 op[1];
1374+
memset(&args, 0, sizeof(args));
1375+
args.argarray.argarray_len = sizeof(op) / sizeof(nfs_argop4);
1376+
args.argarray.argarray_val = op;
1377+
memset(op, 0, sizeof(op));
1378+
1379+
nfs4_op_bindconntosession(&op[0], &vnfs->sessionid, CDFC4_FORE_OR_BOTH, false);
1380+
1381+
if (rpc_nfs4_compound_async(vnfs->rpc, bind_conn_cb, &args, vnfs) != 0) {
1382+
fprintf(stderr, "Failed to send NFS:setclientid request\n");
1383+
return -1;
1384+
}
1385+
1386+
return 0;
1387+
}
1388+
1389+
static verifier4 default_verifier = {'0', '1', '2', '3', '4', '5', '6', '7'};
1390+
1391+
static void exchangeid_cb(struct rpc_context *rpc, int status, void *data, void *private_data)
1392+
{
1393+
struct virtionfs *vnfs = private_data;
1394+
COMPOUND4res *res = data;
1395+
1396+
if (status != RPC_STATUS_SUCCESS) {
1397+
fprintf(stderr, "RPC with NFS:exchange_id unsuccessful: rpc error=%d\n", status);
1398+
return;
1399+
}
1400+
if (res->status != NFS4_OK) {
1401+
fprintf(stderr, "NFS:exchange_id unsuccessful: nfs error=%d\n", res->status);
1402+
return;
1403+
}
1404+
1405+
EXCHANGE_ID4resok *ok = &res->resarray.resarray_val[0].nfs_resop4_u.opexchangeid.EXCHANGE_ID4res_u.eir_resok4;
1406+
1407+
if (vnfs->connections == 0) {
1408+
memcpy(&vnfs->first_exchangeid, ok, sizeof(*ok));
1409+
create_session(vnfs);
1410+
} else {
1411+
if (nfs4_check_session_trunking_allowed(&vnfs->first_exchangeid, ok)) {
1412+
bind_conn(vnfs);
1413+
} else {
1414+
// TODO handle can't trunking
1415+
// close connection
1416+
}
1417+
}
1418+
}
1419+
1420+
static int exchangeid(struct virtionfs *vnfs)
1421+
{
1422+
COMPOUND4args args;
1423+
nfs_argop4 op[1];
1424+
memset(&args, 0, sizeof(args));
1425+
args.argarray.argarray_len = sizeof(op) / sizeof(nfs_argop4);
1426+
args.argarray.argarray_val = op;
1427+
memset(op, 0, sizeof(op));
1428+
1429+
1430+
verifier4 v;
1431+
memcpy(default_verifier, v, sizeof(v));
1432+
v[0] = vnfs->connections;
1433+
nfs4_op_exchangeid(&op[0], default_verifier, "virtionfs");
1434+
1435+
if (rpc_nfs4_compound_async(vnfs->rpc, exchangeid_cb, &args, vnfs) != 0) {
1436+
fprintf(stderr, "Failed to send NFS:exchange_id request\n");
1437+
return -1;
1438+
}
1439+
1440+
return 0;
1441+
}
1442+
13121443
static void
13131444
setclientid_cb_2(struct rpc_context *rpc, int status, void *data,
13141445
void *private_data)
@@ -1371,7 +1502,6 @@ setclientid_cb_1(struct rpc_context *rpc, int status, void *data,
13711502
}
13721503
}
13731504

1374-
static verifier4 verifier = {'0', '1', '2', '3', '4', '5', '6', '7'};
13751505

13761506
int setclientid(struct virtionfs *vnfs)
13771507
{
@@ -1384,7 +1514,7 @@ int setclientid(struct virtionfs *vnfs)
13841514
memset(op, 0, sizeof(op));
13851515

13861516
// TODO make this verifier random and the client name somewhat unique too
1387-
nfs4_op_setclientid(&op[0], verifier, "virtionfs");
1517+
nfs4_op_setclientid(&op[0], default_verifier, "virtionfs");
13881518

13891519
if (rpc_nfs4_compound_async(vnfs->rpc, setclientid_cb_1, &args, vnfs) != 0) {
13901520
fprintf(stderr, "Failed to send NFS:setclientid request\n");

0 commit comments

Comments
 (0)