summaryrefslogtreecommitdiff
path: root/usr/src/uts/common/io/sata/impl/sata.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/uts/common/io/sata/impl/sata.c')
-rw-r--r--usr/src/uts/common/io/sata/impl/sata.c253
1 files changed, 250 insertions, 3 deletions
diff --git a/usr/src/uts/common/io/sata/impl/sata.c b/usr/src/uts/common/io/sata/impl/sata.c
index 1115e99ea8..60e6c6bbd4 100644
--- a/usr/src/uts/common/io/sata/impl/sata.c
+++ b/usr/src/uts/common/io/sata/impl/sata.c
@@ -102,7 +102,7 @@ static void sata_save_atapi_trace(sata_pkt_txlate_t *, int);
sata_save_atapi_trace(spx, count);
#else
-#define SATA_LOG_D(arg)
+#define SATA_LOG_D(args) sata_trace_log args
#define SATAATAPITRACE(spx, count)
#endif
@@ -309,6 +309,7 @@ static int sata_build_lsense_page_30(sata_drive_info_t *, uint8_t *,
static void sata_save_drive_settings(sata_drive_info_t *);
static void sata_show_drive_info(sata_hba_inst_t *, sata_drive_info_t *);
static void sata_log(sata_hba_inst_t *, uint_t, char *fmt, ...);
+static void sata_trace_log(sata_hba_inst_t *, uint_t, const char *fmt, ...);
static int sata_fetch_smart_return_status(sata_hba_inst_t *,
sata_drive_info_t *);
static int sata_fetch_smart_data(sata_hba_inst_t *, sata_drive_info_t *,
@@ -411,6 +412,17 @@ static kmutex_t sata_log_mutex; /* protects log */
static char sata_log_buf[256];
+/*
+ * sata trace debug
+ */
+static sata_trace_rbuf_t *sata_debug_rbuf;
+static sata_trace_dmsg_t *sata_trace_dmsg_alloc(void);
+static void sata_trace_dmsg_free(void);
+static void sata_trace_rbuf_alloc(void);
+static void sata_trace_rbuf_free(void);
+
+int dmsg_ring_size = DMSG_RING_SIZE;
+
/* Default write cache setting for SATA hard disks */
int sata_write_cache = 1; /* enabled */
@@ -543,10 +555,12 @@ _init()
mutex_init(&sata_event_mutex, NULL, MUTEX_DRIVER, NULL);
mutex_init(&sata_log_mutex, NULL, MUTEX_DRIVER, NULL);
cv_init(&sata_event_cv, NULL, CV_DRIVER, NULL);
+ sata_trace_rbuf_alloc();
if ((rval = mod_install(&modlinkage)) != 0) {
#ifdef SATA_DEBUG
cmn_err(CE_WARN, "sata: _init: mod_install failed\n");
#endif
+ sata_trace_rbuf_free();
mutex_destroy(&sata_log_mutex);
cv_destroy(&sata_event_cv);
mutex_destroy(&sata_event_mutex);
@@ -563,6 +577,7 @@ _fini()
if ((rval = mod_remove(&modlinkage)) != 0)
return (rval);
+ sata_trace_rbuf_free();
mutex_destroy(&sata_log_mutex);
cv_destroy(&sata_event_cv);
mutex_destroy(&sata_event_mutex);
@@ -13825,7 +13840,7 @@ static void
sata_log(sata_hba_inst_t *sata_hba_inst, uint_t level, char *fmt, ...)
{
char pathname[128];
- dev_info_t *dip;
+ dev_info_t *dip = NULL;
va_list ap;
mutex_enter(&sata_log_mutex);
@@ -13854,6 +13869,9 @@ sata_log(sata_hba_inst_t *sata_hba_inst, uint_t level, char *fmt, ...)
}
}
+ /* sata trace debug */
+ sata_trace_debug(dip, sata_log_buf);
+
mutex_exit(&sata_log_mutex);
}
@@ -15835,7 +15853,7 @@ static uint32_t sata_fault_suspend_count = 0;
*/
-static void
+static void
sata_inject_pkt_fault(sata_pkt_t *spkt, int *rval, int fault)
{
@@ -15950,3 +15968,232 @@ sata_inject_pkt_fault(sata_pkt_t *spkt, int *rval, int fault)
}
#endif
+
+/*
+ * SATA Trace Ring Buffer
+ * ----------------------
+ *
+ * Overview
+ *
+ * The SATA trace ring buffer is a ring buffer created and managed by
+ * the SATA framework module that can be used by any module or driver
+ * within the SATA framework to store debug messages.
+ *
+ * Ring Buffer Interfaces:
+ *
+ * sata_vtrace_debug() <-- Adds debug message to ring buffer
+ * sata_trace_debug() <-- Wraps varargs into sata_vtrace_debug()
+ *
+ * Note that the sata_trace_debug() interface was created to give
+ * consumers the flexibilty of sending debug messages to ring buffer
+ * as variable arguments. Consumers can send type va_list debug
+ * messages directly to sata_vtrace_debug(). The sata_trace_debug()
+ * and sata_vtrace_debug() relationship is similar to that of
+ * cmn_err(9F) and vcmn_err(9F).
+ *
+ * Below is a diagram of the SATA trace ring buffer interfaces and
+ * sample consumers:
+ *
+ * +---------------------------------+
+ * | o o SATA Framework Module |
+ * | o SATA o +------------------+ +------------------+
+ * |o Trace o <--|sata_vtrace_debug/|<-----|SATA HBA Driver #1|
+ * |o R-Buf o |sata_trace_debug |<--+ +------------------+
+ * | o o +------------------+ | +------------------+
+ * | o o ^ | +--|SATA HBA Driver #2|
+ * | | | +------------------+
+ * | +------------------+ |
+ * | |SATA Debug Message| |
+ * | +------------------+ |
+ * +---------------------------------+
+ *
+ * Supporting Routines:
+ *
+ * sata_trace_rbuf_alloc() <-- Initializes ring buffer
+ * sata_trace_rbuf_free() <-- Destroys ring buffer
+ * sata_trace_dmsg_alloc() <-- Creates or reuses buffer in ring buffer
+ * sata_trace_dmsg_free() <-- Destroys content of ring buffer
+ *
+ * The default SATA trace ring buffer size is defined by DMSG_RING_SIZE.
+ * The ring buffer size can be adjusted by setting dmsg_ring_size in
+ * /etc/system to desired size in unit of bytes.
+ *
+ * The individual debug message size in the ring buffer is restricted
+ * to DMSG_BUF_SIZE.
+ */
+void
+sata_vtrace_debug(dev_info_t *dip, const char *fmt, va_list ap)
+{
+ sata_trace_dmsg_t *dmsg;
+
+ if (sata_debug_rbuf == NULL) {
+ return;
+ }
+
+ /*
+ * If max size of ring buffer is smaller than size
+ * required for one debug message then just return
+ * since we have no room for the debug message.
+ */
+ if (sata_debug_rbuf->maxsize < (sizeof (sata_trace_dmsg_t))) {
+ return;
+ }
+
+ mutex_enter(&sata_debug_rbuf->lock);
+
+ /* alloc or reuse on ring buffer */
+ dmsg = sata_trace_dmsg_alloc();
+
+ if (dmsg == NULL) {
+ /* resource allocation failed */
+ mutex_exit(&sata_debug_rbuf->lock);
+ return;
+ }
+
+ dmsg->dip = dip;
+ gethrestime(&dmsg->timestamp);
+
+ (void) vsnprintf(dmsg->buf, sizeof (dmsg->buf), fmt, ap);
+
+ mutex_exit(&sata_debug_rbuf->lock);
+}
+
+void
+sata_trace_debug(dev_info_t *dip, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ sata_vtrace_debug(dip, fmt, ap);
+ va_end(ap);
+}
+
+/*
+ * This routine is used to manage debug messages
+ * on ring buffer.
+ */
+static sata_trace_dmsg_t *
+sata_trace_dmsg_alloc(void)
+{
+ sata_trace_dmsg_t *dmsg_alloc, *dmsg = sata_debug_rbuf->dmsgp;
+
+ if (sata_debug_rbuf->looped == TRUE) {
+ sata_debug_rbuf->dmsgp = dmsg->next;
+ return (sata_debug_rbuf->dmsgp);
+ }
+
+ /*
+ * If we're looping for the first time,
+ * connect the ring.
+ */
+ if (((sata_debug_rbuf->size + (sizeof (sata_trace_dmsg_t))) >
+ sata_debug_rbuf->maxsize) && (sata_debug_rbuf->dmsgh != NULL)) {
+ dmsg->next = sata_debug_rbuf->dmsgh;
+ sata_debug_rbuf->dmsgp = sata_debug_rbuf->dmsgh;
+ sata_debug_rbuf->looped = TRUE;
+ return (sata_debug_rbuf->dmsgp);
+ }
+
+ /* If we've gotten this far then memory allocation is needed */
+ dmsg_alloc = kmem_zalloc(sizeof (sata_trace_dmsg_t), KM_NOSLEEP);
+ if (dmsg_alloc == NULL) {
+ sata_debug_rbuf->allocfailed++;
+ return (dmsg_alloc);
+ } else {
+ sata_debug_rbuf->size += sizeof (sata_trace_dmsg_t);
+ }
+
+ if (sata_debug_rbuf->dmsgp != NULL) {
+ dmsg->next = dmsg_alloc;
+ sata_debug_rbuf->dmsgp = dmsg->next;
+ return (sata_debug_rbuf->dmsgp);
+ } else {
+ /*
+ * We should only be here if we're initializing
+ * the ring buffer.
+ */
+ if (sata_debug_rbuf->dmsgh == NULL) {
+ sata_debug_rbuf->dmsgh = dmsg_alloc;
+ } else {
+ /* Something is wrong */
+ kmem_free(dmsg_alloc, sizeof (sata_trace_dmsg_t));
+ return (NULL);
+ }
+
+ sata_debug_rbuf->dmsgp = dmsg_alloc;
+ return (sata_debug_rbuf->dmsgp);
+ }
+}
+
+
+/*
+ * Free all messages on debug ring buffer.
+ */
+static void
+sata_trace_dmsg_free(void)
+{
+ sata_trace_dmsg_t *dmsg_next, *dmsg = sata_debug_rbuf->dmsgh;
+
+ while (dmsg != NULL) {
+ dmsg_next = dmsg->next;
+ kmem_free(dmsg, sizeof (sata_trace_dmsg_t));
+
+ /*
+ * If we've looped around the ring than we're done.
+ */
+ if (dmsg_next == sata_debug_rbuf->dmsgh) {
+ break;
+ } else {
+ dmsg = dmsg_next;
+ }
+ }
+}
+
+
+/*
+ * This function can block
+ */
+static void
+sata_trace_rbuf_alloc(void)
+{
+ sata_debug_rbuf = kmem_zalloc(sizeof (sata_trace_rbuf_t), KM_SLEEP);
+
+ mutex_init(&sata_debug_rbuf->lock, NULL, MUTEX_DRIVER, NULL);
+
+ if (dmsg_ring_size > 0) {
+ sata_debug_rbuf->maxsize = (size_t)dmsg_ring_size;
+ }
+}
+
+
+static void
+sata_trace_rbuf_free(void)
+{
+ sata_trace_dmsg_free();
+ mutex_destroy(&sata_debug_rbuf->lock);
+ kmem_free(sata_debug_rbuf, sizeof (sata_trace_rbuf_t));
+}
+
+/*
+ * If SATA_DEBUG is not defined then this routine is called instead
+ * of sata_log() via the SATA_LOG_D macro.
+ */
+static void
+sata_trace_log(sata_hba_inst_t *sata_hba_inst, uint_t level,
+ const char *fmt, ...)
+{
+#ifndef __lock_lint
+ _NOTE(ARGUNUSED(level))
+#endif
+
+ dev_info_t *dip = NULL;
+ va_list ap;
+
+ if (sata_hba_inst != NULL) {
+ dip = SATA_DIP(sata_hba_inst);
+ }
+
+ va_start(ap, fmt);
+ sata_vtrace_debug(dip, fmt, ap);
+ va_end(ap);
+}