View Javadoc

1   package org.jscsi.target.connection.stage.fullfeature;
2   
3   
4   import static org.jscsi.target.storage.IStorageModule.VIRTUAL_BLOCK_SIZE;
5   
6   import java.io.IOException;
7   import java.nio.ByteBuffer;
8   
9   import org.jscsi.exception.InternetSCSIException;
10  import org.jscsi.parser.BasicHeaderSegment;
11  import org.jscsi.parser.ProtocolDataUnit;
12  import org.jscsi.parser.scsi.SCSICommandParser;
13  import org.jscsi.parser.scsi.SCSIResponseParser.ServiceResponse;
14  import org.jscsi.parser.scsi.SCSIStatus;
15  import org.jscsi.target.connection.TargetPduFactory;
16  import org.jscsi.target.connection.phase.TargetFullFeaturePhase;
17  import org.jscsi.target.scsi.ScsiResponseDataSegment;
18  import org.jscsi.target.scsi.cdb.Read10Cdb;
19  import org.jscsi.target.scsi.cdb.Read6Cdb;
20  import org.jscsi.target.scsi.cdb.ReadCdb;
21  import org.jscsi.target.scsi.cdb.ScsiOperationCode;
22  import org.jscsi.target.settings.SettingsException;
23  import org.slf4j.Logger;
24  import org.slf4j.LoggerFactory;
25  
26  
27  /**
28   * A stage for processing <code>READ (6)</code> and <code>READ (10)</code> SCSI commands.
29   * 
30   * @author Andreas Ergenzinger
31   */
32  public class ReadStage extends ReadOrWriteStage {
33  
34      private static final Logger LOGGER = LoggerFactory.getLogger(ReadStage.class);
35  
36      public ReadStage (final TargetFullFeaturePhase targetFullFeaturePhase) {
37          super(targetFullFeaturePhase);
38      }
39  
40      @Override
41      public void execute (ProtocolDataUnit pdu) throws IOException , InterruptedException , InternetSCSIException , SettingsException {
42  
43          // get relevant variables ...
44          // ... from settings
45          final boolean immediateData = settings.getImmediateData();
46  
47          if (LOGGER.isDebugEnabled()) {
48              LOGGER.debug("immediateData = " + immediateData);
49              LOGGER.debug("maxRecvDataSegmentLength = " + settings.getMaxRecvDataSegmentLength());
50          }
51  
52          // ... and from the PDU
53          BasicHeaderSegment bhs = pdu.getBasicHeaderSegment();
54          SCSICommandParser parser = (SCSICommandParser) bhs.getParser();
55          final int initiatorTaskTag = bhs.getInitiatorTaskTag();
56  
57          // get the Read(6) or Read(10) CDB
58          ReadCdb cdb;
59          final ScsiOperationCode scsiOpCode = ScsiOperationCode.valueOf(parser.getCDB().get(0));
60          if (scsiOpCode == ScsiOperationCode.READ_10)// most likely option first
61              cdb = new Read10Cdb(parser.getCDB());
62          else if (scsiOpCode == ScsiOperationCode.READ_6)
63              cdb = new Read6Cdb(parser.getCDB());
64          else {
65              // anything else wouldn't be good (programmer error)
66              // close connection
67              throw new InternetSCSIException("wrong SCSI Operation Code " + scsiOpCode + " in ReadStage");
68          }
69  
70          // check if requested blocks are out of bounds
71          checkOverAndUnderflow(cdb);
72  
73          // check illegal field pointers
74          if (cdb.getIllegalFieldPointers() != null) {
75              // the command must fail
76  
77              LOGGER.debug("illegal field in Read CDB");
78  
79              // create and send error PDU and leave stage
80              final ProtocolDataUnit responsePdu = createFixedFormatErrorPdu(cdb.getIllegalFieldPointers(),// senseKeySpecificData
81                      initiatorTaskTag, parser.getExpectedDataTransferLength());
82              connection.sendPdu(responsePdu);
83              return;
84          }
85  
86          final int totalTransferLength = VIRTUAL_BLOCK_SIZE * cdb.getTransferLength();
87          final long storageOffset = VIRTUAL_BLOCK_SIZE * cdb.getLogicalBlockAddress();
88  
89          if (LOGGER.isDebugEnabled()) {
90              LOGGER.debug("cdb.getLogicalBlockAddress() = " + cdb.getLogicalBlockAddress());
91              LOGGER.debug("blockSize = " + VIRTUAL_BLOCK_SIZE);
92              LOGGER.debug("totalTransferLength = " + totalTransferLength);
93              LOGGER.debug("expectedDataSegmentLength = " + parser.getExpectedDataTransferLength());
94          }
95  
96          // *** start sending ***
97          // initialize counters and data segment buffer
98          int bytesSent = 0;
99          int dataSequenceNumber = 0;
100         byte[] dataSegmentArray = null;
101         ByteBuffer dataSegment = null;
102         ProtocolDataUnit responsePdu;
103 
104         // *** send up to last but one Data-In PDU ***
105         // (with DataSegmentSize == MaxRecvDataSegmentLength)
106 
107         if (bytesSent < totalTransferLength - settings.getMaxRecvDataSegmentLength()) {
108             /*
109              * Initialize dataSegmentArray and dataSegment with MaxRecvDataSegmentLength bytes.
110              */
111             dataSegmentArray = connection.getDataInArray(settings.getMaxRecvDataSegmentLength());
112             dataSegment = ByteBuffer.wrap(dataSegmentArray);
113         }
114 
115         while (bytesSent < totalTransferLength - settings.getMaxRecvDataSegmentLength()) {
116 
117             // get data and prepare data segment
118             session.getStorageModule().read(dataSegmentArray, storageOffset + bytesSent);
119 
120             // create and send PDU
121             responsePdu = TargetPduFactory.createDataInPdu(false,// finalFlag,
122                                                                  // not the last
123                                                                  // PDU with
124                                                                  // data payload
125                                                                  // in the
126                                                                  // sequence
127                     false,// acknowledgeFlag, ErrorRecoveryLevel == 0, so we
128                           // never do that
129                     false,// residualOverflowFlag
130                     false,// residualUnderflowFlag
131                     false,// statusFlag
132                     SCSIStatus.GOOD,// status, actually reserved i.e. 0x0
133                     0L,// logicalUnitNumber, reserved
134                     initiatorTaskTag, 0xffffffff,// targetTransferTag
135                     dataSequenceNumber,// dataSequenceNumber
136                     bytesSent,// bufferOffset
137                     0,// residualCount
138                     dataSegment);
139 
140             connection.sendPdu(responsePdu);
141 
142             // increment counters
143             ++dataSequenceNumber;
144             bytesSent += settings.getMaxRecvDataSegmentLength();
145         }
146 
147         /*
148          * If ImmediateData=Yes has been negotiated, then a phase collapse has to take place, i.e. the status is sent in
149          * the last Data-In PDU. Otherwise a separate SCSI Response PDU must follow.
150          */
151 
152         // *** send last Data-In PDU ***
153 
154         // get data and prepare data segment
155         final int bytesRemaining = totalTransferLength - bytesSent;
156         dataSegmentArray = connection.getDataInArray(bytesRemaining);
157         session.getStorageModule().read(dataSegmentArray, storageOffset + bytesSent);
158         dataSegment = ByteBuffer.wrap(dataSegmentArray);
159 
160         // create and send PDU (with or without status)
161         responsePdu = TargetPduFactory.createDataInPdu(true,// finalFlag, last
162                                                             // PDU in the
163                                                             // sequence with
164                                                             // data payload
165                 false,// acknowledgeFlag, ErrorRecoveryLevel == 0, so we never
166                       // do that
167                 false,// residualOverflowFlag
168                 false,// residualUnderflowFlag
169                 immediateData,// statusFlag
170                 SCSIStatus.GOOD,// status, or not (reserved if no status)
171                 0L,// logicalUnitNumber, reserved
172                 initiatorTaskTag, 0xffffffff,// targetTransferTag
173                 dataSequenceNumber,// dataSequenceNumber
174                 bytesSent,// bufferOffset
175                 0,// residualCount
176                 dataSegment);
177 
178         LOGGER.debug("sending last Data-In PDU");
179         connection.sendPdu(responsePdu);
180 
181         // send SCSI Response PDU?
182         if (!immediateData) {
183 
184             responsePdu = TargetPduFactory.createSCSIResponsePdu(false,// bidirectionalReadResidualOverflow
185                     false,// bidirectionalReadResidualUnderflow
186                     false,// residualOverflow
187                     false,// residualUnderflow
188                     ServiceResponse.COMMAND_COMPLETED_AT_TARGET,// response
189                     SCSIStatus.GOOD,// status
190                     initiatorTaskTag,// initiatorTaskTag
191                     0,// snackTag, reserved
192                     0,// expectedDataSequenceNumber, reserved
193                     0,// bidirectionalReadResidualCount
194                     0,// residualCount
195                     ScsiResponseDataSegment.EMPTY_DATA_SEGMENT);// empty
196                                                                 // ScsiResponseDataSegment
197 
198             LOGGER.debug("sending SCSI Response PDU");
199             connection.sendPdu(responsePdu);
200         }
201 
202     }
203 
204 }