[OpenAFS-devel] Cache corruption with RX busy code

Simon Wilkinson sxw@your-file-system.com
Fri, 12 Apr 2013 20:59:27 +0100


Hi,

I've been debugging a problem with the new RX packet busy code, that I =
thought it was worth discussing more widely.

Various things can cause a client and server to have differing views on =
the available call channels. When the client attempts to use a call =
channel that the server thinks is in use, the server responds with a =
BUSY packet. Originally, the client would just ignore this. It would =
then look like the server wasn't responding, and the client would keep =
retrying on that channel until either the call timed out, or the channel =
on the server was freed.

54fb96d2b6517ae491fd7a7c03246850d29156d5 changed this behaviour so that =
if a call is stuck on a busy channel, and there are other free channels =
on the connection, we time out the busy call, and allow the client to =
try again on a different channel. However, this opens up a potential =
race which can lead to cache corruption. The race permits the first =
attempt at the call to succeed on the server, but to appear to fail to =
the client. The client then retries the call, believing it failed. With =
operations that modify server state, this second attempt can fail =
because the change has already happened. The failure is returned to the =
user, and the client's cache isn't updated. So, the AFS cache no longer =
reflects the state on the server.

The race is as follows:

Client					Server
=09
Sends 1st pkt of call to server	=09
					Receives 1st packet, but channel =
busy
					    sets error on old call
					    sends BUSY packet to client
RTT expires
    resends 1st pkt
					Old call terminates
Receives BUSY packet
    sets call busy flag
					Receives resent packet
					    starts to processes call
					    sends ACK for resent packet
RTT expires
    checks busy flag
    sets RX_CALL_BUSY error
					Sends response packet
Receives ACK packet
    discards it as call destroyed=20

Receives response packet
    discards it as call destroyed

This sequence of events gives a call that succeeds on the server, but =
fails on the client. The retry then puts the cache into an inconsistent =
state.

This is exactly the same problem as we encountered with idle dead =
processing. A client can't unilaterally terminate an RPC to a server, =
because it actually has no way of knowing whether the RPC succeeded or =
not. If the client does terminate an RPC, it needs to invalidate all of =
the cached state that that client may have touched.

The question is whether just adding more cases where we invalidate the =
cache is the right approach, or whether we should reconsider the BUSY =
behaviour.

Cheers,

Simon