linux/drivers/scsi/fnic/fnic_trace.c
<<
>>
Prefs
   1/*
   2 * Copyright 2012 Cisco Systems, Inc.  All rights reserved.
   3 *
   4 * This program is free software; you may redistribute it and/or modify
   5 * it under the terms of the GNU General Public License as published by
   6 * the Free Software Foundation; version 2 of the License.
   7 *
   8 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
   9 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  10 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  11 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
  12 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
  13 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  14 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  15 * SOFTWARE.
  16 */
  17
  18#include <linux/module.h>
  19#include <linux/mempool.h>
  20#include <linux/errno.h>
  21#include <linux/spinlock.h>
  22#include <linux/kallsyms.h>
  23#include "fnic_io.h"
  24#include "fnic.h"
  25
  26unsigned int trace_max_pages;
  27static int fnic_max_trace_entries;
  28
  29static unsigned long fnic_trace_buf_p;
  30static DEFINE_SPINLOCK(fnic_trace_lock);
  31
  32static fnic_trace_dbg_t fnic_trace_entries;
  33int fnic_tracing_enabled = 1;
  34
  35/*
  36 * fnic_trace_get_buf - Give buffer pointer to user to fill up trace information
  37 *
  38 * Description:
  39 * This routine gets next available trace buffer entry location @wr_idx
  40 * from allocated trace buffer pages and give that memory location
  41 * to user to store the trace information.
  42 *
  43 * Return Value:
  44 * This routine returns pointer to next available trace entry
  45 * @fnic_buf_head for user to fill trace information.
  46 */
  47fnic_trace_data_t *fnic_trace_get_buf(void)
  48{
  49        unsigned long fnic_buf_head;
  50        unsigned long flags;
  51
  52        spin_lock_irqsave(&fnic_trace_lock, flags);
  53
  54        /*
  55         * Get next available memory location for writing trace information
  56         * at @wr_idx and increment @wr_idx
  57         */
  58        fnic_buf_head =
  59                fnic_trace_entries.page_offset[fnic_trace_entries.wr_idx];
  60        fnic_trace_entries.wr_idx++;
  61
  62        /*
  63         * Verify if trace buffer is full then change wd_idx to
  64         * start from zero
  65         */
  66        if (fnic_trace_entries.wr_idx >= fnic_max_trace_entries)
  67                fnic_trace_entries.wr_idx = 0;
  68
  69        /*
  70         * Verify if write index @wr_idx and read index @rd_idx are same then
  71         * increment @rd_idx to move to next entry in trace buffer
  72         */
  73        if (fnic_trace_entries.wr_idx == fnic_trace_entries.rd_idx) {
  74                fnic_trace_entries.rd_idx++;
  75                if (fnic_trace_entries.rd_idx >= fnic_max_trace_entries)
  76                        fnic_trace_entries.rd_idx = 0;
  77        }
  78        spin_unlock_irqrestore(&fnic_trace_lock, flags);
  79        return (fnic_trace_data_t *)fnic_buf_head;
  80}
  81
  82/*
  83 * fnic_get_trace_data - Copy trace buffer to a memory file
  84 * @fnic_dbgfs_t: pointer to debugfs trace buffer
  85 *
  86 * Description:
  87 * This routine gathers the fnic trace debugfs data from the fnic_trace_data_t
  88 * buffer and dumps it to fnic_dbgfs_t. It will start at the rd_idx entry in
  89 * the log and process the log until the end of the buffer. Then it will gather
  90 * from the beginning of the log and process until the current entry @wr_idx.
  91 *
  92 * Return Value:
  93 * This routine returns the amount of bytes that were dumped into fnic_dbgfs_t
  94 */
  95int fnic_get_trace_data(fnic_dbgfs_t *fnic_dbgfs_prt)
  96{
  97        int rd_idx;
  98        int wr_idx;
  99        int len = 0;
 100        unsigned long flags;
 101        char str[KSYM_SYMBOL_LEN];
 102        struct timespec val;
 103        fnic_trace_data_t *tbp;
 104
 105        spin_lock_irqsave(&fnic_trace_lock, flags);
 106        rd_idx = fnic_trace_entries.rd_idx;
 107        wr_idx = fnic_trace_entries.wr_idx;
 108        if (wr_idx < rd_idx) {
 109                while (1) {
 110                        /* Start from read index @rd_idx */
 111                        tbp = (fnic_trace_data_t *)
 112                                  fnic_trace_entries.page_offset[rd_idx];
 113                        if (!tbp) {
 114                                spin_unlock_irqrestore(&fnic_trace_lock, flags);
 115                                return 0;
 116                        }
 117                        /* Convert function pointer to function name */
 118                        if (sizeof(unsigned long) < 8) {
 119                                sprint_symbol(str, tbp->fnaddr.low);
 120                                jiffies_to_timespec(tbp->timestamp.low, &val);
 121                        } else {
 122                                sprint_symbol(str, tbp->fnaddr.val);
 123                                jiffies_to_timespec(tbp->timestamp.val, &val);
 124                        }
 125                        /*
 126                         * Dump trace buffer entry to memory file
 127                         * and increment read index @rd_idx
 128                         */
 129                        len += snprintf(fnic_dbgfs_prt->buffer + len,
 130                                  (trace_max_pages * PAGE_SIZE * 3) - len,
 131                                  "%16lu.%16lu %-50s %8x %8x %16llx %16llx "
 132                                  "%16llx %16llx %16llx\n", val.tv_sec,
 133                                  val.tv_nsec, str, tbp->host_no, tbp->tag,
 134                                  tbp->data[0], tbp->data[1], tbp->data[2],
 135                                  tbp->data[3], tbp->data[4]);
 136                        rd_idx++;
 137                        /*
 138                         * If rd_idx is reached to maximum trace entries
 139                         * then move rd_idx to zero
 140                         */
 141                        if (rd_idx > (fnic_max_trace_entries-1))
 142                                rd_idx = 0;
 143                        /*
 144                         * Continure dumpping trace buffer entries into
 145                         * memory file till rd_idx reaches write index
 146                         */
 147                        if (rd_idx == wr_idx)
 148                                break;
 149                }
 150        } else if (wr_idx > rd_idx) {
 151                while (1) {
 152                        /* Start from read index @rd_idx */
 153                        tbp = (fnic_trace_data_t *)
 154                                  fnic_trace_entries.page_offset[rd_idx];
 155                        if (!tbp) {
 156                                spin_unlock_irqrestore(&fnic_trace_lock, flags);
 157                                return 0;
 158                        }
 159                        /* Convert function pointer to function name */
 160                        if (sizeof(unsigned long) < 8) {
 161                                sprint_symbol(str, tbp->fnaddr.low);
 162                                jiffies_to_timespec(tbp->timestamp.low, &val);
 163                        } else {
 164                                sprint_symbol(str, tbp->fnaddr.val);
 165                                jiffies_to_timespec(tbp->timestamp.val, &val);
 166                        }
 167                        /*
 168                         * Dump trace buffer entry to memory file
 169                         * and increment read index @rd_idx
 170                         */
 171                        len += snprintf(fnic_dbgfs_prt->buffer + len,
 172                                  (trace_max_pages * PAGE_SIZE * 3) - len,
 173                                  "%16lu.%16lu %-50s %8x %8x %16llx %16llx "
 174                                  "%16llx %16llx %16llx\n", val.tv_sec,
 175                                  val.tv_nsec, str, tbp->host_no, tbp->tag,
 176                                  tbp->data[0], tbp->data[1], tbp->data[2],
 177                                  tbp->data[3], tbp->data[4]);
 178                        rd_idx++;
 179                        /*
 180                         * Continue dumpping trace buffer entries into
 181                         * memory file till rd_idx reaches write index
 182                         */
 183                        if (rd_idx == wr_idx)
 184                                break;
 185                }
 186        }
 187        spin_unlock_irqrestore(&fnic_trace_lock, flags);
 188        return len;
 189}
 190
 191/*
 192 * fnic_trace_buf_init - Initialize fnic trace buffer logging facility
 193 *
 194 * Description:
 195 * Initialize trace buffer data structure by allocating required memory and
 196 * setting page_offset information for every trace entry by adding trace entry
 197 * length to previous page_offset value.
 198 */
 199int fnic_trace_buf_init(void)
 200{
 201        unsigned long fnic_buf_head;
 202        int i;
 203        int err = 0;
 204
 205        trace_max_pages = fnic_trace_max_pages;
 206        fnic_max_trace_entries = (trace_max_pages * PAGE_SIZE)/
 207                                          FNIC_ENTRY_SIZE_BYTES;
 208
 209        fnic_trace_buf_p = (unsigned long)vmalloc((trace_max_pages * PAGE_SIZE));
 210        if (!fnic_trace_buf_p) {
 211                printk(KERN_ERR PFX "Failed to allocate memory "
 212                                  "for fnic_trace_buf_p\n");
 213                err = -ENOMEM;
 214                goto err_fnic_trace_buf_init;
 215        }
 216        memset((void *)fnic_trace_buf_p, 0, (trace_max_pages * PAGE_SIZE));
 217
 218        fnic_trace_entries.page_offset = vmalloc(fnic_max_trace_entries *
 219                                                  sizeof(unsigned long));
 220        if (!fnic_trace_entries.page_offset) {
 221                printk(KERN_ERR PFX "Failed to allocate memory for"
 222                                  " page_offset\n");
 223                if (fnic_trace_buf_p) {
 224                        vfree((void *)fnic_trace_buf_p);
 225                        fnic_trace_buf_p = 0;
 226                }
 227                err = -ENOMEM;
 228                goto err_fnic_trace_buf_init;
 229        }
 230        memset((void *)fnic_trace_entries.page_offset, 0,
 231                  (fnic_max_trace_entries * sizeof(unsigned long)));
 232        fnic_trace_entries.wr_idx = fnic_trace_entries.rd_idx = 0;
 233        fnic_buf_head = fnic_trace_buf_p;
 234
 235        /*
 236         * Set page_offset field of fnic_trace_entries struct by
 237         * calculating memory location for every trace entry using
 238         * length of each trace entry
 239         */
 240        for (i = 0; i < fnic_max_trace_entries; i++) {
 241                fnic_trace_entries.page_offset[i] = fnic_buf_head;
 242                fnic_buf_head += FNIC_ENTRY_SIZE_BYTES;
 243        }
 244        err = fnic_trace_debugfs_init();
 245        if (err < 0) {
 246                printk(KERN_ERR PFX "Failed to initialize debugfs for tracing\n");
 247                goto err_fnic_trace_debugfs_init;
 248        }
 249        printk(KERN_INFO PFX "Successfully Initialized Trace Buffer\n");
 250        return err;
 251err_fnic_trace_debugfs_init:
 252        fnic_trace_free();
 253err_fnic_trace_buf_init:
 254        return err;
 255}
 256
 257/*
 258 * fnic_trace_free - Free memory of fnic trace data structures.
 259 */
 260void fnic_trace_free(void)
 261{
 262        fnic_tracing_enabled = 0;
 263        fnic_trace_debugfs_terminate();
 264        if (fnic_trace_entries.page_offset) {
 265                vfree((void *)fnic_trace_entries.page_offset);
 266                fnic_trace_entries.page_offset = NULL;
 267        }
 268        if (fnic_trace_buf_p) {
 269                vfree((void *)fnic_trace_buf_p);
 270                fnic_trace_buf_p = 0;
 271        }
 272        printk(KERN_INFO PFX "Successfully Freed Trace Buffer\n");
 273}
 274