/** * 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.parser.tmf; import org.jscsi.exception.InternetSCSIException; import org.jscsi.parser.BasicHeaderSegment; import org.jscsi.parser.Constants; import org.jscsi.parser.InitiatorMessageParser; import org.jscsi.parser.ProtocolDataUnit; import org.jscsi.parser.datasegment.DataSegmentFactory.DataSegmentFormat; import org.jscsi.utils.Utils; import com.carrotsearch.hppc.ByteObjectOpenHashMap; /** * <h1>TaskManagementFunctionRequestParser</h1> * <p> * This class parses a Task Management Function Request message defined in the iSCSI Standard (RFC3720). * <p> * <h4>TotalAHSLength and DataSegmentLength</h4> For this PDU TotalAHSLength and DataSegmentLength MUST be * <code>0</code>. * <p> * <h4>LUN</h4> This field is required for functions that address a specific LU (ABORT TASK, CLEAR TASK SET, * ABORT TASK SET, CLEAR ACA, LOGICAL UNIT RESET) and is reserved in all others. * <p> * * @author Volker Wildi */ public final class TaskManagementFunctionRequestParser extends InitiatorMessageParser { // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- /** * This enumeration defines all valid function codes, which are defined in * the iSCSI standard (RFC3720). */ public static enum FunctionCode { /** * This is an artificial function code, which is only used to indicate a * not set function code. */ UNSET((byte)0), /** Aborts the task identified by the Referenced Task Tag field. */ ABORT_TASK((byte)1), /** Aborts all Tasks issued via this session on the logical unit. */ ABORT_TASK_SET((byte)2), /** Clears the Auto Contingent Allegiance condition. */ CLEAR_ACA((byte)3); private byte value; private static ByteObjectOpenHashMap<FunctionCode> mapping; static { FunctionCode.mapping = new ByteObjectOpenHashMap<FunctionCode>(values().length); for (FunctionCode s : values()) { FunctionCode.mapping.put(s.value, s); } } private FunctionCode(final byte newValue) { value = newValue; } /** * Returns the value of this enumeration. * * @return The value of this enumeration. */ public final byte value() { return value; } /** * Returns the constant defined for the given <code>value</code>. * * @param value * The value to search for. * @return The constant defined for the given <code>value</code>. Or <code>null</code>, if this value * is not defined by this * enumeration. */ public static final FunctionCode valueOf(final byte value) { return FunctionCode.mapping.get(value); } } // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- /** The function code. */ private FunctionCode functionCode; /** The referenced task tag. */ private int referencedTaskTag; /** The referenced command sequence number. */ private int refCmdSN; /** The expected data sequence number. */ private int expDataSN; // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- /** * Default constructor, creates a new, empty <code>TaskManagementFunctionRequestParser</code> object. * * @param initProtocolDataUnit * The reference <code>ProtocolDataUnit</code> instance, which * contains this <code>TaskManagementFunctionRequestParser</code> subclass object. */ public TaskManagementFunctionRequestParser(final ProtocolDataUnit initProtocolDataUnit) { super(initProtocolDataUnit); } // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- /** {@inheritDoc} */ @Override public final String toString() { final StringBuilder sb = new StringBuilder(Constants.LOG_INITIAL_SIZE); // Utils.printField(sb, "Function", functionCode.value(), 1); Utils.printField(sb, "LUN", logicalUnitNumber, 1); Utils.printField(sb, "Referenced Task Tag", referencedTaskTag, 1); sb.append(super.toString()); Utils.printField(sb, "RefCmdSN", refCmdSN, 1); Utils.printField(sb, "ExpDataSN", expDataSN, 1); return sb.toString(); } /** {@inheritDoc} */ @Override public final DataSegmentFormat getDataSegmentFormat() { return DataSegmentFormat.NONE; } /** {@inheritDoc} */ @Override public final void clear() { super.clear(); functionCode = FunctionCode.UNSET; referencedTaskTag = 0x00000000; refCmdSN = 0x00000000; expDataSN = 0x00000000; } // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- /** * For recovery purposes, the iSCSI target and initiator maintain a data * acknowledgement reference number - the first input DataSN number * unacknowledged by the initiator. When issuing a new command, this number * is set to <code>0</code>. If the function is TASK REASSIGN, which * establishes a new connection allegiance for a previously issued Read or * Bidirectional command, <code>ExpDataSN</code> will contain an updated * data acknowledgement reference number or the value <code>0</code>; the * latter indicating that the data acknowledgement reference number is * unchanged. The initiator MUST discard any data PDUs from the previous * execution that it did not acknowledge and the target MUST transmit all * Data-In PDUs (if any) starting with the data acknowledgement reference * number. The number of retransmitted PDUs may or may not be the same as * the original transmission depending on if there was a change in * MaxRecvDataSegmentLength in the reassignment. The target MAY also send no * more Data-In PDUs if all data has been acknowledged. * <p/> * The value of <code>ExpDataSN</code> MUST be <code>0</code> or higher than the <code>DataSN</code> of * the last acknowledged Data-In PDU, but not larger than <code>DataSN+1</code> of the last Data-In PDU * sent by the target. Any other value MUST be ignored by the target. * <p/> * For other functions this field is reserved. * <p/> * * @return The expected data sequence number of this <code>TaskManagementFunctionRequestParser</code> * object. */ public final int getExpDataSN() { return expDataSN; } /** * The Task Management functions provide an initiator with a way to * explicitly control the execution of one or more Tasks (SCSI and iSCSI * tasks). The Task Management function codes are listed below. For a more * detailed description of SCSI task management, see [SAM2]. * <p> * <table border="1"> * <tr> * <th>Function Code</th> * <th>Description</th> * </tr> * <tr> * <td>1</td> * <td>ABORT TASK - aborts the task identified by the Referenced Task Tag field.</td> * </tr> * <tr> * <td>2</td> * <td>ABORT TASK SET - aborts all Tasks issued via this session on the logical unit.</td> * </tr> * <tr> * <td>3</td> * <td>CLEAR ACA - clears the Auto Contingent Allegiance condition.</td> * </tr> * <tr> * <td>4</td> * <td>CLEAR TASK SET - aborts all Tasks in the appropriate task set as defined by the TST field in the * Control mode page (see [SPC3]).</td> * </tr> * <tr> * <td>5</td> * <td>LOGICAL UNIT RESET</td> * </tr> * <tr> * <td>6</td> * <td>TARGET WARM RESET</td> * </tr> * <tr> * <td>7</td> * <td>TARGET COLD RESET</td> * </tr> * <tr> * <td>8</td> * <td>TASK REASSIGN - reassigns connection allegiance for the task identified by the Referenced Task Tag * field to this connection, thus resuming the iSCSI exchanges for the task.</td> * </tr> * </table> * <p> * For all these functions, the Task Management function response MUST be returned as detailed in Section * 10.6 Task Management Function Response. All these functions apply to the referenced tasks regardless of * whether they are proper SCSI tasks or tagged iSCSI operations. Task management requests must act on all * the commands from the same session having a CmdSN lower than the task management CmdSN. LOGICAL UNIT * RESET, TARGET WARM RESET and TARGET COLD RESET may affect commands from other sessions or commands from * the same session with CmdSN equal or exceeding CmdSN. * <p> * If the task management request is marked for immediate delivery, it must be considered immediately for * execution, but the operations involved (all or part of them) may be postponed to allow the target to * receive all relevant tasks. According to [SAM2], for all the tasks covered by the Task Management * response (i.e., with CmdSN lower than the task management command CmdSN) but except the Task Management * response to a TASK REASSIGN, additional responses MUST NOT be delivered to the SCSI layer after the * Task Management response. The iSCSI initiator MAY deliver to the SCSI layer all responses received * before the Task Management response (i.e., it is a matter of implementation if the SCSI responses, * received before the Task Management response but after the task management request was issued, are * delivered to the SCSI layer by the iSCSI layer in the initiator). The iSCSI target MUST ensure that no * responses for the tasks covered by a task management function are delivered to the iSCSI initiator * after the Task Management response except for a task covered by a TASK REASSIGN. * <p> * For ABORT TASK SET and CLEAR TASK SET, the issuing initiator MUST continue to respond to all valid * target transfer tags (received via R2T, Text Response, NOP-In, or SCSI Data-In PDUs) related to the * affected task set, even after issuing the task management request. The issuing initiator SHOULD however * terminate (i.e., by setting the F-bit to 1) these response sequences as quickly as possible. The target * on its part MUST wait for responses on all affected target transfer tags before acting on either of * these two task management requests. In case all or part of the response sequence is not received (due * to digest errors) for a valid TTT, the target MAY treat it as a case of within-command error recovery * class (see Section 6.1.4.1 Recovery Within-command) if it is supporting ErrorRecoveryLevel >= 1, or * alternatively may drop the connection to complete the requested task set function. * <p> * If an ABORT TASK is issued for a task created by an immediate command then RefCmdSN MUST be that of the * Task Management request itself (i.e., CmdSN and RefCmdSN are equal); otherwise RefCmdSN MUST be set to * the CmdSN of the task to be aborted (lower than CmdSN). * <p> * If the connection is still active (it is not undergoing an implicit or explicit logout), ABORT TASK * MUST be issued on the same connection to which the task to be aborted is allegiant at the time the Task * Management Request is issued. If the connection is implicitly or explicitly logged out (i.e., no other * request will be issued on the failing connection and no other response will be received on the failing * connection), then an ABORT TASK function request may be issued on another connection. This Task * Management request will then establish a new allegiance for the command to be aborted as well as abort * it (i.e., the task to be aborted will not have to be retried or reassigned, and its status, if issued * but not acknowledged, will be reissued followed by the Task Management response). * <p> * At the target an ABORT TASK function MUST NOT be executed on a Task Management request; such a request * MUST result in Task Management response of "Function rejected". * <p> * For the LOGICAL UNIT RESET function, the target MUST behave as dictated by the Logical Unit Reset * function in [SAM2]. * <p> * The implementation of the TARGET WARM RESET function and the TARGET COLD RESET function is OPTIONAL and * when implemented, should act as described below. The TARGET WARM RESET is also subject to SCSI access * controls on the requesting initiator as defined in [SPC3]. When authorization fails at the target, the * appropriate response as described in Section 10.6 Task Management Function Response MUST be returned by * the target. The TARGET COLD RESET function is not subject to SCSI access controls, but its execution * privileges may be managed by iSCSI mechanisms such as login authentication. * <p> * When executing the TARGET WARM RESET and TARGET COLD RESET functions, the target cancels all pending * operations on all Logical Units known by the issuing initiator. Both functions are equivalent to the * Target Reset function specified by [SAM2]. They can affect many other initiators logged in with the * servicing SCSI target port. * <p> * The target MUST treat the TARGET COLD RESET function additionally as a power on event, thus terminating * all of its TCP connections to all initiators (all sessions are terminated). For this reason, the * Service Response (defined by [SAM2]) for this SCSI task management function may not be reliably * delivered to the issuing initiator port. For the TASK REASSIGN function, the target should reassign the * connection allegiance to this new connection (and thus resume iSCSI exchanges for the task). TASK * REASSIGN MUST ONLY be received by the target after the connection on which the command was previously * executing has been successfully logged-out. The Task Management response MUST be issued before the * reassignment becomes effective. For additional usage semantics see Section 6.2 Retry and Reassign in * Recovery. * <p> * At the target a TASK REASSIGN function request MUST NOT be executed to reassign the connection * allegiance of a Task Management function request, an active text negotiation task, or a Logout task; * such a request MUST result in Task Management response of "Function rejected". * <p> * TASK REASSIGN MUST be issued as an immediate command. * * @return The function code of this <code>TaskManagementFunctionRequestParser</code> object. */ public final FunctionCode getFunction() { return functionCode; } /** * If an ABORT TASK is issued for a task created by an immediate command * then RefCmdSN MUST be that of the Task Management request itself (i.e., <code>CmdSN</code> and * <code>RefCmdSN</code> are equal). * <p> * For an ABORT TASK of a task created by non-immediate command <code>RefCmdSN</code> MUST be set to the * <code>CmdSN</code> of the task identified by the Referenced Task Tag field. Targets must use this field * as described in section 10.6.1 when the task identified by the Referenced Task Tag field is not with * the target. * <p> * Otherwise, this field is reserved. * * @return The referenced command sequence number of this <code>TaskManagementFunctionRequestParser</code> * object. */ public final int getRefCmdSN() { return refCmdSN; } /** * The Initiator Task Tag of the task to be aborted for the ABORT TASK * function or reassigned for the TASK REASSIGN function. For all the other * functions this field MUST be set to the reserved value <code>0xffffffff</code>. * * @return The referenced task tag of this <code>TaskManagementFunctionRequestParser</code> object. */ public final int getReferencedTaskTag() { return referencedTaskTag; } // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- /** {@inheritDoc} */ @Override protected final void deserializeBytes1to3(final int line) throws InternetSCSIException { functionCode = FunctionCode.valueOf((byte)(line & Constants.SECOND_BYTE_MASK)); Utils.isReserved(line & Constants.LAST_TWO_BYTES_MASK); } /** {@inheritDoc} */ @Override protected final void deserializeBytes20to23(final int line) throws InternetSCSIException { referencedTaskTag = line; } /** {@inheritDoc} */ @Override protected final void deserializeBytes32to35(final int line) throws InternetSCSIException { refCmdSN = line; } /** {@inheritDoc} */ @Override protected final void deserializeBytes36to39(final int line) throws InternetSCSIException { expDataSN = line; } // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- /** {@inheritDoc} */ @Override protected final void checkIntegrity() throws InternetSCSIException { String exceptionMessage; do { BasicHeaderSegment bhs = protocolDataUnit.getBasicHeaderSegment(); if (bhs.getTotalAHSLength() != 0) { exceptionMessage = "TotalAHSLength must be 0!"; break; } if (bhs.getDataSegmentLength() != 0) { exceptionMessage = "DataSegmentLength must be 0!"; break; } if (functionCode != FunctionCode.ABORT_TASK && functionCode != FunctionCode.ABORT_TASK_SET && functionCode != FunctionCode.CLEAR_ACA) { Utils.isReserved(logicalUnitNumber); } // message is checked correctly return; } while (false); throw new InternetSCSIException(exceptionMessage); } // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- /** {@inheritDoc} */ @Override protected final int serializeBytes1to3() { return functionCode.value() << Constants.TWO_BYTES_SHIFT; } /** {@inheritDoc} */ @Override protected final int serializeBytes20to23() { return referencedTaskTag; } /** {@inheritDoc} */ @Override protected final int serializeBytes32to35() { return refCmdSN; } /** {@inheritDoc} */ @Override protected final int serializeBytes36to39() { return expDataSN; } // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- }