diff mbox series

scsi: Fix scsi_get/set_resid() interface

Message ID 20191028105732.29913-1-damien.lemoal@wdc.com (mailing list archive)
State Superseded
Headers show
Series scsi: Fix scsi_get/set_resid() interface | expand

Commit Message

Damien Le Moal Oct. 28, 2019, 10:57 a.m. UTC
struct scsi_cmnd cmd->req.resid_len which is returned and set
respectively by the helper functions scsi_get_resid() and
scsi_set_resid() is an unsigned int. Reflect this fact in the interface
of these helper functions.

Also fix compilation errors due to min() and max() type mismatch
introduced by this change in scsi debug code and usb transport code.

Signed-off-by: Damien Le Moal <damien.lemoal@wdc.com>
---
 drivers/scsi/scsi_debug.c       | 4 ++--
 drivers/usb/storage/transport.c | 3 +--
 include/scsi/scsi_cmnd.h        | 4 ++--
 3 files changed, 5 insertions(+), 6 deletions(-)

Comments

Bart Van Assche Oct. 28, 2019, 8:38 p.m. UTC | #1
On 10/28/19 3:57 AM, Damien Le Moal wrote:
> struct scsi_cmnd cmd->req.resid_len which is returned and set
> respectively by the helper functions scsi_get_resid() and
> scsi_set_resid() is an unsigned int. Reflect this fact in the interface
> of these helper functions.
> [ ... ]
> -static inline void scsi_set_resid(struct scsi_cmnd *cmd, int resid)
> +static inline void scsi_set_resid(struct scsi_cmnd *cmd, unsigned int resid)
>   {
>   	cmd->req.resid_len = resid;
>   }
>   
> -static inline int scsi_get_resid(struct scsi_cmnd *cmd)
> +static inline unsigned int scsi_get_resid(struct scsi_cmnd *cmd)
>   {
>   	return cmd->req.resid_len;
>   }

 From the iSCSI RFC:

    SCSI-Presented Data Transfer Length (SPDTL) is the term this document
    uses (see Section 1.1 for definition) to represent the aggregate data
    length that the target SCSI layer attempts to transfer using the
    local iSCSI layer for a task.  Expected Data Transfer Length (EDTL)
    is the iSCSI term that represents the length of data that the iSCSI
    layer expects to transfer for a task.  EDTL is specified in the SCSI
    Command PDU.

    When SPDTL = EDTL for a task, the target iSCSI layer completes the
    task with no residuals.  Whenever SPDTL differs from EDTL for a task,
    that task is said to have a residual.

    If SPDTL > EDTL for a task, iSCSI Overflow MUST be signaled in the
    SCSI Response PDU as specified in [RFC3720].  The Residual Count MUST
    be set to the numerical value of (SPDTL - EDTL).

    If SPDTL < EDTL for a task, iSCSI Underflow MUST be signaled in the
    SCSI Response PDU as specified in [RFC3720].  The Residual Count MUST
    be set to the numerical value of (EDTL - SPDTL).

    Note that the Overflow and Underflow scenarios are independent of
    Data-In and Data-Out.  Either scenario is logically possible in
    either direction of data transfer.

If the residual is changed from signed into unsigned, how is a SCSI LLD 
expected to report the difference between residual overflow and residual 
underflow to the SCSI core?

Thanks,

Bart.
Damien Le Moal Oct. 29, 2019, 8:17 a.m. UTC | #2
Bart,

On 2019/10/28 21:38, Bart Van Assche wrote:
> On 10/28/19 3:57 AM, Damien Le Moal wrote:
>> struct scsi_cmnd cmd->req.resid_len which is returned and set
>> respectively by the helper functions scsi_get_resid() and
>> scsi_set_resid() is an unsigned int. Reflect this fact in the interface
>> of these helper functions.
>> [ ... ]
>> -static inline void scsi_set_resid(struct scsi_cmnd *cmd, int resid)
>> +static inline void scsi_set_resid(struct scsi_cmnd *cmd, unsigned int resid)
>>   {
>>   	cmd->req.resid_len = resid;
>>   }
>>   
>> -static inline int scsi_get_resid(struct scsi_cmnd *cmd)
>> +static inline unsigned int scsi_get_resid(struct scsi_cmnd *cmd)
>>   {
>>   	return cmd->req.resid_len;
>>   }
> 
>  From the iSCSI RFC:
> 
>     SCSI-Presented Data Transfer Length (SPDTL) is the term this document
>     uses (see Section 1.1 for definition) to represent the aggregate data
>     length that the target SCSI layer attempts to transfer using the
>     local iSCSI layer for a task.  Expected Data Transfer Length (EDTL)
>     is the iSCSI term that represents the length of data that the iSCSI
>     layer expects to transfer for a task.  EDTL is specified in the SCSI
>     Command PDU.
> 
>     When SPDTL = EDTL for a task, the target iSCSI layer completes the
>     task with no residuals.  Whenever SPDTL differs from EDTL for a task,
>     that task is said to have a residual.
> 
>     If SPDTL > EDTL for a task, iSCSI Overflow MUST be signaled in the
>     SCSI Response PDU as specified in [RFC3720].  The Residual Count MUST
>     be set to the numerical value of (SPDTL - EDTL).
> 
>     If SPDTL < EDTL for a task, iSCSI Underflow MUST be signaled in the
>     SCSI Response PDU as specified in [RFC3720].  The Residual Count MUST
>     be set to the numerical value of (EDTL - SPDTL).
> 
>     Note that the Overflow and Underflow scenarios are independent of
>     Data-In and Data-Out.  Either scenario is logically possible in
>     either direction of data transfer.
> 
> If the residual is changed from signed into unsigned, how is a SCSI LLD 
> expected to report the difference between residual overflow and residual 
> underflow to the SCSI core?

As mentioned in the commit message, cmd->req.resid_len where the resid
is stored is an unsigned int. And it has been an unsigned int forever as
far as I know.

So I think changing the interface to unsigned int makes sense in that
context. Also, unless I am reading this wrong, the above definition you
quote always lead to resid >= 0, so I do not see what problem you are
worried about. Could you elaborate your concerns please ?

> 
> Thanks,
> 
> Bart.
>
Bart Van Assche Oct. 29, 2019, 3:14 p.m. UTC | #3
On 10/29/19 1:17 AM, Damien Le Moal wrote:
> So I think changing the interface to unsigned int makes sense in that
> context. Also, unless I am reading this wrong, the above definition you
> quote always lead to resid >= 0, so I do not see what problem you are
> worried about. Could you elaborate your concerns please ?

Hi Damien,

SCSI LLDs handle residual overflow in at least two different ways. I 
think the iSCSI initiator handles this by reporting the DID_BAD_TARGET 
error code. In the tcm_loop driver I found the following, which I think 
is incorrect:

	set_host_byte(sc, DID_OK);
	if ((se_cmd->se_cmd_flags & SCF_OVERFLOW_BIT) ||
	    (se_cmd->se_cmd_flags & SCF_UNDERFLOW_BIT))
		scsi_set_resid(sc, se_cmd->residual_count);

Seeing this made me wonder what the best approach is for a SCSI LLD to 
report a residual overflow?

Thanks,

Bart.
Douglas Gilbert Oct. 29, 2019, 5:59 p.m. UTC | #4
On 2019-10-29 4:17 a.m., Damien Le Moal wrote:
> Bart,
> 
> On 2019/10/28 21:38, Bart Van Assche wrote:
>> On 10/28/19 3:57 AM, Damien Le Moal wrote:
>>> struct scsi_cmnd cmd->req.resid_len which is returned and set
>>> respectively by the helper functions scsi_get_resid() and
>>> scsi_set_resid() is an unsigned int. Reflect this fact in the interface
>>> of these helper functions.
>>> [ ... ]
>>> -static inline void scsi_set_resid(struct scsi_cmnd *cmd, int resid)
>>> +static inline void scsi_set_resid(struct scsi_cmnd *cmd, unsigned int resid)
>>>    {
>>>    	cmd->req.resid_len = resid;
>>>    }
>>>    
>>> -static inline int scsi_get_resid(struct scsi_cmnd *cmd)
>>> +static inline unsigned int scsi_get_resid(struct scsi_cmnd *cmd)
>>>    {
>>>    	return cmd->req.resid_len;
>>>    }
>>
>>   From the iSCSI RFC:
>>
>>      SCSI-Presented Data Transfer Length (SPDTL) is the term this document
>>      uses (see Section 1.1 for definition) to represent the aggregate data
>>      length that the target SCSI layer attempts to transfer using the
>>      local iSCSI layer for a task.  Expected Data Transfer Length (EDTL)
>>      is the iSCSI term that represents the length of data that the iSCSI
>>      layer expects to transfer for a task.  EDTL is specified in the SCSI
>>      Command PDU.
>>
>>      When SPDTL = EDTL for a task, the target iSCSI layer completes the
>>      task with no residuals.  Whenever SPDTL differs from EDTL for a task,
>>      that task is said to have a residual.
>>
>>      If SPDTL > EDTL for a task, iSCSI Overflow MUST be signaled in the
>>      SCSI Response PDU as specified in [RFC3720].  The Residual Count MUST
>>      be set to the numerical value of (SPDTL - EDTL).
>>
>>      If SPDTL < EDTL for a task, iSCSI Underflow MUST be signaled in the
>>      SCSI Response PDU as specified in [RFC3720].  The Residual Count MUST
>>      be set to the numerical value of (EDTL - SPDTL).
>>
>>      Note that the Overflow and Underflow scenarios are independent of
>>      Data-In and Data-Out.  Either scenario is logically possible in
>>      either direction of data transfer.
>>
>> If the residual is changed from signed into unsigned, how is a SCSI LLD
>> expected to report the difference between residual overflow and residual
>> underflow to the SCSI core?



> As mentioned in the commit message, cmd->req.resid_len where the resid
> is stored is an unsigned int. And it has been an unsigned int forever as
> far as I know.

If my memory serves, I put it in over 20 years ago, about the same time
as the sg v2 interface (struct sg_io_hdr) was implemented. And when I
put it in it was a (signed) int and this is why:

struct ccb_accept_targ3 {
     .....
     CAM_I32 cam_resid;    /* Transfer residual length: 2's comp */
     .....
};

[from cam3r03.pdf]

So it coped with both underflow and the less likely case of overflow.

Notice in:
    struct sg_io_hdr {
       .....
       int resid;  /* [o] dxfer_len - actual_transferred */
       ...
};

At the time the SCSI mid-level and the block layer didn't bother about
resid, only some LLDs which is where the sg driver picked it up from.

So some ignoramus changed it to unsigned since then, probably before
git strangled the kernel source tree.

> So I think changing the interface to unsigned int makes sense in that
> context. Also, unless I am reading this wrong, the above definition you
> quote always lead to resid >= 0, so I do not see what problem you are
> worried about. Could you elaborate your concerns please ?

Linux's internal representation is wrong and should be fixed to match
its exposed interface (struct sg_io_hdr) which predates the breakage.

Doug Gilbert
Bart Van Assche Oct. 30, 2019, 1 a.m. UTC | #5
On 2019-10-29 10:59, Douglas Gilbert wrote:
> Linux's internal representation is wrong and should be fixed to match
> its exposed interface (struct sg_io_hdr) which predates the breakage.

If resid_len is changed from unsigned int to int all SCSI core and SCSI
LLD code that uses resid_len will have to be reviewed to see whether it
handles residual overflow correctly.

Bart.
Hannes Reinecke Oct. 30, 2019, 8:30 a.m. UTC | #6
On 10/28/19 9:38 PM, Bart Van Assche wrote:
> On 10/28/19 3:57 AM, Damien Le Moal wrote:
>> struct scsi_cmnd cmd->req.resid_len which is returned and set
>> respectively by the helper functions scsi_get_resid() and
>> scsi_set_resid() is an unsigned int. Reflect this fact in the interface
>> of these helper functions.
>> [ ... ]
>> -static inline void scsi_set_resid(struct scsi_cmnd *cmd, int resid)
>> +static inline void scsi_set_resid(struct scsi_cmnd *cmd, unsigned int 
>> resid)
>>   {
>>       cmd->req.resid_len = resid;
>>   }
>> -static inline int scsi_get_resid(struct scsi_cmnd *cmd)
>> +static inline unsigned int scsi_get_resid(struct scsi_cmnd *cmd)
>>   {
>>       return cmd->req.resid_len;
>>   }
> 
>  From the iSCSI RFC:
> 
>     SCSI-Presented Data Transfer Length (SPDTL) is the term this document
>     uses (see Section 1.1 for definition) to represent the aggregate data
>     length that the target SCSI layer attempts to transfer using the
>     local iSCSI layer for a task.  Expected Data Transfer Length (EDTL)
>     is the iSCSI term that represents the length of data that the iSCSI
>     layer expects to transfer for a task.  EDTL is specified in the SCSI
>     Command PDU.
> 
>     When SPDTL = EDTL for a task, the target iSCSI layer completes the
>     task with no residuals.  Whenever SPDTL differs from EDTL for a task,
>     that task is said to have a residual.
> 
>     If SPDTL > EDTL for a task, iSCSI Overflow MUST be signaled in the
>     SCSI Response PDU as specified in [RFC3720].  The Residual Count MUST
>     be set to the numerical value of (SPDTL - EDTL).
> 
>     If SPDTL < EDTL for a task, iSCSI Underflow MUST be signaled in the
>     SCSI Response PDU as specified in [RFC3720].  The Residual Count MUST
>     be set to the numerical value of (EDTL - SPDTL).
> 
>     Note that the Overflow and Underflow scenarios are independent of
>     Data-In and Data-Out.  Either scenario is logically possible in
>     either direction of data transfer.
> 
> If the residual is changed from signed into unsigned, how is a SCSI LLD 
> expected to report the difference between residual overflow and residual 
> underflow to the SCSI core?
> 
You don't have to. To quote RFC 3720 page 122:

      bit 5 - (O) set for Residual Overflow.  In this case, the Residual
        Count indicates the number of bytes that were not transferred
        because the initiator's Expected Data Transfer Length was not
        sufficient.  For a bidirectional operation, the Residual Count
        contains the residual for the write operation.

IE the 'overflow' setting in the iSCSI command response is an indicator 
that there _would_ be more data if the command request _would_ have 
specified a larger buffer.
But as it didn't, the entire buffer was filled, and the overflow counter 
is set.
Which, of course, is then ignored by the linux SCSI stack as the request 
got all data, and the residual is set to zero.
Then it's left to the caller to re-send with a larger buffer if 
required. But it's nothing the SCSI stack can nor should be attempting 
on its own.

As such I think the patch is correct.

Reviewed-by: Hannes Reinecke <hare@suse.de>

Cheers,

Hannes
Bart Van Assche Oct. 30, 2019, 3:12 p.m. UTC | #7
On 10/30/19 1:30 AM, Hannes Reinecke wrote:
> On 10/28/19 9:38 PM, Bart Van Assche wrote:
>> If the residual is changed from signed into unsigned, how is a SCSI 
>> LLD expected to report the difference between residual overflow and 
>> residual underflow to the SCSI core?
>
> You don't have to. To quote RFC 3720 page 122:
> 
>       bit 5 - (O) set for Residual Overflow.  In this case, the Residual
>         Count indicates the number of bytes that were not transferred
>         because the initiator's Expected Data Transfer Length was not
>         sufficient.  For a bidirectional operation, the Residual Count
>         contains the residual for the write operation.
> 
> IE the 'overflow' setting in the iSCSI command response is an indicator 
> that there _would_ be more data if the command request _would_ have 
> specified a larger buffer.
> But as it didn't, the entire buffer was filled, and the overflow counter 
> is set.
> Which, of course, is then ignored by the linux SCSI stack as the request 
> got all data, and the residual is set to zero.
> Then it's left to the caller to re-send with a larger buffer if 
> required. But it's nothing the SCSI stack can nor should be attempting 
> on its own.

Hi Hannes,

I do not agree that reporting a residual overflow by calling 
scsi_set_resid(..., 0) is acceptable. For reads a residual overflow 
means that the length specified in the CDB (scsi_bufflen()) exceeds the 
data buffer size (length of scsi_sglist()). I think it's dangerous to 
report to the block layer that such requests completed successfully and 
with residual zero.

Bart.
Hannes Reinecke Oct. 30, 2019, 3:18 p.m. UTC | #8
On 10/30/19 4:12 PM, Bart Van Assche wrote:
> On 10/30/19 1:30 AM, Hannes Reinecke wrote:
>> On 10/28/19 9:38 PM, Bart Van Assche wrote:
>>> If the residual is changed from signed into unsigned, how is a SCSI 
>>> LLD expected to report the difference between residual overflow and 
>>> residual underflow to the SCSI core?
>>
>> You don't have to. To quote RFC 3720 page 122:
>>
>>       bit 5 - (O) set for Residual Overflow.  In this case, the Residual
>>         Count indicates the number of bytes that were not transferred
>>         because the initiator's Expected Data Transfer Length was not
>>         sufficient.  For a bidirectional operation, the Residual Count
>>         contains the residual for the write operation.
>>
>> IE the 'overflow' setting in the iSCSI command response is an 
>> indicator that there _would_ be more data if the command request 
>> _would_ have specified a larger buffer.
>> But as it didn't, the entire buffer was filled, and the overflow 
>> counter is set.
>> Which, of course, is then ignored by the linux SCSI stack as the 
>> request got all data, and the residual is set to zero.
>> Then it's left to the caller to re-send with a larger buffer if 
>> required. But it's nothing the SCSI stack can nor should be attempting 
>> on its own.
> 
> Hi Hannes,
> 
> I do not agree that reporting a residual overflow by calling 
> scsi_set_resid(..., 0) is acceptable. For reads a residual overflow 
> means that the length specified in the CDB (scsi_bufflen()) exceeds the 
> data buffer size (length of scsi_sglist()). I think it's dangerous to 
> report to the block layer that such requests completed successfully and 
> with residual zero.
> 
But that is an error on submission, and should be aborted before it even 
got send to the drive.

However, this does not relate to the residual, which is handled after 
the command completes (and which sparked this entire thread ...).

Cheers,

Hannes
Bart Van Assche Oct. 30, 2019, 3:24 p.m. UTC | #9
On 10/30/19 8:18 AM, Hannes Reinecke wrote:
> On 10/30/19 4:12 PM, Bart Van Assche wrote:
>> I do not agree that reporting a residual overflow by calling 
>> scsi_set_resid(..., 0) is acceptable. For reads a residual overflow 
>> means that the length specified in the CDB (scsi_bufflen()) exceeds 
>> the data buffer size (length of scsi_sglist()). I think it's dangerous 
>> to report to the block layer that such requests completed successfully 
>> and with residual zero.
>>
> But that is an error on submission, and should be aborted before it even 
> got send to the drive.

If such a bug ever gets introduced in the SCSI core, I think that SCSI 
target code should detect and report it. If the SCSI core receives a 
response with a residual overflow it can then take appropriate action, 
e.g. call WARN_ON_ONCE().

Users of sg_raw can trigger the residual overflow case easily.

> However, this does not relate to the residual, which is handled after 
> the command completes (and which sparked this entire thread ...).

I'm still waiting for an answer to my question of how SCSI LLDs are 
expected to report a residual overflow to the SCSI core.

Thanks,

Bart.
Douglas Gilbert Oct. 30, 2019, 4:18 p.m. UTC | #10
On 2019-10-30 11:18 a.m., Hannes Reinecke wrote:
> On 10/30/19 4:12 PM, Bart Van Assche wrote:
>> On 10/30/19 1:30 AM, Hannes Reinecke wrote:
>>> On 10/28/19 9:38 PM, Bart Van Assche wrote:
>>>> If the residual is changed from signed into unsigned, how is a SCSI LLD 
>>>> expected to report the difference between residual overflow and residual 
>>>> underflow to the SCSI core?
>>>
>>> You don't have to. To quote RFC 3720 page 122:
>>>
>>>       bit 5 - (O) set for Residual Overflow.  In this case, the Residual
>>>         Count indicates the number of bytes that were not transferred
>>>         because the initiator's Expected Data Transfer Length was not
>>>         sufficient.  For a bidirectional operation, the Residual Count
>>>         contains the residual for the write operation.
>>>
>>> IE the 'overflow' setting in the iSCSI command response is an indicator that 
>>> there _would_ be more data if the command request _would_ have specified a 
>>> larger buffer.
>>> But as it didn't, the entire buffer was filled, and the overflow counter is set.
>>> Which, of course, is then ignored by the linux SCSI stack as the request got 
>>> all data, and the residual is set to zero.
>>> Then it's left to the caller to re-send with a larger buffer if required. But 
>>> it's nothing the SCSI stack can nor should be attempting on its own.
>>
>> Hi Hannes,
>>
>> I do not agree that reporting a residual overflow by calling 
>> scsi_set_resid(..., 0) is acceptable. For reads a residual overflow means that 
>> the length specified in the CDB (scsi_bufflen()) exceeds the data buffer size 
>> (length of scsi_sglist()). I think it's dangerous to report to the block layer 
>> that such requests completed successfully and with residual zero.
>>
> But that is an error on submission, and should be aborted before it even got 
> send to the drive.
> 
> However, this does not relate to the residual, which is handled after the 
> command completes (and which sparked this entire thread ...).

Seen from a pass-through perspective, the resid is just about the near-end
data transfer between the HBA and pass-through, or as cam3r03 says:

− cam_resid;
     The data residual length member contains the difference in twos complement
     form of the number of data bytes transferred by the HA for the SCSI
     command compared with the number of bytes requested by the CCB
     cam_dxfer_len member. This is calculated by the total number of bytes
     requested to be transferred by the CCB minus the actual number of bytes
     transferred by the HA.

[where "HA" is HBA (or the initiator)]

That makes overflow (negative resid) a bit interesting as it is only
reasonable that the pass-though user allocated a buffer big enough to
receive dxfer_len bytes. One would hope that in the READ case of overflow,
the HBA would have sent the residual bytes to the bit bucket (i.e.
/dev/null) rather than overfill the data buffer provided by the pass-through.

Handling discrepancies between page length (e.g. of VPD, LOG and MODE pages)
and the ALLOCATION LENGTH field are defined in the SCSI standards.

That leaves the difficult cases: when there is a discrepancy between what the
SCSI command (and the storage device) implied as a data length and the
dxfer_len allocated at the near-end. In some, but not all, cases that is
detectable before the command is issued.

Overflow can happen, for example if the RDPROTECT field in a READ(10) is
accidentally set (e.g. because it uses bits previously used for a SPI
LUN) and the storage device has protection information. That will result
in an extra 8 bytes per logical block being returned.

Doug Gilbert
Hannes Reinecke Oct. 31, 2019, 7:51 a.m. UTC | #11
On 10/30/19 5:18 PM, Douglas Gilbert wrote:
> On 2019-10-30 11:18 a.m., Hannes Reinecke wrote:
>> On 10/30/19 4:12 PM, Bart Van Assche wrote:
>>> On 10/30/19 1:30 AM, Hannes Reinecke wrote:
>>>> On 10/28/19 9:38 PM, Bart Van Assche wrote:
>>>>> If the residual is changed from signed into unsigned, how is a SCSI 
>>>>> LLD expected to report the difference between residual overflow and 
>>>>> residual underflow to the SCSI core?
>>>>
>>>> You don't have to. To quote RFC 3720 page 122:
>>>>
>>>>       bit 5 - (O) set for Residual Overflow.  In this case, the 
>>>> Residual
>>>>         Count indicates the number of bytes that were not transferred
>>>>         because the initiator's Expected Data Transfer Length was not
>>>>         sufficient.  For a bidirectional operation, the Residual Count
>>>>         contains the residual for the write operation.
>>>>
>>>> IE the 'overflow' setting in the iSCSI command response is an 
>>>> indicator that there _would_ be more data if the command request 
>>>> _would_ have specified a larger buffer.
>>>> But as it didn't, the entire buffer was filled, and the overflow 
>>>> counter is set.
>>>> Which, of course, is then ignored by the linux SCSI stack as the 
>>>> request got all data, and the residual is set to zero.
>>>> Then it's left to the caller to re-send with a larger buffer if 
>>>> required. But it's nothing the SCSI stack can nor should be 
>>>> attempting on its own.
>>>
>>> Hi Hannes,
>>>
>>> I do not agree that reporting a residual overflow by calling 
>>> scsi_set_resid(..., 0) is acceptable. For reads a residual overflow 
>>> means that the length specified in the CDB (scsi_bufflen()) exceeds 
>>> the data buffer size (length of scsi_sglist()). I think it's 
>>> dangerous to report to the block layer that such requests completed 
>>> successfully and with residual zero.
>>>
>> But that is an error on submission, and should be aborted before it 
>> even got send to the drive.
>>
>> However, this does not relate to the residual, which is handled after 
>> the command completes (and which sparked this entire thread ...).
> 
> Seen from a pass-through perspective, the resid is just about the near-end
> data transfer between the HBA and pass-through, or as cam3r03 says:
> 
> − cam_resid;
>      The data residual length member contains the difference in twos 
> complement
>      form of the number of data bytes transferred by the HA for the SCSI
>      command compared with the number of bytes requested by the CCB
>      cam_dxfer_len member. This is calculated by the total number of bytes
>      requested to be transferred by the CCB minus the actual number of 
> bytes
>      transferred by the HA.
> 
> [where "HA" is HBA (or the initiator)]
> 
> That makes overflow (negative resid) a bit interesting as it is only
> reasonable that the pass-though user allocated a buffer big enough to
> receive dxfer_len bytes. One would hope that in the READ case of overflow,
> the HBA would have sent the residual bytes to the bit bucket (i.e.
> /dev/null) rather than overfill the data buffer provided by the 
> pass-through.
> 
Yes. But my point here is that any residual values are handled (and 
defined) at the protocol level. Any SCSI midlayer protocol like SPC or, 
indeed, SAM have no concept of residuals.

THe SCSI midlayer chose to add a residual value to document the number 
of bytes _not_ being handled from hardware.

> Handling discrepancies between page length (e.g. of VPD, LOG and MODE 
> pages) and the ALLOCATION LENGTH field are defined in the SCSI standards.
> 
> That leaves the difficult cases: when there is a discrepancy between 
> what the
> SCSI command (and the storage device) implied as a data length and the
> dxfer_len allocated at the near-end. In some, but not all, cases that is
> detectable before the command is issued.
> 
As mentioned, this needs to be handled in the submission path, and the 
command should be aborted before being sent to the drive.

> Overflow can happen, for example if the RDPROTECT field in a READ(10) is
> accidentally set (e.g. because it uses bits previously used for a SPI
> LUN) and the storage device has protection information. That will result
> in an extra 8 bytes per logical block being returned.
> 

See above. We should intercept such a command, and simply abort it with 
an appropriate error.
That goes for internal commands, of course; I know it doesn't work for 
things like sg_raw, but one could argue that this was precisely the 
point of that command ...

Cheers,

Hannes
Hannes Reinecke Oct. 31, 2019, 8:28 a.m. UTC | #12
On 10/30/19 4:12 PM, Bart Van Assche wrote:
> On 10/30/19 1:30 AM, Hannes Reinecke wrote:
>> On 10/28/19 9:38 PM, Bart Van Assche wrote:
>>> If the residual is changed from signed into unsigned, how is a SCSI 
>>> LLD expected to report the difference between residual overflow and 
>>> residual underflow to the SCSI core?
>>
>> You don't have to. To quote RFC 3720 page 122:
>>
>>       bit 5 - (O) set for Residual Overflow.  In this case, the Residual
>>         Count indicates the number of bytes that were not transferred
>>         because the initiator's Expected Data Transfer Length was not
>>         sufficient.  For a bidirectional operation, the Residual Count
>>         contains the residual for the write operation.
>>
>> IE the 'overflow' setting in the iSCSI command response is an 
>> indicator that there _would_ be more data if the command request 
>> _would_ have specified a larger buffer.
>> But as it didn't, the entire buffer was filled, and the overflow 
>> counter is set.
>> Which, of course, is then ignored by the linux SCSI stack as the 
>> request got all data, and the residual is set to zero.
>> Then it's left to the caller to re-send with a larger buffer if 
>> required. But it's nothing the SCSI stack can nor should be attempting 
>> on its own.
> 
> Hi Hannes,
> 
> I do not agree that reporting a residual overflow by calling 
> scsi_set_resid(..., 0) is acceptable. For reads a residual overflow 
> means that the length specified in the CDB (scsi_bufflen()) exceeds the 
> data buffer size (length of scsi_sglist()). I think it's dangerous to 
> report to the block layer that such requests completed successfully and 
> with residual zero.
> 
Dangerous how?

fcp3:
For read operations and write operations, if the FCP_RESID_OVER bit is 
set to one, the FCP_RESID field contains the excess of the number of 
bytes required by the SCSI command to be transferred over the number of 
bytes specified by the FCP_DL field.

SAS doesn't even have the notion of residuals

srp04:
DOOVER , when set to one, indicates that the DATA-OUT RESIDUAL COUNT 
field is valid and contains the count of data bytes that could not be 
transferred from the data-out buffer because the length of the data-out 
buffer was not sufficient. The application client should examine the 
DATA-OUT RESIDUAL COUNT field in the context of the command to determine 
whether or not an error condition occurred.

iSCSI we've already covered.

In all cases, the overflow value is an _indicator_ that additional data 
is available, but was not transferred due to the lack of space.
So we will not have any buffer overflow as data is never transferred.

And in most cases an overflow is actually desired; it is a pretty common 
use pattern to send a SCSI command with a small enough buffer to return 
the length of available data, and then send the actual command knowing 
how large the buffer needs to be.
See for example scsi_report_lun_scan() or the VPD handling code.

So again, I don't think it's something we need to worry about.

Cheers,

Hannes
diff mbox series

Patch

diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
index d323523f5f9d..4daf2637c011 100644
--- a/drivers/scsi/scsi_debug.c
+++ b/drivers/scsi/scsi_debug.c
@@ -1025,7 +1025,7 @@  static int fill_from_dev_buffer(struct scsi_cmnd *scp, unsigned char *arr,
 static int p_fill_from_dev_buffer(struct scsi_cmnd *scp, const void *arr,
 				  int arr_len, unsigned int off_dst)
 {
-	int act_len, n;
+	unsigned int act_len, n;
 	struct scsi_data_buffer *sdb = &scp->sdb;
 	off_t skip = off_dst;
 
@@ -1039,7 +1039,7 @@  static int p_fill_from_dev_buffer(struct scsi_cmnd *scp, const void *arr,
 	pr_debug("%s: off_dst=%u, scsi_bufflen=%u, act_len=%u, resid=%d\n",
 		 __func__, off_dst, scsi_bufflen(scp), act_len,
 		 scsi_get_resid(scp));
-	n = (int)scsi_bufflen(scp) - ((int)off_dst + act_len);
+	n = scsi_bufflen(scp) - (off_dst + act_len);
 	scsi_set_resid(scp, min(scsi_get_resid(scp), n));
 	return 0;
 }
diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c
index 96cb0409dd89..238a8088e17f 100644
--- a/drivers/usb/storage/transport.c
+++ b/drivers/usb/storage/transport.c
@@ -1284,8 +1284,7 @@  int usb_stor_Bulk_transport(struct scsi_cmnd *srb, struct us_data *us)
 
 		} else {
 			residue = min(residue, transfer_length);
-			scsi_set_resid(srb, max(scsi_get_resid(srb),
-			                                       (int) residue));
+			scsi_set_resid(srb, max(scsi_get_resid(srb), residue));
 		}
 	}
 
diff --git a/include/scsi/scsi_cmnd.h b/include/scsi/scsi_cmnd.h
index 91bd749a02f7..3772ae5d40cd 100644
--- a/include/scsi/scsi_cmnd.h
+++ b/include/scsi/scsi_cmnd.h
@@ -190,12 +190,12 @@  static inline unsigned scsi_bufflen(struct scsi_cmnd *cmd)
 	return cmd->sdb.length;
 }
 
-static inline void scsi_set_resid(struct scsi_cmnd *cmd, int resid)
+static inline void scsi_set_resid(struct scsi_cmnd *cmd, unsigned int resid)
 {
 	cmd->req.resid_len = resid;
 }
 
-static inline int scsi_get_resid(struct scsi_cmnd *cmd)
+static inline unsigned int scsi_get_resid(struct scsi_cmnd *cmd)
 {
 	return cmd->req.resid_len;
 }