2021-07-24 10:20:23 +03:00
// SPDX-License-Identifier: GPL-2.0
# include <linux/bsg.h>
# include <scsi/scsi.h>
# include <scsi/scsi_ioctl.h>
# include <scsi/scsi_cmnd.h>
# include <scsi/scsi_device.h>
# include <scsi/sg.h>
# include "scsi_priv.h"
# define uptr64(val) ((void __user *)(uintptr_t)(val))
2021-07-29 09:48:45 +03:00
static int scsi_bsg_sg_io_fn ( struct request_queue * q , struct sg_io_v4 * hdr ,
fmode_t mode , unsigned int timeout )
2021-07-24 10:20:23 +03:00
{
2022-02-24 20:55:47 +03:00
struct scsi_cmnd * scmd ;
2021-07-29 09:48:45 +03:00
struct request * rq ;
struct bio * bio ;
int ret ;
2021-07-24 10:20:23 +03:00
if ( hdr - > protocol ! = BSG_PROTOCOL_SCSI | |
hdr - > subprotocol ! = BSG_SUB_PROTOCOL_SCSI_CMD )
return - EINVAL ;
if ( hdr - > dout_xfer_len & & hdr - > din_xfer_len ) {
pr_warn_once ( " BIDI support in bsg has been removed. \n " ) ;
return - EOPNOTSUPP ;
}
2021-10-21 09:06:05 +03:00
rq = scsi_alloc_request ( q , hdr - > dout_xfer_len ?
REQ_OP_DRV_OUT : REQ_OP_DRV_IN , 0 ) ;
2021-07-29 09:48:45 +03:00
if ( IS_ERR ( rq ) )
return PTR_ERR ( rq ) ;
rq - > timeout = timeout ;
2022-02-24 20:55:47 +03:00
scmd = blk_mq_rq_to_pdu ( rq ) ;
scmd - > cmd_len = hdr - > request_len ;
if ( scmd - > cmd_len > sizeof ( scmd - > cmnd ) ) {
ret = - EINVAL ;
goto out_put_request ;
2021-07-24 10:20:23 +03:00
}
2021-07-29 09:48:45 +03:00
ret = - EFAULT ;
2022-02-24 20:55:47 +03:00
if ( copy_from_user ( scmd - > cmnd , uptr64 ( hdr - > request ) , scmd - > cmd_len ) )
goto out_put_request ;
2021-07-29 09:48:45 +03:00
ret = - EPERM ;
2022-02-24 20:55:47 +03:00
if ( ! scsi_cmd_allowed ( scmd - > cmnd , mode ) )
goto out_put_request ;
2021-07-24 10:20:23 +03:00
2021-07-31 10:40:26 +03:00
ret = 0 ;
2021-07-29 09:48:45 +03:00
if ( hdr - > dout_xfer_len ) {
ret = blk_rq_map_user ( rq - > q , rq , NULL , uptr64 ( hdr - > dout_xferp ) ,
hdr - > dout_xfer_len , GFP_KERNEL ) ;
} else if ( hdr - > din_xfer_len ) {
ret = blk_rq_map_user ( rq - > q , rq , NULL , uptr64 ( hdr - > din_xferp ) ,
hdr - > din_xfer_len , GFP_KERNEL ) ;
}
if ( ret )
2022-02-24 20:55:47 +03:00
goto out_put_request ;
2021-07-29 09:48:45 +03:00
bio = rq - > bio ;
2021-11-26 15:18:01 +03:00
blk_execute_rq ( rq , ! ( hdr - > flags & BSG_FLAG_Q_AT_TAIL ) ) ;
2021-07-24 10:20:23 +03:00
/*
* fill in all the output members
*/
2022-02-24 20:55:50 +03:00
hdr - > device_status = scmd - > result & 0xff ;
hdr - > transport_status = host_byte ( scmd - > result ) ;
2021-07-24 10:20:23 +03:00
hdr - > driver_status = 0 ;
2022-02-24 20:55:50 +03:00
if ( scsi_status_is_check_condition ( scmd - > result ) )
2021-07-24 10:20:23 +03:00
hdr - > driver_status = DRIVER_SENSE ;
hdr - > info = 0 ;
if ( hdr - > device_status | | hdr - > transport_status | | hdr - > driver_status )
hdr - > info | = SG_INFO_CHECK ;
hdr - > response_len = 0 ;
2022-02-24 20:55:48 +03:00
if ( scmd - > sense_len & & hdr - > response ) {
2021-07-24 10:20:23 +03:00
int len = min_t ( unsigned int , hdr - > max_response_len ,
2022-02-24 20:55:48 +03:00
scmd - > sense_len ) ;
2021-07-24 10:20:23 +03:00
2022-02-24 20:55:48 +03:00
if ( copy_to_user ( uptr64 ( hdr - > response ) , scmd - > sense_buffer ,
len ) )
2021-07-24 10:20:23 +03:00
ret = - EFAULT ;
else
hdr - > response_len = len ;
}
if ( rq_data_dir ( rq ) = = READ )
2022-02-24 20:55:49 +03:00
hdr - > din_resid = scmd - > resid_len ;
2021-07-24 10:20:23 +03:00
else
2022-02-24 20:55:49 +03:00
hdr - > dout_resid = scmd - > resid_len ;
2021-07-24 10:20:23 +03:00
2021-07-29 09:48:45 +03:00
blk_rq_unmap_user ( bio ) ;
2021-07-24 10:20:23 +03:00
2021-07-29 09:48:45 +03:00
out_put_request :
2021-10-25 10:05:07 +03:00
blk_mq_free_request ( rq ) ;
2021-07-29 09:48:45 +03:00
return ret ;
2021-07-24 10:20:23 +03:00
}
2021-07-29 09:48:42 +03:00
struct bsg_device * scsi_bsg_register_queue ( struct scsi_device * sdev )
2021-07-24 10:20:23 +03:00
{
2021-07-29 09:48:42 +03:00
return bsg_register_queue ( sdev - > request_queue , & sdev - > sdev_gendev ,
2021-07-29 09:48:45 +03:00
dev_name ( & sdev - > sdev_gendev ) , scsi_bsg_sg_io_fn ) ;
2021-07-24 10:20:23 +03:00
}