Page MenuHomeFreeBSD

nfsclrdma.ko: Client side NFS over RDMA module
Needs ReviewPublic

Authored by rmacklem on Sun, Aug 30, 10:46 PM.
Tags
None
Referenced Files
F171186531: D59278.id186181.diff
Wed, Sep 9, 8:48 AM
F171169802: D59278.id186219.diff
Wed, Sep 9, 5:10 AM
F171169758: D59278.id186219.diff
Wed, Sep 9, 5:09 AM
F171122973: D59278.id185964.diff
Tue, Sep 8, 8:37 PM
F171109035: D59278.id186189.diff
Tue, Sep 8, 6:22 PM
F171098481: D59278.id185964.diff
Tue, Sep 8, 4:52 PM
Unknown Object (File)
Tue, Sep 8, 8:18 AM
Unknown Object (File)
Tue, Sep 8, 2:22 AM
Subscribers

Details

Summary

This module implements RDMA for the NFS client.

It implements RFC-8166 and RFC-8267 using FRWR
where possible.

It does not, as yet, use Kostik's helper function for
access to a file's pages to avoid data copying to/from
them.

It implements a layer above the kernel OFED verbs
that is used by the client side krpc.

I realize this is a rather large chunk of code, so I
understand if you cannot review it.
(I'm putting it up now to give people lots of time,
since I am not planning on committing this to main
for at least 2 months.)

Thanks go to the Netperf lab for providing a test
facility and thanks goes to the Netperf lab folk
for their help with a newbie (me) getting set up.

I'd also like to thank Vinicius Ferrao for his server
implementation for FreeBSD. I would have never
been able to test this code without it.

Test Plan

Only minimal testing sofar. I will continue over
the coming weeks and hope that, by making
both the client and server modules available,
there may be other testers.

I will be announcing how to download the
modules on freebsd-current@ soon.

Diff Detail

Repository
rG FreeBSD src repository
Lint
Lint Skipped
Unit
Tests Skipped

Event Timeline

Oh, I should have noted that I either wrote the code
or cribbed it from Linux sources that carry the same
dual license as the OFED sources.

I was careful to avoid copying from any of the Linux
files that are only GPL'd.

This update fixes a bunch of issues.

xprt_verbs.c started out as a dual Linux file called
net/sunrpc/xprtrdma/verbs.c (dual licensed like the
OFED stack). It used wait_for_completion_interrupt_timeout().
--> These calls caused a lot of trouble, such as "use after free" or

crashes caused by structures being used after they were free'd.

So now the code uses wait_for_completion() and seems to be working
well. (If I run into a "hard hang" of the client, I may have to revisit this.)

A bug where the client used the wrong xid value has been fixed by
putting the call_msg on the stack instead of using a global one.
(This bug caused Read reduction to fail on the server.)

The code has been modified to handle retries correctly. Still not
really tested, but it is at least close to correct.

It now seems to be working ok for normal mount usage.

It still has a problem after the server is rebooted.

  • It makes a new connection (qp) after the server reboots, but for some reason, all RPCs get a RPC_PROGUNAVAIL error reply after that. However, new mounts work.

I think this is a bug in the current "glue" in the NFS server.
It calls svc_listen() before svc_reg() has been called in nfsrvd_addsock(),
resulting in RPC_RPOGUNAVAIL replies to RPCs when the client
connects right after a server reboot.
I think a single call to svc_reg() done in svc_rdma_sro_newconn() for
the first connection after loading will fix this, but I haven't tested it yet.

A small optimization where, instead of using m_adj() after
converting an mbuf list from M_EXTPG mbufs to clusters
via mb_unmapped_to_ext(), it now trims the M_EXTPG mbuf
list before calling mb_unmapped_to_ext().

Plus assorted other changes I can no longer recall.

Rick, overall, it looks good, but theres a lot of code here. The only way to be sure is testing that.

However, if we keep the original args chain intact, as suggested in the review, and leave the metadata buffer mr linked in it, we also need to adjust how mr is freed. It could add issues if we don't clean up it correctly.

clnt_rdma.c
557–571

Rick, I think this block is modifying the wrong list.

m1 is the copy being modified to be sent, but the for loop moves through the original args.

Doesn't *mreduce_prev = m2->m_next; modify the original argument list? It also looks to me that the assingment, in m2->m_next will be always NULL inside the if, because it's never set.

Maybe we should build the outgoing copy without the metadata, instead of copying everything and then trying to remove it?

rmacklem added inline comments.
clnt_rdma.c
557–571

Yes. Good catch. The code should remove the
mbuf that indicates reduction from m1 and not
args. It needs to remain in args, so that a retry works.

I'll fix this for the next update.

clnt_rdma.c
496

If we came from call_again label this may not be true and this entire block is skipped.

523

This will not be set if we didn't get through.

652–659

I think rpos += tlen2 is adding too much for a reduced write.

It already includes the RPC args and the RDMA header, it should only have been only the RPC.

There should be a way to remove the RDMA block from it, maybe we should save the state before adding the RDMA header to tlen2.

897

This may become an issue. We should track the position independently.

If we reach goto call_again, rpos may have been changed while reading the reply. And reduce_chp will be already set there, effectively skipping the rpos original value at line 522.

Maybe what we should do is just do something like:

/* Save the position */
reduce_pos = rb->pos;

/* Calculate the postion for each write */
read_pos = reduce_pos + rpc_prefix_len;

/* Here we keep rpos for decoding */
XDR_GETINT32(&xdrs, &rpos);

This would avoid reusing rpos for both the request and the reply.

Rick, there are some goto shenanigans happening.

clnt_rdma.c
895

This may be also affected by the call_again label if we came from a goto.

Should we have a cleanup goto label, so it's easy to retry?

rmacklem marked an inline comment as done.

Recent changes to "fix" handling of retries introduced
the bug Vinicius spotted. It actually didn't break anything
for the non-retry case, since it just meant the "reduce" mbuf
wasn't removed from the copy of the mbuf request list.

Since for the read RPC, that mbuf is at the end, it wouldn't
actually break the RPC message (except maybe leaving the
length too long, but the NFS code doesn't care about trailing
junk in the request, once it has parsed all it wants).

Anyhow, this version is fixed so that it removes the reduce
mbuf from the copy.

Since my test setup is broken at the moment, it hasn't actually
been tested.

We may be missing the error treatment for the case where reduce_chp is NULL.

clnt_rdma.c
513

reduce_cmp can be NULL here if xprt_rdma_create_chunk errors out.

551–556

Maybe we should check if reduce_chp was NULL here to also call goto cannotencode?

If we reach the final cleanup without a new mr we may run in a non necessary second free request.

clnt_rdma.c
1039

Add mr = NULL after for consistency to avoid trying to free it later.

1077–1078

We would have another free request even if mr was already freed.

clnt_rdma.c
496

I think this is correct. Since "goto call_again" happens
when the Kerberos credentials time out (based on the
expiry of the TGT), I'm assuming that the reduction for
a read or write will not have been done by the NFS server
and that the reduce chunk formed by this block is still
valid. (It is "yet another piece" not yet tested, because that
requires Kerberos and setting that up in the Netperf lab setup
is something I have not yet done.)
--> If it turns out that the NFS server "swallows" the reduction
chunk (a writelist chunk in RFC8166 terminology), then this
code will have to be rewritten.

523

As above, the first pass through this code should
always succeed. (I currently do not expect
xprt_rdma_create_chunk() to fail and return
NULL, but I won't claim it can never happen,
since I'm still pretty sketchy on the OFED stuff.)

On a subsequent retry, due to a Kerberos ticket
expiry, I assume that the "reduce_chp" is still
valid and does not need to be recreated.

557–571

Now fixed, although it would have only broken
a retry done from above the clnt_rdma_call() and
not the "goto call_again" case.

It was broken in the sense that it didn't take the
mbuf that "tags" the reduction out of the m1 copy
of the arguments list, it turns out that, for a read RPC
it is just trailing junk the NFS code didn't care about.

For write reduction, the code has not yet been tested
because the server does not currently do it.

652–659

Good question. This is for write reduction, that is
not yet tested.

My read of RFC8166 is that the position includes
both the RDMA and RPC headers, but I could be
wrong.

I don't know if anyone has implemented this yet?

If I am wrong and the RDMA header is not supposed
to be in the position count, it can easily be taken out.
See line# 601.

897

Yes, I agree that reuse of rpos was not wise and
I don't think it is correctly calculated at this time.
(Even ignoring that it might be trashed for a retry.)

Since the write reduction code is no tested, I am
not surprised.

I'll revisit rpos for the next rendition.

Of course, the FreeBSD server should learn how to
do write reductions someday. I can work with you
on that one, once this code is stable.

Use a separate variable called reduce_pos for the
reduction position, which is now calculated by the
NFS glue code, as suggested by Vinicius.

This is part of the untested code that does write
reduction, so I suspect there are more issues with it.

That is the same as the mr issue.

clnt_rdma.c
574–575

I think this is the same double free if it get through a goto path.

We should add mreq = NULL; after m_freem();.

1063–1064

Here!

Add a couple of mxxx = NULL statements to avoid
a double free of the mbuf lists, as suggested by Vinicius.

Also, the code now uses a helper function in the generic
RPC code to remove the reduction mbuf from the argument
mbuf list.

This function is also now used by nfsrpc_readrpc(), but the
patch for this is not in main as yet.