Re: [PATCH v2 12/18] sg: sense buffer rework

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 7/27/19 5:37 AM, Douglas Gilbert wrote:
> The biggest single item in the sg_request object is the sense
> buffer array which is SCSI_SENSE_BUFFERSIZE bytes long. That
> constant started out at 18 bytes 20 years ago and is 96 bytes
> now and might grow in the future. On the other hand the sense
> buffer is only used by a small number of SCSI commands: those
> that fail and those that want to return more information
> other than a SCSI status of GOOD.
> 
> Allocate sense buffer as needed on the heap and delete as soon as
> possible.
> 
> Signed-off-by: Douglas Gilbert <dgilbert@xxxxxxxxxxxx>
> ---
>  drivers/scsi/sg.c | 47 +++++++++++++++++++++++++++++++++++------------
>  1 file changed, 35 insertions(+), 12 deletions(-)
> 
> diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c
> index 00b8553e0038..4d6635af7da7 100644
> --- a/drivers/scsi/sg.c
> +++ b/drivers/scsi/sg.c
> @@ -176,7 +176,6 @@ struct sg_request {	/* active SCSI command or inactive on free list (fl) */
>  	spinlock_t req_lck;
>  	struct sg_scatter_hold sgat_h;	/* hold buffer, perhaps scatter list */
>  	struct sg_slice_hdr3 s_hdr3;  /* subset of sg_io_hdr */
> -	u8 sense_b[SCSI_SENSE_BUFFERSIZE];
>  	u32 duration;		/* cmd duration in milliseconds */
>  	u32 rq_flags;		/* hold user supplied flags */
>  	u32 rq_info;		/* info supplied by v3 and v4 interfaces */
> @@ -188,6 +187,7 @@ struct sg_request {	/* active SCSI command or inactive on free list (fl) */
>  	u8 cmd_opcode;		/* first byte of SCSI cdb */
>  	u64 start_ns;		/* starting point of command duration calc */
>  	unsigned long frq_bm[1];	/* see SG_FRQ_* defines above */
> +	u8 *sense_bp;		/* alloc-ed sense buffer, as needed */
>  	struct sg_fd *parentfp;	/* pointer to owning fd, even when on fl */
>  	struct request *rq;	/* released in sg_rq_end_io(), bio kept */
>  	struct bio *bio;	/* kept until this req -->SG_RS_INACTIVE */
> @@ -845,18 +845,21 @@ sg_copy_sense(struct sg_request *srp)
>  	    (driver_byte(srp->rq_result) & DRIVER_SENSE)) {
>  		int sb_len = min_t(int, SCSI_SENSE_BUFFERSIZE, srp->sense_len);
>  		int mx_sb_len;
> +		u8 *sbp = srp->sense_bp;
>  		void __user *up;
>  
> +		srp->sense_bp = NULL;
>  		up = (void __user *)srp->s_hdr3.sbp;
>  		mx_sb_len = srp->s_hdr3.mx_sb_len;
> -		if (up && mx_sb_len > 0) {
> +		if (up && mx_sb_len > 0 && sbp) {
>  			sb_len = min_t(int, sb_len, mx_sb_len);
>  			/* Additional sense length field */
> -			sb_len_wr = 8 + (int)srp->sense_b[7];
> +			sb_len_wr = 8 + (int)sbp[7];
>  			sb_len_wr = min_t(int, sb_len, sb_len_wr);
> -			if (copy_to_user(up, srp->sense_b, sb_len_wr))
> +			if (copy_to_user(up, sbp, sb_len_wr))
>  				sb_len_wr = -EFAULT;
>  		}
> +		kfree(sbp);
>  	}
>  	return sb_len_wr;
>  }
> @@ -963,8 +966,9 @@ sg_rd_v1v2(void __user *buf, int count, struct sg_fd *sfp,
>  	h2p->driver_status = driver_byte(rq_result);
>  	if ((CHECK_CONDITION & status_byte(rq_result)) ||
>  	    (DRIVER_SENSE & driver_byte(rq_result))) {
> -		memcpy(h2p->sense_buffer, srp->sense_b,
> -		       sizeof(h2p->sense_buffer));
> +		if (srp->sense_bp)
> +			memcpy(h2p->sense_buffer, srp->sense_bp,
> +			       sizeof(h2p->sense_buffer));
>  	}
>  	switch (host_byte(rq_result)) {
>  	/*
> @@ -999,17 +1003,21 @@ sg_rd_v1v2(void __user *buf, int count, struct sg_fd *sfp,
>  
>  	/* Now copy the result back to the user buffer.  */
>  	if (count >= SZ_SG_HEADER) {
> -		if (copy_to_user(buf, h2p, SZ_SG_HEADER))
> -			return -EFAULT;
> +		if (copy_to_user(buf, h2p, SZ_SG_HEADER)) {
> +			res = -EFAULT;
> +			goto fini;
> +		}
>  		buf += SZ_SG_HEADER;
>  		if (count > h2p->reply_len)
>  			count = h2p->reply_len;
>  		if (count > SZ_SG_HEADER) {
> -			if (sg_rd_append(srp, buf, count - SZ_SG_HEADER))
> -				return -EFAULT;
> +			res = sg_rd_append(srp, buf, count - SZ_SG_HEADER);
> +			if (res)
> +				goto fini;
>  		}
>  	} else
>  		res = (h2p->result == 0) ? 0 : -EIO;
> +fini:
>  	atomic_set(&srp->rq_st, SG_RS_DONE_RD);
>  	sg_finish_scsi_blk_rq(srp);
>  	sg_deact_request(sfp, srp);
> @@ -1977,8 +1985,17 @@ sg_rq_end_io(struct request *rq, blk_status_t status)
>  	srp->duration = sg_calc_rq_dur(srp);
>  	if (unlikely((srp->rq_result & SG_ML_RESULT_MSK) && slen > 0))
>  		sg_check_sense(sdp, srp, slen);
> -	if (slen > 0)
> -		memcpy(srp->sense_b, scsi_rp->sense, slen);
> +	if (slen > 0) {
> +		if (scsi_rp->sense) {
> +			srp->sense_bp = kzalloc(SCSI_SENSE_BUFFERSIZE,
> +						GFP_ATOMIC);
> +			if (srp->sense_bp)
> +				memcpy(srp->sense_bp, scsi_rp->sense, slen);
> +		} else {
> +			pr_warn("%s: scsi_request::sense==NULL\n", __func__);
> +			slen = 0;
> +		}
> +	}
>  	srp->sense_len = slen;
>  	if (unlikely(test_bit(SG_FRQ_IS_ORPHAN, srp->frq_bm))) {
>  		spin_lock(&srp->req_lck);
> @@ -2940,6 +2957,7 @@ sg_deact_request(struct sg_fd *sfp, struct sg_request *srp)
>  	bool on_fl = false;
>  	int dlen, buflen;
>  	unsigned long iflags;
> +	u8 *sbp;
>  	struct sg_request *t_srp;
>  	struct sg_scatter_hold *schp;
>  	const char *cp = "head";
> @@ -2948,8 +2966,11 @@ sg_deact_request(struct sg_fd *sfp, struct sg_request *srp)
>  		return;
>  	schp = &srp->sgat_h;	/* make sure it is own data buffer */
>  	spin_lock_irqsave(&sfp->rq_list_lock, iflags);
> +	sbp = srp->sense_bp;
> +	srp->sense_bp = NULL;
>  	atomic_set(&srp->rq_st, SG_RS_BUSY);
>  	list_del_rcu(&srp->rq_entry);
> +	kfree(sbp);     /* maybe orphaned req, thus never read */
>  	/*
>  	 * N.B. sg_request object is not de-allocated (freed). The contents
>  	 * of the rq_list and rq_fl lists are de-allocated (freed) when
> @@ -3092,6 +3113,7 @@ sg_remove_sfp_usercontext(struct work_struct *work)
>  		list_del(&srp->rq_entry);
>  		if (srp->sgat_h.buflen > 0)
>  			sg_remove_sgat(srp);
> +		kfree(srp->sense_bp);	/* abnormal close: device detached */
>  		SG_LOG(6, sfp, "%s:%s%p --\n", __func__, cp, srp);
>  		kfree(srp);
>  	}
> @@ -3103,6 +3125,7 @@ sg_remove_sfp_usercontext(struct work_struct *work)
>  		list_del(&srp->fl_entry);
>  		if (srp->sgat_h.buflen > 0)
>  			sg_remove_sgat(srp);
> +		kfree(srp->sense_bp);
>  		SG_LOG(6, sfp, "%s: fl%s%p --\n", __func__, cp, srp);
>  		kfree(srp);
>  	}
> 
Maybe it's worthwhile using a mempool here for sense buffers; frequest
kmalloc()/kfree() really should be avoided.

Also the allocation at end_io time is slightly dodgy; I'd rather have
the sense allocated before setting up the command. Thing is, the end_io
callback might be called at any time and in about every context
(including from an interrupt handler), so I really would avoid having to
do kmalloc() there.

Cheers,

Hannes
-- 
Dr. Hannes Reinecke		   Teamlead Storage & Networking
hare@xxxxxxx			               +49 911 74053 688
SUSE LINUX GmbH, Maxfeldstr. 5, 90409 Nürnberg
GF: Felix Imendörffer, Mary Higgins, Sri Rasiah
HRB 21284 (AG Nürnberg)



[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Index of Archives]     [SCSI Target Devel]     [Linux SCSI Target Infrastructure]     [Kernel Newbies]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Linux IIO]     [Samba]     [Device Mapper]

  Powered by Linux