From: Arun Easi <aeasi@xxxxxxxxxxx> Adding a message based tracing mechanism where a rotating number of messages are captured in a trace structure. Disable/enable/resize operations are allowed via debugfs interfaces. Signed-off-by: Arun Easi <aeasi@xxxxxxxxxxx> Signed-off-by: Nilesh Javali <njavali@xxxxxxxxxxx> --- drivers/scsi/qla2xxx/qla_dbg.c | 12 +++ drivers/scsi/qla2xxx/qla_dbg.h | 140 +++++++++++++++++++++++++++++++++ drivers/scsi/qla2xxx/qla_def.h | 31 ++++++++ drivers/scsi/qla2xxx/qla_dfs.c | 102 ++++++++++++++++++++++++ drivers/scsi/qla2xxx/qla_os.c | 5 ++ 5 files changed, 290 insertions(+) diff --git a/drivers/scsi/qla2xxx/qla_dbg.c b/drivers/scsi/qla2xxx/qla_dbg.c index 7cf1f78cbaee..c4ba8ac51d27 100644 --- a/drivers/scsi/qla2xxx/qla_dbg.c +++ b/drivers/scsi/qla2xxx/qla_dbg.c @@ -2777,3 +2777,15 @@ ql_dbg_qp(uint32_t level, struct qla_qpair *qpair, int32_t id, va_end(va); } + +#ifdef QLA_TRACING +void qla_tracing_init(void) +{ + if (is_kdump_kernel()) + return; +} + +void qla_tracing_exit(void) +{ +} +#endif /* QLA_TRACING */ diff --git a/drivers/scsi/qla2xxx/qla_dbg.h b/drivers/scsi/qla2xxx/qla_dbg.h index feeb1666227f..e0d91a1f81c0 100644 --- a/drivers/scsi/qla2xxx/qla_dbg.h +++ b/drivers/scsi/qla2xxx/qla_dbg.h @@ -5,6 +5,7 @@ */ #include "qla_def.h" +#include <linux/delay.h> /* * Firmware Dump structure definition @@ -321,6 +322,145 @@ struct qla2xxx_fw_dump { extern uint ql_errlev; +#ifdef QLA_TRACING +#include <linux/crash_dump.h> + +extern void qla_tracing_init(void); +extern void qla_tracing_exit(void); + +static inline int +ql_mask_match_ext(uint level, int *log_tunable) +{ + if (*log_tunable == 1) + *log_tunable = QL_DBG_DEFAULT1_MASK; + + return (level & *log_tunable) == level; +} + +static inline int +__qla_trace_get(struct qla_trace *trc) +{ + if (test_bit(QLA_TRACE_QUIESCE, &trc->flags)) + return -EIO; + atomic_inc(&trc->ref_count); + return 0; +} + +static inline int +qla_trace_get(struct qla_trace *trc) +{ + unsigned long flags; + int ret; + + spin_lock_irqsave(&trc->trc_lock, flags); + ret = __qla_trace_get(trc); + spin_unlock_irqrestore(&trc->trc_lock, flags); + + return ret; +} + +static inline void +qla_trace_put(struct qla_trace *trc) +{ + wmb(); + atomic_dec(&trc->ref_count); +} + +static inline char * +qla_get_trace_next(struct qla_trace *trc) +{ + uint32_t t_ind; + char *buf; + unsigned long flags; + + spin_lock_irqsave(&trc->trc_lock, flags); + if (!test_bit(QLA_TRACE_ENABLED, &trc->flags) || + __qla_trace_get(trc)) { + spin_unlock_irqrestore(&trc->trc_lock, flags); + return NULL; + } + t_ind = trc->trace_ind = qla_trace_ind_norm(trc, trc->trace_ind + 1); + spin_unlock_irqrestore(&trc->trc_lock, flags); + + if (!t_ind) + set_bit(QLA_TRACE_WRAPPED, &trc->flags); + + buf = qla_trace_record(trc, t_ind); + /* Put an end marker '>' for the next record. */ + qla_trace_record(trc, qla_trace_ind_norm(trc, t_ind + 1))[0] = '>'; + + return buf; +} + +static inline int +qla_trace_quiesce(struct qla_trace *trc) +{ + unsigned long flags; + u32 cnt = 0; + int ret = 0; + + set_bit(QLA_TRACE_QUIESCE, &trc->flags); + + spin_lock_irqsave(&trc->trc_lock, flags); + while (atomic_read(&trc->ref_count)) { + spin_unlock_irqrestore(&trc->trc_lock, flags); + + msleep(1); + + spin_lock_irqsave(&trc->trc_lock, flags); + cnt++; + if (cnt > 10 * 1000) { + pr_info("qla2xxx: Trace could not be quiesced now (count=%d).", + atomic_read(&trc->ref_count)); + /* Leave trace enabled */ + clear_bit(QLA_TRACE_QUIESCE, &trc->flags); + ret = -EIO; + break; + } + } + spin_unlock_irqrestore(&trc->trc_lock, flags); + return ret; +} + +static inline void +qla_trace_init(struct qla_trace *trc, char *name, u32 num_entries) +{ + if (trc->recs) + return; + + memset(trc, 0, sizeof(*trc)); + + trc->name = name; + spin_lock_init(&trc->trc_lock); + if (!num_entries) + return; + trc->num_entries = num_entries; + trc->recs = vzalloc(trc->num_entries * + sizeof(struct qla_trace_rec)); + if (!trc->recs) + return; + + set_bit(QLA_TRACE_ENABLED, &trc->flags); +} + +static inline void +qla_trace_uninit(struct qla_trace *trc) +{ + if (!trc->recs) + return; + + vfree(trc->recs); + trc->recs = NULL; + clear_bit(QLA_TRACE_ENABLED, &trc->flags); +} + +#else /* QLA_TRACING */ +#define qla_trace_init(trc, name, num) +#define qla_trace_uninit(trc) +#define qla_tracing_init() +#define qla_tracing_exit() +#endif /* QLA_TRACING */ + void __attribute__((format (printf, 4, 5))) ql_dbg(uint, scsi_qla_host_t *vha, uint, const char *fmt, ...); void __attribute__((format (printf, 4, 5))) diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h index 22274b405d01..39322105e7be 100644 --- a/drivers/scsi/qla2xxx/qla_def.h +++ b/drivers/scsi/qla2xxx/qla_def.h @@ -35,6 +35,37 @@ #include <uapi/scsi/fc/fc_els.h> +#define QLA_TRACING /* Captures driver messages to buffer */ + +#ifdef QLA_TRACING +#define QLA_TRACE_LINE_SIZE 256 /* Biggest so far is ~215 */ +#define qla_trace_ind_norm(_trc, _ind) ((_ind) >= (_trc)->num_entries ? \ + 0 : (_ind)) +#define qla_trace_record(_trc, __ind) ((_trc)->recs[__ind].buf) +#define qla_trace_record_len (sizeof(struct qla_trace_rec)) +#define qla_trace_start(_trc) qla_trace_record(_trc, 0) +#define qla_trace_len(_trc) ((_trc)->num_entries) +#define qla_trace_size(_trc) (qla_trace_record_len * \ + (_trc)->num_entries) +#define qla_trace_cur_ind(_trc) ((_trc)->trace_ind) +struct qla_trace_rec { + char buf[QLA_TRACE_LINE_SIZE]; +}; + +struct qla_trace { +#define QLA_TRACE_ENABLED 0 /* allow trace writes or not */ +#define QLA_TRACE_WRAPPED 1 +#define QLA_TRACE_QUIESCE 2 + unsigned long flags; + atomic_t ref_count; + u32 num_entries; + u32 trace_ind; + spinlock_t trc_lock; + char *name; + struct qla_trace_rec *recs; +}; +#endif /* QLA_TRACING */ + #define QLA_DFS_DEFINE_DENTRY(_debugfs_file_name) \ struct dentry *dfs_##_debugfs_file_name #define QLA_DFS_ROOT_DEFINE_DENTRY(_debugfs_file_name) \ diff --git a/drivers/scsi/qla2xxx/qla_dfs.c b/drivers/scsi/qla2xxx/qla_dfs.c index c3c8b9536ef6..98c6390ad1f1 100644 --- a/drivers/scsi/qla2xxx/qla_dfs.c +++ b/drivers/scsi/qla2xxx/qla_dfs.c @@ -489,6 +489,108 @@ qla_dfs_naqp_show(struct seq_file *s, void *unused) return 0; } +#ifdef QLA_TRACING +static char *trace_help = "\ +# Format:\n\ +# <msec> <cpu#> <message>\n\ +#\n\ +# Trace control by writing:\n\ +# 'enable' - to enable this trace\n\ +# 'disable' - to disable this trace\n\ +# 'resize=<nlines>' - to resize this trace to <nlines>\n\ +#\n"; + +static int +qla_dfs_trace_show(struct seq_file *s, void *unused) +{ + struct qla_trace *trc = s->private; + char *buf; + u32 t_ind = 0, i; + + seq_puts(s, trace_help); + + if (qla_trace_get(trc)) + return 0; + + seq_printf(s, "# Trace max lines = %d, writes = %s\n#\n", + trc->num_entries, test_bit(QLA_TRACE_ENABLED, + &trc->flags) ? "enabled" : "disabled"); + + if (test_bit(QLA_TRACE_WRAPPED, &trc->flags)) + t_ind = qla_trace_cur_ind(trc) + 1; + + for (i = 0; i < qla_trace_len(trc); i++, t_ind++) { + t_ind = qla_trace_ind_norm(trc, t_ind); + buf = qla_trace_record(trc, t_ind); + if (!buf[0]) + continue; + seq_puts(s, buf); + } + + mb(); + qla_trace_put(trc); + return 0; +} + +#define string_is(_buf, _str_val) \ + (strncmp(_str_val, _buf, strlen(_str_val)) == 0) + +static ssize_t +qla_dfs_trace_write(struct file *file, const char __user *buffer, + size_t count, loff_t *pos) +{ + struct seq_file *s = file->private_data; + struct qla_trace *trc = s->private; + char buf[32]; + ssize_t ret = count; + + memset(buf, 0, sizeof(buf)); + if (copy_from_user(buf, buffer, min(sizeof(buf), count))) + return -EFAULT; + + if (string_is(buf, "enable")) { + if (!trc->recs) { + pr_warn("qla2xxx: '%s' is empty, resize before enabling.\n", + trc->name); + return -EINVAL; + } + pr_info("qla2xxx: Enabling trace '%s'\n", trc->name); + set_bit(QLA_TRACE_ENABLED, &trc->flags); + } else if (string_is(buf, "disable")) { + pr_info("qla2xxx: Disabling trace '%s'\n", trc->name); + clear_bit(QLA_TRACE_ENABLED, &trc->flags); + } else if (string_is(buf, "resize")) { + u32 new_len; + + if (sscanf(buf, "resize=%d", &new_len) != 1) + return -EINVAL; + if (new_len == trc->num_entries) { + pr_info("qla2xxx: New trace size is same as old.\n"); + return count; + } + pr_info("qla2xxx: Changing trace '%s' size to %d\n", + trc->name, new_len); + if (qla_trace_quiesce(trc)) { + ret = -EBUSY; + goto done; + } + qla_trace_uninit(trc); + /* + * Go through init once again to start creating traces + * based on the respective tunable. + */ + qla_trace_init(trc, trc->name, new_len); + if (!trc->recs) { + pr_warn("qla2xxx: Trace allocation failed for '%s'\n", + trc->name); + ret = -ENOMEM; + } + } +done: + return ret; +} +#endif /* QLA_TRACING */ + /* * Helper macros for setting up debugfs entries. * _name: The name of the debugfs entry diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index 0bd0fd1042df..0d2397069cac 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -8191,6 +8191,8 @@ qla2x00_module_init(void) BUILD_BUG_ON(sizeof(sw_info_t) != 32); BUILD_BUG_ON(sizeof(target_id_t) != 2); + qla_tracing_init(); + /* Allocate cache for SRBs. */ srb_cachep = kmem_cache_create("qla2xxx_srbs", sizeof(srb_t), 0, SLAB_HWCACHE_ALIGN, NULL); @@ -8269,6 +8271,7 @@ qla2x00_module_init(void) destroy_cache: kmem_cache_destroy(srb_cachep); + qla_tracing_exit(); return ret; } @@ -8287,6 +8290,8 @@ qla2x00_module_exit(void) fc_release_transport(qla2xxx_transport_template); qlt_exit(); kmem_cache_destroy(srb_cachep); + + qla_tracing_exit(); } module_init(qla2x00_module_init); -- 2.19.0.rc0