/** * Copyright (c) 2012, University of Konstanz, Distributed Systems Group * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the University of Konstanz nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.jscsi.initiator.connection.phase; import java.nio.ByteBuffer; import org.jscsi.initiator.connection.Connection; import org.jscsi.initiator.connection.ITask; import org.jscsi.initiator.connection.Session; import org.jscsi.initiator.connection.TargetCapacityInformations; import org.jscsi.initiator.connection.state.CapacityRequestState; import org.jscsi.initiator.connection.state.GetConnectionsRequestState; import org.jscsi.initiator.connection.state.LogoutRequestState; import org.jscsi.initiator.connection.state.ReadRequestState; import org.jscsi.initiator.connection.state.WriteRequestState; import org.jscsi.parser.login.LoginStage; import org.jscsi.parser.logout.LogoutRequestParser.LogoutReasonCode; import org.jscsi.parser.scsi.SCSICommandParser.TaskAttributes; /** * <h1>FullFeaturePhase</h1> * <p/> * This class represents the Full-Feature Phase of a session. In this phase all commands are allowed (eg. * read, write, login of further connections, ...). * * @author Volker Wildi */ public final class FullFeaturePhase extends AbstractPhase { // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- /** Number of blocks to read in the first stage of a read operation. */ private static final int READ_FIRST_STAGE_BLOCKS = 64; /** Number of blocks to read in the second stage of a read operation. */ private static final int READ_SECOND_STAGE_BLOCKS = 128; /** Number of blocks to read in the third stage of a read operation. */ private static final int READ_THIRD_STAGE_BLOCKS = 256; /** Number of blocks to read in the first stage of a write operation. */ private static final int WRITE_FIRST_STAGE_BLOCKS = 1024; /** Number of blocks to read in the second stage of a write operation. */ private static final int WRITE_SECOND_STAGE_BLOCKS = 2048; /** Number of blocks to read in the third stage of a write operation. */ private static final int WRITE_THIRD_STAGE_BLOCKS = 4096; // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- /** {@inheritDoc} */ @Override public final boolean login(final Session session) throws Exception { final Connection connection = session.getNextFreeConnection(); // OODRIVE int initiatorTaskTag = connection.getSession().incrementInitiatorTaskTag(); connection.nextState(new GetConnectionsRequestState(connection, initiatorTaskTag), initiatorTaskTag); session.releaseUsedConnection(connection); return true; } // TODO: Uncomment // /** {@inheritDoc} */ // @Override // public final void logoutConnection(final Session session) throws // Exception // { // // final Connection connection = session.getNextFreeConnection(); // connection.setState(new LogoutRequestState(connection, // LogoutReasonCode.CLOSE_CONNECTION)); // connection.execute(); // } /** {@inheritDoc} */ @Override public final boolean logoutSession(final ITask task, final Session session) throws Exception { final Connection connection = session.getNextFreeConnection(); connection.getSession().addOutstandingTask(connection, task); // OODRIVE int initiatorTaskTag = connection.getSession().incrementInitiatorTaskTag() ; connection.nextState(new LogoutRequestState(connection, LogoutReasonCode.CLOSE_SESSION, initiatorTaskTag), initiatorTaskTag); return true; } /** {@inheritDoc} */ @Override public final boolean read(final ITask task, final Session session, final ByteBuffer dst, final int logicalBlockAddress, final long length) throws Exception { if (dst.remaining() < length) { throw new IllegalArgumentException("Destination buffer is too small."); } int startAddress = logicalBlockAddress; final long blockSize = session.getBlockSize(); long totalBlocks = (long)Math.ceil(length / (double)blockSize); long bytes2Process = length; // OODRIVE //final Connection connection = session.getFreeConnection(); final Connection connection = session.peekFreeConnection(); // If connection is not free, wait for it if (connection == null){ session.getNextFreeConnection(); } connection.getSession().addOutstandingTask(connection, task); // first stage short blocks = (short)Math.min(READ_FIRST_STAGE_BLOCKS, totalBlocks); if (LOGGER.isInfoEnabled()) { LOGGER.info("Now reading sequences of length " + blocks + " blocks."); } // OODRIVE final int initiatorTaskTag = connection.getSession().incrementInitiatorTaskTag() ; connection.nextState(new ReadRequestState(connection, dst, TaskAttributes.SIMPLE, (int)Math.min( bytes2Process, blocks * blockSize), startAddress, blocks,initiatorTaskTag ), initiatorTaskTag); startAddress += blocks; totalBlocks -= blocks; bytes2Process -= blocks * blockSize; // second stage blocks = (short)Math.min(READ_SECOND_STAGE_BLOCKS, totalBlocks); if (blocks > 0) { if (LOGGER.isInfoEnabled()) { LOGGER.info("Now reading sequences of length " + blocks + " blocks."); } // OODRIVE connection.nextState(new ReadRequestState(connection, dst, TaskAttributes.SIMPLE, (int)Math.min( bytes2Process, blocks * blockSize), startAddress, blocks, initiatorTaskTag), initiatorTaskTag); startAddress += blocks; totalBlocks -= blocks; bytes2Process -= blocks * blockSize; } // third stage blocks = (short)Math.min(READ_THIRD_STAGE_BLOCKS, totalBlocks); while (blocks > 0) { if (LOGGER.isInfoEnabled()) { LOGGER.info("Now reading sequences of length " + blocks + " blocks."); } // OODRIVE connection.nextState(new ReadRequestState(connection, dst, TaskAttributes.SIMPLE, (int)Math.min( bytes2Process, blocks * blockSize), startAddress, blocks, initiatorTaskTag), initiatorTaskTag); startAddress += blocks; totalBlocks -= blocks; blocks = (short)Math.min(READ_THIRD_STAGE_BLOCKS, totalBlocks); } return true; } /** {@inheritDoc} */ @Override public final boolean write(final ITask task, final Session session, final ByteBuffer src, final int logicalBlockAddress, final long length) throws Exception { if (src.remaining() < length) { throw new IllegalArgumentException("Source buffer is too small. Buffer size: " + src.remaining() + " Expected: " + length); } int startAddress = logicalBlockAddress; final long blockSize = session.getBlockSize(); int totalBlocks = (int)Math.ceil(length / (double)blockSize); long bytes2Process = length; int bufferPosition = 0; // OODRIVE //final Connection connection = session.getFreeConnection(); final Connection connection = session.peekFreeConnection(); // If connection is not free, wait for it if (connection == null){ session.getNextFreeConnection(); } connection.getSession().addOutstandingTask(connection, task); // first stage short blocks = (short)Math.min(WRITE_FIRST_STAGE_BLOCKS, totalBlocks); if (LOGGER.isInfoEnabled()) { LOGGER.info("Now sending sequences of length " + blocks + " blocks."); } int expectedDataTransferLength = (int)Math.min(bytes2Process, blocks * blockSize); //OODRIVE final int initiatorTaskTag = connection.getSession().incrementInitiatorTaskTag(); connection.nextState(new WriteRequestState(connection, src, bufferPosition, TaskAttributes.SIMPLE, expectedDataTransferLength, startAddress, blocks, initiatorTaskTag), initiatorTaskTag); startAddress += blocks; totalBlocks -= blocks; bytes2Process -= blocks * blockSize; bufferPosition += expectedDataTransferLength; // second stage blocks = (short)Math.min(WRITE_SECOND_STAGE_BLOCKS, totalBlocks); if (blocks > 0) { if (LOGGER.isInfoEnabled()) { LOGGER.info("Now sending sequences of length " + blocks + " blocks."); LOGGER.info("Remaining, DataSegmentLength: " + bytes2Process + ", " + expectedDataTransferLength); } expectedDataTransferLength = (int)Math.min(bytes2Process, blocks * blockSize); //OODRIVE connection.nextState(new WriteRequestState(connection, src, bufferPosition, TaskAttributes.SIMPLE, expectedDataTransferLength, startAddress, blocks, initiatorTaskTag), initiatorTaskTag); startAddress += blocks; totalBlocks -= blocks; bytes2Process -= blocks * blockSize; bufferPosition += expectedDataTransferLength; } // third stage blocks = (short)Math.min(WRITE_THIRD_STAGE_BLOCKS, totalBlocks); while (blocks > 0) { if (LOGGER.isInfoEnabled()) { LOGGER.info("Now sending sequences of length " + blocks + " blocks."); } expectedDataTransferLength = (int)Math.min(bytes2Process, blocks * blockSize); //OODRIVE connection.nextState(new WriteRequestState(connection, src, bufferPosition, TaskAttributes.SIMPLE, expectedDataTransferLength, startAddress, blocks, initiatorTaskTag), initiatorTaskTag); startAddress += blocks; totalBlocks -= blocks; blocks = (short)Math.min(READ_THIRD_STAGE_BLOCKS, totalBlocks); bufferPosition += expectedDataTransferLength; } return true; } /** {@inheritDoc} */ @Override public final boolean getCapacity(final Session session, final TargetCapacityInformations capacityInformation) throws Exception { if (capacityInformation == null) { throw new NullPointerException(); } final Connection connection = session.getNextFreeConnection(); if (connection == null) { throw new NullPointerException(); } // OODRIVE int initiatorTaskTag = connection.getSession().incrementInitiatorTaskTag() ; connection .nextState(new CapacityRequestState(connection, capacityInformation, TaskAttributes.SIMPLE, initiatorTaskTag), initiatorTaskTag); session.releaseUsedConnection(connection); return true; } // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- /** {@inheritDoc} */ @Override public final LoginStage getStage() { return LoginStage.FULL_FEATURE_PHASE; } // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- }