From patchwork Mon Oct 24 03:20:41 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Douglas Gilbert X-Patchwork-Id: 13016566 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 0DAF3C3A59D for ; Mon, 24 Oct 2022 03:26:03 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230465AbiJXDZ7 (ORCPT ); Sun, 23 Oct 2022 23:25:59 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:55560 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230093AbiJXDXB (ORCPT ); Sun, 23 Oct 2022 23:23:01 -0400 Received: from smtp.infotech.no (smtp.infotech.no [82.134.31.41]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 0AB1D10FCD for ; Sun, 23 Oct 2022 20:21:43 -0700 (PDT) Received: from localhost (localhost [127.0.0.1]) by smtp.infotech.no (Postfix) with ESMTP id 8102B20423B; Mon, 24 Oct 2022 05:21:41 +0200 (CEST) X-Virus-Scanned: by amavisd-new-2.6.6 (20110518) (Debian) at infotech.no Received: from smtp.infotech.no ([127.0.0.1]) by localhost (smtp.infotech.no [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 0m9D-ppuT5hR; Mon, 24 Oct 2022 05:21:41 +0200 (CEST) Received: from treten.bingwo.ca (unknown [10.16.20.11]) by smtp.infotech.no (Postfix) with ESMTPA id 5AD952041AF; Mon, 24 Oct 2022 05:21:40 +0200 (CEST) From: Douglas Gilbert To: linux-scsi@vger.kernel.org Cc: martin.petersen@oracle.com, jejb@linux.vnet.ibm.com, hare@suse.de, bvanassche@acm.org Subject: [PATCH v25 27/44] sg: rework debug info Date: Sun, 23 Oct 2022 23:20:41 -0400 Message-Id: <20221024032058.14077-28-dgilbert@interlog.com> X-Mailer: git-send-email 2.37.3 In-Reply-To: <20221024032058.14077-1-dgilbert@interlog.com> References: <20221024032058.14077-1-dgilbert@interlog.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org Since the version 2 driver, the state of the driver can be found with 'cat /proc/scsi/sg/debug'. As the driver becomes more threaded and IO faster (e.g. scsi_debug with a command timer of 5 microseconds), the existing state dump can become misleading as the state can change during the "snapshot". The new approach in this patch is to allocate a buffer of SG_PROC_DEBUG_SZ bytes and use scnprintf() to populate it. Only when the whole state is captured (or the buffer fills) is the output to the caller's terminal performed. The previous approach was line based: assemble a line of information and then output it. Locks are taken as required for short periods and should not interfere with a disk IO intensive program. Operations such as closing a sg file descriptor or removing a sg device may be held up for a short while (microseconds). Reviewed-by: Hannes Reinecke Signed-off-by: Douglas Gilbert --- drivers/scsi/sg.c | 255 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 177 insertions(+), 78 deletions(-) diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 6cd1a8b77d8f..264955e660f3 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -2287,7 +2287,7 @@ sg_rq_end_io(struct request *rq, blk_status_t status) if (unlikely((srp->rq_result & SG_ML_RESULT_MSK) && slen > 0)) sg_check_sense(sdp, srp, slen); if (slen > 0) { - if (scmd->sense_buffer) { + if (scmd->sense_buffer && !srp->sense_bp) { srp->sense_bp = mempool_alloc(sg_sense_pool, GFP_ATOMIC); if (srp->sense_bp) { memcpy(srp->sense_bp, scmd->sense_buffer, slen); @@ -2299,6 +2299,9 @@ sg_rq_end_io(struct request *rq, blk_status_t status) pr_warn("%s: sense but can't alloc buffer\n", __func__); } + } else if (srp->sense_bp) { + slen = 0; + pr_warn("%s: non-NULL srp->sense_bp ? ?\n", __func__); } else { slen = 0; pr_warn("%s: sense_len>0 but sense==NULL\n", __func__); @@ -3785,115 +3788,211 @@ sg_proc_seq_show_devstrs(struct seq_file *s, void *v) return 0; } -/* must be called while holding sg_index_lock */ -static void -sg_proc_debug_helper(struct seq_file *s, struct sg_device *sdp) +/* Writes debug info for one sg_request in obp buffer */ +static int +sg_proc_debug_sreq(struct sg_request *srp, int to, char *obp, int len) { - int k; - unsigned long idx, idx2; + bool is_v3v4, v4, is_dur; + int n = 0; + u32 dur; + enum sg_rq_state rq_st; + const char *cp; + + if (len < 1) + return 0; + v4 = test_bit(SG_FRQ_IS_V4I, srp->frq_bm); + is_v3v4 = v4 ? true : (srp->s_hdr3.interface_id != '\0'); + if (srp->parentfp->rsv_srp == srp) + cp = (is_v3v4 && (srp->rq_flags & SG_FLAG_MMAP_IO)) ? + " mmap>> " : " rsv>> "; + else + cp = (srp->rq_info & SG_INFO_DIRECT_IO_MASK) ? + " dio>> " : " "; + rq_st = atomic_read(&srp->rq_st); + dur = sg_get_dur(srp, &rq_st, &is_dur); + n += scnprintf(obp + n, len - n, "%s%s: dlen=%d/%d id=%d", cp, + sg_rq_st_str(rq_st, false), srp->sgat_h.dlen, + srp->sgat_h.buflen, (int)srp->pack_id); + if (is_dur) /* cmd/req has completed, waiting for ... */ + n += scnprintf(obp + n, len - n, " dur=%ums", dur); + else if (dur < U32_MAX) /* in-flight or busy (so ongoing) */ + n += scnprintf(obp + n, len - n, " t_o/elap=%us/%ums", + to / 1000, dur); + n += scnprintf(obp + n, len - n, " sgat=%d op=0x%02x\n", + srp->sgat_h.num_sgat, srp->cmd_opcode); + return n; +} + +/* Writes debug info for one sg fd (including its sg requests) in obp buffer */ +static int +sg_proc_debug_fd(struct sg_fd *fp, char *obp, int len, unsigned long idx) +{ + int n = 0; + int to, k; + unsigned long iflags; struct sg_request *srp; - struct sg_fd *fp; - const char * cp; - unsigned int ms; + /* sgat=-1 means unavailable */ + to = (fp->timeout >= 0) ? jiffies_to_msecs(fp->timeout) : -999; + if (to < 0) + n += scnprintf(obp + n, len - n, "BAD timeout=%d", + fp->timeout); + else if (to % 1000) + n += scnprintf(obp + n, len - n, "timeout=%dms rs", to); + else + n += scnprintf(obp + n, len - n, "timeout=%ds rs", to / 1000); + n += scnprintf(obp + n, len - n, "v_buflen=%d idx=%lu\n cmd_q=%d ", + fp->rsv_srp->sgat_h.buflen, idx, + (int)test_bit(SG_FFD_CMD_Q, fp->ffd_bm)); + n += scnprintf(obp + n, len - n, + "f_packid=%d k_orphan=%d ffd_bm=0x%lx\n", + (int)test_bit(SG_FFD_FORCE_PACKID, fp->ffd_bm), + (int)test_bit(SG_FFD_KEEP_ORPHAN, fp->ffd_bm), + fp->ffd_bm[0]); + n += scnprintf(obp + n, len - n, " mmap_called=%d\n", + test_bit(SG_FFD_MMAP_CALLED, fp->ffd_bm)); + n += scnprintf(obp + n, len - n, + " submitted=%d waiting=%d open thr_id=%d\n", + atomic_read(&fp->submitted), + atomic_read(&fp->waiting), fp->tid); k = 0; + xa_lock_irqsave(&fp->srp_arr, iflags); + xa_for_each(&fp->srp_arr, idx, srp) { + if (!srp) + continue; + if (xa_get_mark(&fp->srp_arr, idx, SG_XA_RQ_INACTIVE)) + continue; + n += sg_proc_debug_sreq(srp, fp->timeout, obp + n, len - n); + ++k; + if ((k % 8) == 0) { /* don't hold up isr_s too long */ + xa_unlock_irqrestore(&fp->srp_arr, iflags); + cpu_relax(); + xa_lock_irqsave(&fp->srp_arr, iflags); + } + } + if (k == 0) + n += scnprintf(obp + n, len - n, " No requests active\n"); + k = 0; + xa_for_each_marked(&fp->srp_arr, idx, srp, SG_XA_RQ_INACTIVE) { + if (!srp) + continue; + if (k == 0) + n += scnprintf(obp + n, len - n, " Inactives:\n"); + n += sg_proc_debug_sreq(srp, fp->timeout, obp + n, len - n); + ++k; + if ((k % 8) == 0) { /* don't hold up isr_s too long */ + xa_unlock_irqrestore(&fp->srp_arr, iflags); + cpu_relax(); + xa_lock_irqsave(&fp->srp_arr, iflags); + } + } + xa_unlock_irqrestore(&fp->srp_arr, iflags); + return n; +} + +/* Writes debug info for one sg device (including its sg fds) in obp buffer */ +static int +sg_proc_debug_sdev(struct sg_device *sdp, char *obp, int len, int *fd_counterp) +{ + int n = 0; + int my_count = 0; + unsigned long idx; + struct scsi_device *ssdp = sdp->device; + struct sg_fd *fp; + char *disk_name; + int *countp; + + countp = fd_counterp ? fd_counterp : &my_count; + disk_name = (sdp->name[0] ? sdp->name : "?_?"); + n += scnprintf(obp + n, len - n, " >>> device=%s ", disk_name); + n += scnprintf(obp + n, len - n, "%d:%d:%d:%llu ", ssdp->host->host_no, + ssdp->channel, ssdp->id, ssdp->lun); + n += scnprintf(obp + n, len - n, + " max_sgat_sz,elems=2^%d,%d excl=%d open_cnt=%d\n", + ilog2(sdp->max_sgat_sz), sdp->max_sgat_elems, + SG_HAVE_EXCLUDE(sdp), atomic_read(&sdp->open_cnt)); xa_for_each(&sdp->sfp_arr, idx, fp) { if (!fp) continue; - k++; - seq_printf(s, " FD(%d): timeout=%dms buflen=%d (res)sgat=%d idx=%lu\n", - k, jiffies_to_msecs(fp->timeout), - fp->rsv_srp->sgat_h.buflen, - (int)fp->rsv_srp->sgat_h.num_sgat, idx); - seq_printf(s, " cmd_q=%d f_packid=%d k_orphan=%d closed=0\n", - (int)test_bit(SG_FFD_CMD_Q, fp->ffd_bm), - (int)test_bit(SG_FFD_FORCE_PACKID, fp->ffd_bm), - (int)test_bit(SG_FFD_KEEP_ORPHAN, fp->ffd_bm)); - seq_printf(s, " submitted=%d waiting=%d\n", - atomic_read(&fp->submitted), - atomic_read(&fp->waiting)); - xa_for_each(&fp->srp_arr, idx2, srp) { - const struct sg_slice_hdr3 *sh3p = &srp->s_hdr3; - bool is_v3 = (sh3p->interface_id != '\0'); - enum sg_rq_state rq_st = atomic_read(&srp->rq_st); - - if (!srp) - continue; - if (srp->parentfp->rsv_srp == srp) { - if (is_v3 && (SG_FLAG_MMAP_IO & sh3p->flags)) - cp = " mmap>> "; - else - cp = " rb>> "; - } else { - if (SG_INFO_DIRECT_IO_MASK & srp->rq_info) - cp = " dio>> "; - else - cp = " "; - } - seq_puts(s, cp); - seq_puts(s, sg_rq_st_str(rq_st, false)); - seq_printf(s, ": id=%d len/blen=%d/%d", - srp->pack_id, srp->sgat_h.dlen, - srp->sgat_h.buflen); - if (rq_st == SG_RS_AWAIT_RCV || - rq_st == SG_RS_INACTIVE) { - seq_printf(s, " dur=%d", srp->duration); - goto fin_line; - } - ms = jiffies_to_msecs(jiffies); - seq_printf(s, " t_o/elap=%d/%d", - (is_v3 ? sh3p->timeout : - jiffies_to_msecs(fp->timeout)), - (ms > srp->duration ? ms - srp->duration : - 0)); -fin_line: - seq_printf(s, "ms sgat=%d op=0x%02x\n", - srp->sgat_h.num_sgat, (int)srp->cmd_opcode); - } - if (xa_empty(&fp->srp_arr)) - seq_puts(s, " No requests active\n"); + ++*countp; + n += scnprintf(obp + n, len - n, " FD(%d): ", *countp); + n += sg_proc_debug_fd(fp, obp + n, len - n, idx); } + return n; } +/* Called via dbg_seq_ops once for each sg device */ static int sg_proc_seq_show_debug(struct seq_file *s, void *v) { + bool found = false; + bool trunc = false; + const int bp_len = SG_PROC_DEBUG_SZ; + int n = 0; + int k = 0; + unsigned long iflags; struct sg_proc_deviter *it = (struct sg_proc_deviter *)v; struct sg_device *sdp; - unsigned long iflags; + int *fdi_p; + char *bp; + char *disk_name; + char b1[128]; + b1[0] = '\0'; if (it && (0 == it->index)) seq_printf(s, "max_active_device=%d def_reserved_size=%d\n", - (int)it->max, sg_big_buff); - + (int)it->max, def_reserved_size); + fdi_p = it ? &it->fd_index : &k; + bp = kzalloc(bp_len, __GFP_NOWARN | GFP_KERNEL); + if (!bp) { + seq_printf(s, "%s: Unable to allocate %d on heap, finish\n", + __func__, bp_len); + return -ENOMEM; + } read_lock_irqsave(&sg_index_lock, iflags); sdp = it ? sg_lookup_dev(it->index) : NULL; if (NULL == sdp) goto skip; if (!xa_empty(&sdp->sfp_arr)) { - seq_printf(s, " >>> device=%s ", sdp->name); + found = true; + disk_name = (sdp->name[0] ? sdp->name : "?_?"); if (SG_IS_DETACHING(sdp)) - seq_puts(s, "detaching pending close "); + snprintf(b1, sizeof(b1), " >>> device=%s %s\n", + disk_name, "detaching pending close\n"); else if (sdp->device) { - struct scsi_device *scsidp = sdp->device; - - seq_printf(s, "%d:%d:%d:%llu em=%d", - scsidp->host->host_no, - scsidp->channel, scsidp->id, - scsidp->lun, - scsidp->host->hostt->emulated); + n = sg_proc_debug_sdev(sdp, bp, bp_len, fdi_p); + if (n >= bp_len - 1) { + trunc = true; + if (bp[bp_len - 2] != '\n') + bp[bp_len - 2] = '\n'; + } + } else { + snprintf(b1, sizeof(b1), " >>> device=%s %s\n", + disk_name, "sdp->device==NULL, skip"); } - seq_printf(s, " max_sgat_sz=%d excl=%d open_cnt=%d\n", - sdp->max_sgat_sz, SG_HAVE_EXCLUDE(sdp), - atomic_read(&sdp->open_cnt)); - sg_proc_debug_helper(s, sdp); } skip: read_unlock_irqrestore(&sg_index_lock, iflags); + if (found) { + if (n > 0) { + seq_puts(s, bp); + if (seq_has_overflowed(s)) + goto s_ovfl; + if (trunc) + seq_printf(s, " >> Output truncated %s\n", + "due to buffer size"); + } else if (b1[0]) { + seq_puts(s, b1); + if (seq_has_overflowed(s)) + goto s_ovfl; + } + } +s_ovfl: + kfree(bp); return 0; } -#endif /* CONFIG_SCSI_PROC_FS (~300 lines back) */ +#endif /* CONFIG_SCSI_PROC_FS (~400 lines back) */ module_init(init_sg); module_exit(exit_sg);