Commit d9e1019f authored by Tigran Mkrtchyan's avatar Tigran Mkrtchyan
Browse files

nfs: add session reply cache handling

NFSv4.1 defines client managed reply cache identified
by slot id:

http://www.nfsv4-editor.org/draft-25/draft-ietf-nfsv4-minorversion1-25.html#Slot Identifiers and Server Reply Cache

Acked-by: Gerd
parent dd44d064
......@@ -54,6 +54,7 @@ public class NFSServerV41 extends nfs4_prot_NFS4_PROGRAM_ServerStub {
List<nfs_resop4> v = new ArrayList<nfs_resop4>(arg1.argarray.length);
if (arg1.minorversion.value > 1) {
res.status = nfsstat4.NFS4ERR_MINOR_VERS_MISMATCH;
res.resarray = new nfs_resop4[0];
_log.log(Level.FINE, " : NFS4ERR_MINOR_VERS_MISMATCH");
} else {
......@@ -72,18 +73,20 @@ public class NFSServerV41 extends nfs4_prot_NFS4_PROGRAM_ServerStub {
} catch (ChimeraNFSException he) {
_log.fine("CURFH: NULL");
}
v = context.processedOperations();
res.resarray = v.toArray(new nfs_resop4[v.size()]);
// result status must be equivalent
// to the status of the last operation that
// was executed within the COMPOUND procedure
if(!v.isEmpty()) {
res.status = res.resarray[res.resarray.length - 1].getStatus();
}else{
res.status = nfsstat4.NFS4_OK;
}
}
res.tag = arg1.tag;
res.resarray = v.toArray(new nfs_resop4[v.size()]);
// result status must be equivalent
// to the status of the last operation that
// was executed within the COMPOUND procedure
res.status = res.resarray[res.resarray.length-1].getStatus();
_log.log(Level.FINE, "OP: {1} status: {1}", new Object[]{res.tag, res.status});
} catch (Exception e) {
......
package org.dcache.chimera.nfs.v4;
import org.dcache.chimera.nfs.v4.xdr.nfsstat4;
import java.util.List;
import org.dcache.chimera.nfs.ChimeraNFSException;
import java.util.concurrent.atomic.AtomicLong;
import org.dcache.chimera.nfs.v4.xdr.nfs_resop4;
import org.dcache.chimera.nfs.v4.xdr.nfsstat4;
public class NFSv41Session {
private static final AtomicLong SESSIONS = new AtomicLong(0);
private int _sequenceID = 0;
private final byte[] _session = (Long.toString(SESSIONS.incrementAndGet()) + "###############").getBytes();
// FIXME: here have to be real cache
private final int _slots[] = new int[200];
/**
* Session reply slots.
*/
private final SessionSlot[] _slots;
private final NFS4Client _client;
public NFSv41Session(NFS4Client client) {
public NFSv41Session(NFS4Client client, int replyCacheSize) {
_client = client;
_sequenceID = _client.currentSeqID();
_slots = new SessionSlot[replyCacheSize];
}
public byte[] id() {
......@@ -29,54 +31,45 @@ public class NFSv41Session {
return id;
}
public int nextSequenceID() {
return ++_sequenceID;
}
public int sequenceID() {
return _sequenceID;
}
public NFS4Client getClient() {
return _client;
}
public int slotMax() {
return _slots.length -1;
}
/**
* Get maximum slot id.
* @return max slot id.
*/
public int slotMax() {
return _slots.length - 1;
}
public boolean updateSlot(int slot, int sequence, List<nfs_resop4> reply) throws ChimeraNFSException {
return getSlot(slot).update(sequence, reply);
}
public void setSlot(int slot, int sequence) throws ChimeraNFSException {
/**
* Get cache slot for given id.
* @param i
* @return cache slot.
* @throws ChimeraNFSException
*/
SessionSlot getSlot(int slot) throws ChimeraNFSException {
if( slot >= _slots.length ) {
if (slot > slotMax()) {
throw new ChimeraNFSException(nfsstat4.NFS4ERR_BADSLOT, "slot id overflow");
}
/*
* According to spec.
*
* If the previous sequence id was 0xFFFFFFFF,
* then the next request for the slot MUST have
* the sequence id set to zero.
*/
int validValue;
if( _slots[slot] == 0xFFFFFFFF ) {
validValue = 0;
}else{
validValue = _slots[slot] + 1;
}
if( sequence != validValue ) {
throw new ChimeraNFSException(nfsstat4.NFS4ERR_SEQ_MISORDERED, "slot["+ slot +"] disordered : v/n : " + Integer.toHexString(validValue) +"/" +Integer.toHexString(sequence) );
}
_slots[slot] = sequence;
}
if (_slots[slot] == null) {
_slots[slot] = new SessionSlot();
}
return _slots[slot];
}
@Override
public String toString() {
String s = String.format("Session: [%s], sequence: [%d]", new String(_session), _sequenceID);
String s = String.format("Session: [%s]", new String(_session));
return s;
}
}
......@@ -113,7 +113,7 @@ public class OperationCREATE_SESSION extends AbstractNFSv4Operation {
throw new ChimeraNFSException(nfsstat4.NFS4ERR_SEQ_MISORDERED, "bad sequence id: " + client.currentSeqID() + " / " + seqId);
}
session = new NFSv41Session(client);
session = new NFSv41Session(client,_args.opcreate_session.csa_fore_chan_attrs.ca_maxrequests.value.value );
client.addSession( session);
_log.log(Level.FINE, "adding new session [{0}]", new String(session.id()) );
NFSv4StateHandler.getInstace().sessionById(session.id(), session);
......@@ -131,7 +131,14 @@ public class OperationCREATE_SESSION extends AbstractNFSv4Operation {
res.csr_resok4.csr_sessionid = new sessionid4( session.id() );
res.csr_resok4.csr_sequence = _args.opcreate_session.csa_sequence;
res.csr_resok4.csr_sequence = new sequenceid4( new uint32_t( session.sequenceID() ) );
/**
* from spec:
*
* Once the session is created, the first SEQUENCE or CB_SEQUENCE
* received on a slot MUST have a sequence ID equal to 1;
* if not the server MUST return NFS4ERR_SEQ_MISORDERED.
*/
res.csr_resok4.csr_sequence = new sequenceid4( new uint32_t( 1 ) );
/* we do not support callback connections on the same line*/
res.csr_resok4.csr_flags = new uint32_t( _args.opcreate_session.csa_flags.value ^ nfs4_prot.CREATE_SESSION4_FLAG_CONN_BACK_CHAN);
......
package org.dcache.chimera.nfs.v4;
import java.util.List;
import org.dcache.chimera.nfs.v4.xdr.nfsstat4;
import org.dcache.chimera.nfs.v4.xdr.sessionid4;
import org.dcache.chimera.nfs.v4.xdr.uint32_t;
......@@ -11,7 +12,7 @@ import org.dcache.chimera.nfs.v4.xdr.SEQUENCE4resok;
import org.dcache.chimera.nfs.ChimeraNFSException;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.dcache.chimera.nfs.v4.xdr.sequenceid4;
import org.dcache.chimera.nfs.v4.xdr.nfs_resop4;
public class OperationSEQUENCE extends AbstractNFSv4Operation {
......@@ -76,7 +77,19 @@ public class OperationSEQUENCE extends AbstractNFSv4Operation {
*
*/
if( _trackSession ) {
session.setSlot(_args.opsequence.sa_slotid.value.value,_args.opsequence.sa_sequenceid.value.value );
List<nfs_resop4> reply;
if(_args.opsequence.sa_cachethis) {
reply = context.processedOperations();
}else{
reply = null;
}
if(session.updateSlot(_args.opsequence.sa_slotid.value.value,_args.opsequence.sa_sequenceid.value.value , reply) ) {
/*
* retransmit + cached reply available.
* Stop processing.
*/
return false;
}
session.getClient().updateLeaseTime(NFSv4Defaults.NFS4_LEASE_TIME);
}
context.setSession(session);
......
/*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program (see the file COPYING.LIB for more
* details); if not, write to the Free Software Foundation, Inc.,
* 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package org.dcache.chimera.nfs.v4;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.dcache.chimera.nfs.ChimeraNFSException;
import org.dcache.chimera.nfs.v4.xdr.nfs_resop4;
import org.dcache.chimera.nfs.v4.xdr.nfsstat4;
/**
*
*/
public class SessionSlot {
private static final Logger _log = Logger.getLogger(SessionSlot.class.getName());
private int _sequence;
private List<nfs_resop4> _reply;
SessionSlot(int sequence) {
_sequence = sequence;
}
public SessionSlot() {
this(0);
}
/**
*
* @param sequence
* @param reply
* @return true if retransmit is detected and cached reply available.
* @throws ChimeraNFSException
*/
boolean update(int sequence, List<nfs_resop4> reply) throws ChimeraNFSException {
if( sequence == _sequence ) {
_log.log(Level.INFO, "retransmit detected");
if( _reply != null ) {
_log.log(Level.INFO, "using cached reply");
reply.clear();
reply.addAll(_reply);
return true;
}else{
throw new ChimeraNFSException(nfsstat4.NFS4ERR_RETRY_UNCACHED_REP,
"Uncached reply retry");
}
}
/*
* According to spec.
*
* If the previous sequence id was 0xFFFFFFFF,
* then the next request for the slot MUST have
* the sequence id set to zero.
*/
int validValue;
if (_sequence == 0xFFFFFFFF) {
validValue = 0;
} else {
validValue = _sequence + 1;
}
if (sequence != validValue) {
throw new ChimeraNFSException(nfsstat4.NFS4ERR_SEQ_MISORDERED,
"disordered : v/n : " + Integer.toHexString(validValue) +
"/" + Integer.toHexString(sequence));
}
_sequence = sequence;
_reply = reply;
return false;
}
List<nfs_resop4> reply() {
return _reply;
}
int sequence() {
return _sequence;
}
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment