Loading drivers/platform/msm/gsi/gsi.c +42 −8 Original line number Original line Diff line number Diff line Loading @@ -315,7 +315,7 @@ static void gsi_incr_ring_rp(struct gsi_ring_ctx *ctx) ctx->rp_local = ctx->base; ctx->rp_local = ctx->base; } } static uint16_t gsi_find_idx_from_addr(struct gsi_ring_ctx *ctx, uint64_t addr) uint16_t gsi_find_idx_from_addr(struct gsi_ring_ctx *ctx, uint64_t addr) { { BUG_ON(addr < ctx->base || addr >= ctx->end); BUG_ON(addr < ctx->base || addr >= ctx->end); Loading Loading @@ -1538,10 +1538,12 @@ int gsi_alloc_channel(struct gsi_chan_props *props, unsigned long dev_hdl, spin_lock_init(&ctx->ring.slock); spin_lock_init(&ctx->ring.slock); gsi_init_chan_ring(props, &ctx->ring); gsi_init_chan_ring(props, &ctx->ring); if (!props->max_re_expected) ctx->props.max_re_expected = ctx->ring.max_num_elem; ctx->user_data = user_data; ctx->user_data = user_data; *chan_hdl = props->ch_id; *chan_hdl = props->ch_id; ctx->allocated = true; ctx->allocated = true; ctx->stats.dp.last_timestamp = jiffies_to_msecs(jiffies); atomic_inc(&gsi_ctx->num_chan); atomic_inc(&gsi_ctx->num_chan); return GSI_STATUS_SUCCESS; return GSI_STATUS_SUCCESS; Loading Loading @@ -1955,35 +1957,67 @@ int gsi_dealloc_channel(unsigned long chan_hdl) } } EXPORT_SYMBOL(gsi_dealloc_channel); EXPORT_SYMBOL(gsi_dealloc_channel); void gsi_update_ch_dp_stats(struct gsi_chan_ctx *ctx, uint16_t used) { unsigned long now = jiffies_to_msecs(jiffies); unsigned long elapsed; if (used == 0) { elapsed = now - ctx->stats.dp.last_timestamp; if (ctx->stats.dp.empty_time < elapsed) ctx->stats.dp.empty_time = elapsed; } if (used <= ctx->props.max_re_expected / 3) ++ctx->stats.dp.ch_below_lo; else if (used <= 2 * ctx->props.max_re_expected / 3) ++ctx->stats.dp.ch_below_hi; else ++ctx->stats.dp.ch_above_hi; ctx->stats.dp.last_timestamp = now; } static void __gsi_query_channel_free_re(struct gsi_chan_ctx *ctx, static void __gsi_query_channel_free_re(struct gsi_chan_ctx *ctx, uint16_t *num_free_re) uint16_t *num_free_re) { { uint16_t start; uint16_t start; uint16_t start_hw; uint16_t end; uint16_t end; uint64_t rp; uint64_t rp; uint64_t rp_hw; int ee = gsi_ctx->per.ee; int ee = gsi_ctx->per.ee; uint16_t used; uint16_t used; uint16_t used_hw; if (!ctx->evtr) { rp_hw = gsi_readl(gsi_ctx->base + rp = gsi_readl(gsi_ctx->base + GSI_EE_n_GSI_CH_k_CNTXT_4_OFFS(ctx->props.ch_id, ee)); GSI_EE_n_GSI_CH_k_CNTXT_4_OFFS(ctx->props.ch_id, ee)); rp |= ((uint64_t)gsi_readl(gsi_ctx->base + rp_hw |= ((uint64_t)gsi_readl(gsi_ctx->base + GSI_EE_n_GSI_CH_k_CNTXT_5_OFFS(ctx->props.ch_id, ee))) GSI_EE_n_GSI_CH_k_CNTXT_5_OFFS(ctx->props.ch_id, ee))) << 32; << 32; if (!ctx->evtr) { rp = rp_hw; ctx->ring.rp = rp; ctx->ring.rp = rp; } else { } else { rp = ctx->ring.rp_local; rp = ctx->ring.rp_local; } } start = gsi_find_idx_from_addr(&ctx->ring, rp); start = gsi_find_idx_from_addr(&ctx->ring, rp); start_hw = gsi_find_idx_from_addr(&ctx->ring, rp_hw); end = gsi_find_idx_from_addr(&ctx->ring, ctx->ring.wp_local); end = gsi_find_idx_from_addr(&ctx->ring, ctx->ring.wp_local); if (end >= start) if (end >= start) used = end - start; used = end - start; else else used = ctx->ring.max_num_elem + 1 - (end - start); used = ctx->ring.max_num_elem + 1 - (start - end); if (end >= start_hw) used_hw = end - start_hw; else used_hw = ctx->ring.max_num_elem + 1 - (start_hw - end); *num_free_re = ctx->ring.max_num_elem - used; *num_free_re = ctx->ring.max_num_elem - used; gsi_update_ch_dp_stats(ctx, used_hw); } } int gsi_query_channel_info(unsigned long chan_hdl, int gsi_query_channel_info(unsigned long chan_hdl, Loading drivers/platform/msm/gsi/gsi.h +16 −1 Original line number Original line Diff line number Diff line /* Copyright (c) 2015, The Linux Foundation. All rights reserved. /* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. * * * This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * it under the terms of the GNU General Public License version 2 and Loading Loading @@ -60,6 +60,14 @@ struct gsi_ring_ctx { uint64_t end; uint64_t end; }; }; struct gsi_chan_dp_stats { unsigned long ch_below_lo; unsigned long ch_below_hi; unsigned long ch_above_hi; unsigned long empty_time; unsigned long last_timestamp; }; struct gsi_chan_stats { struct gsi_chan_stats { unsigned long queued; unsigned long queued; unsigned long completed; unsigned long completed; Loading @@ -68,6 +76,7 @@ struct gsi_chan_stats { unsigned long invalid_tre_error; unsigned long invalid_tre_error; unsigned long poll_ok; unsigned long poll_ok; unsigned long poll_empty; unsigned long poll_empty; struct gsi_chan_dp_stats dp; }; }; struct gsi_chan_ctx { struct gsi_chan_ctx { Loading @@ -82,6 +91,8 @@ struct gsi_chan_ctx { atomic_t poll_mode; atomic_t poll_mode; union __packed gsi_channel_scratch scratch; union __packed gsi_channel_scratch scratch; struct gsi_chan_stats stats; struct gsi_chan_stats stats; bool enable_dp_stats; bool print_dp_stats; }; }; struct gsi_evt_stats { struct gsi_evt_stats { Loading Loading @@ -128,6 +139,8 @@ struct gsi_ctx { atomic_t num_chan; atomic_t num_chan; atomic_t num_evt_ring; atomic_t num_evt_ring; struct gsi_ee_scratch scratch; struct gsi_ee_scratch scratch; int num_ch_dp_stats; struct workqueue_struct *dp_stat_wq; }; }; enum gsi_re_type { enum gsi_re_type { Loading Loading @@ -203,5 +216,7 @@ enum gsi_evt_ch_cmd_opcode { extern struct gsi_ctx *gsi_ctx; extern struct gsi_ctx *gsi_ctx; void gsi_debugfs_init(void); void gsi_debugfs_init(void); uint16_t gsi_find_idx_from_addr(struct gsi_ring_ctx *ctx, uint64_t addr); void gsi_update_ch_dp_stats(struct gsi_chan_ctx *ctx, uint16_t used); #endif #endif drivers/platform/msm/gsi/gsi_dbg.c +422 −36 Original line number Original line Diff line number Diff line /* Copyright (c) 2015, The Linux Foundation. All rights reserved. /* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. * * * This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * it under the terms of the GNU General Public License version 2 and Loading @@ -24,15 +24,17 @@ pr_err("%s:%d " fmt, __func__, __LINE__, ## args) pr_err("%s:%d " fmt, __func__, __LINE__, ## args) #define TDBG(fmt, args...) \ #define TDBG(fmt, args...) \ pr_debug("%s:%d " fmt, __func__, __LINE__, ## args) pr_debug("%s:%d " fmt, __func__, __LINE__, ## args) #define PRT_STAT(fmt, args...) \ pr_err(fmt, ## args) static struct dentry *dent; static struct dentry *dent; static struct dentry *dfile_gsi_ev_dump; static struct dentry *dfile_gsi_ch_dump; static struct dentry *dfile_gsi_ee_dump; static struct dentry *dfile_gsi_map; static struct dentry *dfile_gsi_stats; static char dbg_buff[4096]; static char dbg_buff[4096]; static void gsi_wq_print_dp_stats(struct work_struct *work); static DECLARE_DELAYED_WORK(gsi_print_dp_stats_work, gsi_wq_print_dp_stats); static void gsi_wq_update_dp_stats(struct work_struct *work); static DECLARE_DELAYED_WORK(gsi_update_dp_stats_work, gsi_wq_update_dp_stats); static ssize_t gsi_dump_evt(struct file *file, static ssize_t gsi_dump_evt(struct file *file, const char __user *buf, size_t count, loff_t *ppos) const char __user *buf, size_t count, loff_t *ppos) { { Loading Loading @@ -354,35 +356,374 @@ static ssize_t gsi_dump_map(struct file *file, return count; return count; } } static ssize_t gsi_dump_stats(struct file *file, static void gsi_dump_ch_stats(struct gsi_chan_ctx *ctx) const char __user *buf, size_t count, loff_t *ppos) { { struct gsi_chan_ctx *ctx; if (!ctx->allocated) int i; return; for (i = 0; i < GSI_MAX_CHAN; i++) { ctx = &gsi_ctx->chan[i]; if (ctx->allocated) { PRT_STAT("CH%2d:\n", ctx->props.ch_id); TERR("CH%2d:\n", ctx->props.ch_id); PRT_STAT("queued=%lu compl=%lu\n", TERR("queued=%lu compl=%lu\n", ctx->stats.queued, ctx->stats.queued, ctx->stats.completed); ctx->stats.completed); TERR("cb->poll=%lu poll->cb=%lu\n", PRT_STAT("cb->poll=%lu poll->cb=%lu\n", ctx->stats.callback_to_poll, ctx->stats.callback_to_poll, ctx->stats.poll_to_callback); ctx->stats.poll_to_callback); TERR("invalid_tre_error=%lu\n", PRT_STAT("invalid_tre_error=%lu\n", ctx->stats.invalid_tre_error); ctx->stats.invalid_tre_error); TERR("poll_ok=%lu poll_empty=%lu\n", PRT_STAT("poll_ok=%lu poll_empty=%lu\n", ctx->stats.poll_ok, ctx->stats.poll_empty); ctx->stats.poll_ok, ctx->stats.poll_empty); if (ctx->evtr) if (ctx->evtr) TERR("compl_evt=%lu\n", PRT_STAT("compl_evt=%lu\n", ctx->evtr->stats.completed); ctx->evtr->stats.completed); PRT_STAT("ch_below_lo=%lu\n", ctx->stats.dp.ch_below_lo); PRT_STAT("ch_below_hi=%lu\n", ctx->stats.dp.ch_below_hi); PRT_STAT("ch_above_hi=%lu\n", ctx->stats.dp.ch_above_hi); PRT_STAT("time_empty=%lums\n", ctx->stats.dp.empty_time); PRT_STAT("\n"); } static ssize_t gsi_dump_stats(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { int ch_id; int min, max; if (sizeof(dbg_buff) < count + 1) goto error; if (copy_from_user(dbg_buff, buf, count)) goto error; dbg_buff[count] = '\0'; if (kstrtos32(dbg_buff, 0, &ch_id)) goto error; if (ch_id == -1) { min = 0; max = GSI_MAX_CHAN; } else if (ch_id < 0 || ch_id >= GSI_MAX_CHAN || !gsi_ctx->chan[ch_id].allocated) { goto error; } else { min = ch_id; max = ch_id + 1; } for (ch_id = min; ch_id < max; ch_id++) gsi_dump_ch_stats(&gsi_ctx->chan[ch_id]); return count; error: TERR("Usage: echo ch_id > stats. Use -1 for all\n"); return -EFAULT; } static int gsi_dbg_create_stats_wq(void) { gsi_ctx->dp_stat_wq = create_singlethread_workqueue("gsi_stat"); if (!gsi_ctx->dp_stat_wq) { TERR("failed create workqueue\n"); return -ENOMEM; } return 0; } static void gsi_dbg_destroy_stats_wq(void) { cancel_delayed_work_sync(&gsi_update_dp_stats_work); cancel_delayed_work_sync(&gsi_print_dp_stats_work); flush_workqueue(gsi_ctx->dp_stat_wq); destroy_workqueue(gsi_ctx->dp_stat_wq); gsi_ctx->dp_stat_wq = NULL; } static ssize_t gsi_enable_dp_stats(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { int ch_id; bool enable; int ret; if (sizeof(dbg_buff) < count + 1) goto error; if (copy_from_user(dbg_buff, buf, count)) goto error; dbg_buff[count] = '\0'; if (dbg_buff[0] != '+' && dbg_buff[0] != '-') goto error; enable = (dbg_buff[0] == '+'); if (kstrtos32(dbg_buff + 1, 0, &ch_id)) goto error; if (ch_id < 0 || ch_id >= GSI_MAX_CHAN || !gsi_ctx->chan[ch_id].allocated) { goto error; } if (gsi_ctx->chan[ch_id].props.prot == GSI_CHAN_PROT_GPI) { TERR("valid for non GPI channels only\n"); goto error; } if (gsi_ctx->chan[ch_id].enable_dp_stats == enable) { TERR("ch_%d: already enabled/disabled\n", ch_id); return -EFAULT; } gsi_ctx->chan[ch_id].enable_dp_stats = enable; if (enable) gsi_ctx->num_ch_dp_stats++; else gsi_ctx->num_ch_dp_stats--; if (enable) { if (gsi_ctx->num_ch_dp_stats == 1) { ret = gsi_dbg_create_stats_wq(); if (ret) return ret; } cancel_delayed_work_sync(&gsi_update_dp_stats_work); queue_delayed_work(gsi_ctx->dp_stat_wq, &gsi_update_dp_stats_work, msecs_to_jiffies(10)); } else if (!enable && gsi_ctx->num_ch_dp_stats == 0) { gsi_dbg_destroy_stats_wq(); } return count; error: TERR("Usage: echo [+-]ch_id > enable_dp_stats\n"); return -EFAULT; } static ssize_t gsi_set_max_elem_dp_stats(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { u32 ch_id; u32 max_elem; unsigned long missing; char *sptr, *token; if (sizeof(dbg_buff) < count + 1) goto error; missing = copy_from_user(dbg_buff, buf, count); if (missing) goto error; dbg_buff[count] = '\0'; sptr = dbg_buff; token = strsep(&sptr, " "); if (!token) { TERR("\n"); goto error; } if (kstrtou32(token, 0, &ch_id)) { TERR("\n"); goto error; } token = strsep(&sptr, " "); if (!token) { /* get */ if (kstrtou32(dbg_buff, 0, &ch_id)) goto error; if (ch_id >= GSI_MAX_CHAN) goto error; PRT_STAT("ch %d: max_re_expected=%d\n", ch_id, gsi_ctx->chan[ch_id].props.max_re_expected); return count; } if (kstrtou32(token, 0, &max_elem)) { TERR("\n"); TERR("\n"); goto error; } TDBG("ch_id=%u max_elem=%u\n", ch_id, max_elem); if (ch_id >= GSI_MAX_CHAN) { TERR("invalid chan id %u\n", ch_id); goto error; } gsi_ctx->chan[ch_id].props.max_re_expected = max_elem; return count; error: TERR("Usage: (set) echo <ch_id> <max_elem> > max_elem_dp_stats\n"); TERR("Usage: (get) echo <ch_id> > max_elem_dp_stats\n"); return -EFAULT; } static void gsi_wq_print_dp_stats(struct work_struct *work) { int ch_id; for (ch_id = 0; ch_id < GSI_MAX_CHAN; ch_id++) { if (gsi_ctx->chan[ch_id].print_dp_stats) gsi_dump_ch_stats(&gsi_ctx->chan[ch_id]); } queue_delayed_work(gsi_ctx->dp_stat_wq, &gsi_print_dp_stats_work, msecs_to_jiffies(1000)); } static void gsi_dbg_update_ch_dp_stats(struct gsi_chan_ctx *ctx) { uint16_t start_hw; uint16_t end_hw; uint64_t rp_hw; uint64_t wp_hw; int ee = gsi_ctx->per.ee; uint16_t used_hw; rp_hw = gsi_readl(gsi_ctx->base + GSI_EE_n_GSI_CH_k_CNTXT_4_OFFS(ctx->props.ch_id, ee)); rp_hw |= ((uint64_t)gsi_readl(gsi_ctx->base + GSI_EE_n_GSI_CH_k_CNTXT_5_OFFS(ctx->props.ch_id, ee))) << 32; wp_hw = gsi_readl(gsi_ctx->base + GSI_EE_n_GSI_CH_k_CNTXT_6_OFFS(ctx->props.ch_id, ee)); wp_hw |= ((uint64_t)gsi_readl(gsi_ctx->base + GSI_EE_n_GSI_CH_k_CNTXT_7_OFFS(ctx->props.ch_id, ee))) << 32; start_hw = gsi_find_idx_from_addr(&ctx->ring, rp_hw); end_hw = gsi_find_idx_from_addr(&ctx->ring, wp_hw); if (end_hw >= start_hw) used_hw = end_hw - start_hw; else used_hw = ctx->ring.max_num_elem + 1 - (start_hw - end_hw); TERR("ch %d used %d\n", ctx->props.ch_id, used_hw); gsi_update_ch_dp_stats(ctx, used_hw); } static void gsi_wq_update_dp_stats(struct work_struct *work) { int ch_id; for (ch_id = 0; ch_id < GSI_MAX_CHAN; ch_id++) { if (gsi_ctx->chan[ch_id].allocated && gsi_ctx->chan[ch_id].props.prot != GSI_CHAN_PROT_GPI && gsi_ctx->chan[ch_id].enable_dp_stats) gsi_dbg_update_ch_dp_stats(&gsi_ctx->chan[ch_id]); } queue_delayed_work(gsi_ctx->dp_stat_wq, &gsi_update_dp_stats_work, msecs_to_jiffies(10)); } static ssize_t gsi_rst_stats(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { int ch_id; int min, max; if (sizeof(dbg_buff) < count + 1) goto error; if (copy_from_user(dbg_buff, buf, count)) goto error; dbg_buff[count] = '\0'; if (kstrtos32(dbg_buff, 0, &ch_id)) goto error; if (ch_id == -1) { min = 0; max = GSI_MAX_CHAN; } else if (ch_id < 0 || ch_id >= GSI_MAX_CHAN || !gsi_ctx->chan[ch_id].allocated) { goto error; } min = ch_id; max = ch_id + 1; for (ch_id = min; ch_id < max; ch_id++) memset(&gsi_ctx->chan[ch_id].stats, 0, sizeof(gsi_ctx->chan[ch_id].stats)); return count; error: TERR("Usage: echo ch_id > rst_stats. Use -1 for all\n"); return -EFAULT; } static ssize_t gsi_print_dp_stats(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { int ch_id; bool enable; int ret; if (sizeof(dbg_buff) < count + 1) goto error; if (copy_from_user(dbg_buff, buf, count)) goto error; dbg_buff[count] = '\0'; if (dbg_buff[0] != '+' && dbg_buff[0] != '-') goto error; enable = (dbg_buff[0] == '+'); if (kstrtos32(dbg_buff + 1, 0, &ch_id)) goto error; if (ch_id < 0 || ch_id >= GSI_MAX_CHAN || !gsi_ctx->chan[ch_id].allocated) { goto error; } if (gsi_ctx->chan[ch_id].print_dp_stats == enable) { TERR("ch_%d: already enabled/disabled\n", ch_id); return -EFAULT; } } gsi_ctx->chan[ch_id].print_dp_stats = enable; if (enable) gsi_ctx->num_ch_dp_stats++; else gsi_ctx->num_ch_dp_stats--; if (enable) { if (gsi_ctx->num_ch_dp_stats == 1) { ret = gsi_dbg_create_stats_wq(); if (ret) return ret; } cancel_delayed_work_sync(&gsi_print_dp_stats_work); queue_delayed_work(gsi_ctx->dp_stat_wq, &gsi_print_dp_stats_work, msecs_to_jiffies(10)); } else if (!enable && gsi_ctx->num_ch_dp_stats == 0) { gsi_dbg_destroy_stats_wq(); } } return count; return count; error: TERR("Usage: echo [+-]ch_id > print_dp_stats\n"); return -EFAULT; } } const struct file_operations gsi_ev_dump_ops = { const struct file_operations gsi_ev_dump_ops = { Loading @@ -405,8 +746,25 @@ const struct file_operations gsi_stats_ops = { .write = gsi_dump_stats, .write = gsi_dump_stats, }; }; const struct file_operations gsi_enable_dp_stats_ops = { .write = gsi_enable_dp_stats, }; const struct file_operations gsi_max_elem_dp_stats_ops = { .write = gsi_set_max_elem_dp_stats, }; const struct file_operations gsi_rst_stats_ops = { .write = gsi_rst_stats, }; const struct file_operations gsi_print_dp_stats_ops = { .write = gsi_print_dp_stats, }; void gsi_debugfs_init(void) void gsi_debugfs_init(void) { { static struct dentry *dfile; const mode_t read_only_mode = S_IRUSR | S_IRGRP | S_IROTH; const mode_t read_only_mode = S_IRUSR | S_IRGRP | S_IROTH; const mode_t write_only_mode = S_IWUSR | S_IWGRP; const mode_t write_only_mode = S_IWUSR | S_IWGRP; Loading @@ -416,37 +774,65 @@ void gsi_debugfs_init(void) return; return; } } dfile_gsi_ev_dump = debugfs_create_file("ev_dump", write_only_mode, dfile = debugfs_create_file("ev_dump", write_only_mode, dent, 0, &gsi_ev_dump_ops); dent, 0, &gsi_ev_dump_ops); if (!dfile_gsi_ev_dump || IS_ERR(dfile_gsi_ev_dump)) { if (!dfile || IS_ERR(dfile)) { TERR("fail to create ev_dump file\n"); TERR("fail to create ev_dump file\n"); goto fail; goto fail; } } dfile_gsi_ch_dump = debugfs_create_file("ch_dump", write_only_mode, dfile = debugfs_create_file("ch_dump", write_only_mode, dent, 0, &gsi_ch_dump_ops); dent, 0, &gsi_ch_dump_ops); if (!dfile_gsi_ch_dump || IS_ERR(dfile_gsi_ch_dump)) { if (!dfile || IS_ERR(dfile)) { TERR("fail to create ch_dump file\n"); TERR("fail to create ch_dump file\n"); goto fail; goto fail; } } dfile_gsi_ee_dump = debugfs_create_file("ee_dump", read_only_mode, dent, dfile = debugfs_create_file("ee_dump", read_only_mode, dent, 0, &gsi_ee_dump_ops); 0, &gsi_ee_dump_ops); if (!dfile_gsi_ee_dump || IS_ERR(dfile_gsi_ee_dump)) { if (!dfile || IS_ERR(dfile)) { TERR("fail to create ee_dump file\n"); TERR("fail to create ee_dump file\n"); goto fail; goto fail; } } dfile_gsi_map = debugfs_create_file("map", read_only_mode, dent, dfile = debugfs_create_file("map", read_only_mode, dent, 0, &gsi_map_ops); 0, &gsi_map_ops); if (!dfile_gsi_map || IS_ERR(dfile_gsi_map)) { if (!dfile || IS_ERR(dfile)) { TERR("fail to create map file\n"); TERR("fail to create map file\n"); goto fail; goto fail; } } dfile_gsi_stats = debugfs_create_file("stats", read_only_mode, dent, dfile = debugfs_create_file("stats", write_only_mode, dent, 0, &gsi_stats_ops); 0, &gsi_stats_ops); if (!dfile_gsi_stats || IS_ERR(dfile_gsi_stats)) { if (!dfile || IS_ERR(dfile)) { TERR("fail to create stats file\n"); goto fail; } dfile = debugfs_create_file("enable_dp_stats", write_only_mode, dent, 0, &gsi_enable_dp_stats_ops); if (!dfile || IS_ERR(dfile)) { TERR("fail to create stats file\n"); goto fail; } dfile = debugfs_create_file("max_elem_dp_stats", write_only_mode, dent, 0, &gsi_max_elem_dp_stats_ops); if (!dfile || IS_ERR(dfile)) { TERR("fail to create stats file\n"); goto fail; } dfile = debugfs_create_file("rst_stats", write_only_mode, dent, 0, &gsi_rst_stats_ops); if (!dfile || IS_ERR(dfile)) { TERR("fail to create stats file\n"); goto fail; } dfile = debugfs_create_file("print_dp_stats", write_only_mode, dent, 0, &gsi_print_dp_stats_ops); if (!dfile || IS_ERR(dfile)) { TERR("fail to create stats file\n"); TERR("fail to create stats file\n"); goto fail; goto fail; } } Loading drivers/platform/msm/ipa/ipa_v3/ipa_dp.c +4 −2 Original line number Original line Diff line number Diff line Loading @@ -3570,10 +3570,12 @@ static int ipa_gsi_setup_channel(struct ipa3_ep_context *ep) memset(&gsi_channel_props, 0, sizeof(gsi_channel_props)); memset(&gsi_channel_props, 0, sizeof(gsi_channel_props)); gsi_channel_props.prot = GSI_CHAN_PROT_GPI; gsi_channel_props.prot = GSI_CHAN_PROT_GPI; if (IPA_CLIENT_IS_PROD(ep->client)) if (IPA_CLIENT_IS_PROD(ep->client)) { gsi_channel_props.dir = GSI_CHAN_DIR_TO_GSI; gsi_channel_props.dir = GSI_CHAN_DIR_TO_GSI; else } else { gsi_channel_props.dir = GSI_CHAN_DIR_FROM_GSI; gsi_channel_props.dir = GSI_CHAN_DIR_FROM_GSI; gsi_channel_props.max_re_expected = ep->sys->rx_pool_sz; } gsi_ep_info = ipa3_get_gsi_ep_info(ipa3_get_ep_mapping(ep->client)); gsi_ep_info = ipa3_get_gsi_ep_info(ipa3_get_ep_mapping(ep->client)); if (!gsi_ep_info) { if (!gsi_ep_info) { Loading include/linux/msm_gsi.h +5 −1 Original line number Original line Diff line number Diff line /* Copyright (c) 2015, The Linux Foundation. All rights reserved. /* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. * * * This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * it under the terms of the GNU General Public License version 2 and Loading Loading @@ -280,6 +280,9 @@ enum gsi_chan_use_db_eng { * @re_size: size of channel ring element * @re_size: size of channel ring element * @ring_len: length of ring in bytes (must be integral multiple of * @ring_len: length of ring in bytes (must be integral multiple of * re_size) * re_size) * @max_re_expected: maximal number of ring elements expected to be queued. * used for data path statistics gathering. if 0 provided * ring_len / re_size will be used. * @ring_base_addr: physical base address of ring. Address must be aligned to * @ring_base_addr: physical base address of ring. Address must be aligned to * ring_len rounded to power of two * ring_len rounded to power of two * @ring_base_vaddr: virtual base address of ring (set to NULL when not * @ring_base_vaddr: virtual base address of ring (set to NULL when not Loading Loading @@ -333,6 +336,7 @@ struct gsi_chan_props { unsigned long evt_ring_hdl; unsigned long evt_ring_hdl; enum gsi_chan_ring_elem_size re_size; enum gsi_chan_ring_elem_size re_size; uint16_t ring_len; uint16_t ring_len; uint16_t max_re_expected; uint64_t ring_base_addr; uint64_t ring_base_addr; void *ring_base_vaddr; void *ring_base_vaddr; enum gsi_chan_use_db_eng use_db_eng; enum gsi_chan_use_db_eng use_db_eng; Loading Loading
drivers/platform/msm/gsi/gsi.c +42 −8 Original line number Original line Diff line number Diff line Loading @@ -315,7 +315,7 @@ static void gsi_incr_ring_rp(struct gsi_ring_ctx *ctx) ctx->rp_local = ctx->base; ctx->rp_local = ctx->base; } } static uint16_t gsi_find_idx_from_addr(struct gsi_ring_ctx *ctx, uint64_t addr) uint16_t gsi_find_idx_from_addr(struct gsi_ring_ctx *ctx, uint64_t addr) { { BUG_ON(addr < ctx->base || addr >= ctx->end); BUG_ON(addr < ctx->base || addr >= ctx->end); Loading Loading @@ -1538,10 +1538,12 @@ int gsi_alloc_channel(struct gsi_chan_props *props, unsigned long dev_hdl, spin_lock_init(&ctx->ring.slock); spin_lock_init(&ctx->ring.slock); gsi_init_chan_ring(props, &ctx->ring); gsi_init_chan_ring(props, &ctx->ring); if (!props->max_re_expected) ctx->props.max_re_expected = ctx->ring.max_num_elem; ctx->user_data = user_data; ctx->user_data = user_data; *chan_hdl = props->ch_id; *chan_hdl = props->ch_id; ctx->allocated = true; ctx->allocated = true; ctx->stats.dp.last_timestamp = jiffies_to_msecs(jiffies); atomic_inc(&gsi_ctx->num_chan); atomic_inc(&gsi_ctx->num_chan); return GSI_STATUS_SUCCESS; return GSI_STATUS_SUCCESS; Loading Loading @@ -1955,35 +1957,67 @@ int gsi_dealloc_channel(unsigned long chan_hdl) } } EXPORT_SYMBOL(gsi_dealloc_channel); EXPORT_SYMBOL(gsi_dealloc_channel); void gsi_update_ch_dp_stats(struct gsi_chan_ctx *ctx, uint16_t used) { unsigned long now = jiffies_to_msecs(jiffies); unsigned long elapsed; if (used == 0) { elapsed = now - ctx->stats.dp.last_timestamp; if (ctx->stats.dp.empty_time < elapsed) ctx->stats.dp.empty_time = elapsed; } if (used <= ctx->props.max_re_expected / 3) ++ctx->stats.dp.ch_below_lo; else if (used <= 2 * ctx->props.max_re_expected / 3) ++ctx->stats.dp.ch_below_hi; else ++ctx->stats.dp.ch_above_hi; ctx->stats.dp.last_timestamp = now; } static void __gsi_query_channel_free_re(struct gsi_chan_ctx *ctx, static void __gsi_query_channel_free_re(struct gsi_chan_ctx *ctx, uint16_t *num_free_re) uint16_t *num_free_re) { { uint16_t start; uint16_t start; uint16_t start_hw; uint16_t end; uint16_t end; uint64_t rp; uint64_t rp; uint64_t rp_hw; int ee = gsi_ctx->per.ee; int ee = gsi_ctx->per.ee; uint16_t used; uint16_t used; uint16_t used_hw; if (!ctx->evtr) { rp_hw = gsi_readl(gsi_ctx->base + rp = gsi_readl(gsi_ctx->base + GSI_EE_n_GSI_CH_k_CNTXT_4_OFFS(ctx->props.ch_id, ee)); GSI_EE_n_GSI_CH_k_CNTXT_4_OFFS(ctx->props.ch_id, ee)); rp |= ((uint64_t)gsi_readl(gsi_ctx->base + rp_hw |= ((uint64_t)gsi_readl(gsi_ctx->base + GSI_EE_n_GSI_CH_k_CNTXT_5_OFFS(ctx->props.ch_id, ee))) GSI_EE_n_GSI_CH_k_CNTXT_5_OFFS(ctx->props.ch_id, ee))) << 32; << 32; if (!ctx->evtr) { rp = rp_hw; ctx->ring.rp = rp; ctx->ring.rp = rp; } else { } else { rp = ctx->ring.rp_local; rp = ctx->ring.rp_local; } } start = gsi_find_idx_from_addr(&ctx->ring, rp); start = gsi_find_idx_from_addr(&ctx->ring, rp); start_hw = gsi_find_idx_from_addr(&ctx->ring, rp_hw); end = gsi_find_idx_from_addr(&ctx->ring, ctx->ring.wp_local); end = gsi_find_idx_from_addr(&ctx->ring, ctx->ring.wp_local); if (end >= start) if (end >= start) used = end - start; used = end - start; else else used = ctx->ring.max_num_elem + 1 - (end - start); used = ctx->ring.max_num_elem + 1 - (start - end); if (end >= start_hw) used_hw = end - start_hw; else used_hw = ctx->ring.max_num_elem + 1 - (start_hw - end); *num_free_re = ctx->ring.max_num_elem - used; *num_free_re = ctx->ring.max_num_elem - used; gsi_update_ch_dp_stats(ctx, used_hw); } } int gsi_query_channel_info(unsigned long chan_hdl, int gsi_query_channel_info(unsigned long chan_hdl, Loading
drivers/platform/msm/gsi/gsi.h +16 −1 Original line number Original line Diff line number Diff line /* Copyright (c) 2015, The Linux Foundation. All rights reserved. /* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. * * * This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * it under the terms of the GNU General Public License version 2 and Loading Loading @@ -60,6 +60,14 @@ struct gsi_ring_ctx { uint64_t end; uint64_t end; }; }; struct gsi_chan_dp_stats { unsigned long ch_below_lo; unsigned long ch_below_hi; unsigned long ch_above_hi; unsigned long empty_time; unsigned long last_timestamp; }; struct gsi_chan_stats { struct gsi_chan_stats { unsigned long queued; unsigned long queued; unsigned long completed; unsigned long completed; Loading @@ -68,6 +76,7 @@ struct gsi_chan_stats { unsigned long invalid_tre_error; unsigned long invalid_tre_error; unsigned long poll_ok; unsigned long poll_ok; unsigned long poll_empty; unsigned long poll_empty; struct gsi_chan_dp_stats dp; }; }; struct gsi_chan_ctx { struct gsi_chan_ctx { Loading @@ -82,6 +91,8 @@ struct gsi_chan_ctx { atomic_t poll_mode; atomic_t poll_mode; union __packed gsi_channel_scratch scratch; union __packed gsi_channel_scratch scratch; struct gsi_chan_stats stats; struct gsi_chan_stats stats; bool enable_dp_stats; bool print_dp_stats; }; }; struct gsi_evt_stats { struct gsi_evt_stats { Loading Loading @@ -128,6 +139,8 @@ struct gsi_ctx { atomic_t num_chan; atomic_t num_chan; atomic_t num_evt_ring; atomic_t num_evt_ring; struct gsi_ee_scratch scratch; struct gsi_ee_scratch scratch; int num_ch_dp_stats; struct workqueue_struct *dp_stat_wq; }; }; enum gsi_re_type { enum gsi_re_type { Loading Loading @@ -203,5 +216,7 @@ enum gsi_evt_ch_cmd_opcode { extern struct gsi_ctx *gsi_ctx; extern struct gsi_ctx *gsi_ctx; void gsi_debugfs_init(void); void gsi_debugfs_init(void); uint16_t gsi_find_idx_from_addr(struct gsi_ring_ctx *ctx, uint64_t addr); void gsi_update_ch_dp_stats(struct gsi_chan_ctx *ctx, uint16_t used); #endif #endif
drivers/platform/msm/gsi/gsi_dbg.c +422 −36 Original line number Original line Diff line number Diff line /* Copyright (c) 2015, The Linux Foundation. All rights reserved. /* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. * * * This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * it under the terms of the GNU General Public License version 2 and Loading @@ -24,15 +24,17 @@ pr_err("%s:%d " fmt, __func__, __LINE__, ## args) pr_err("%s:%d " fmt, __func__, __LINE__, ## args) #define TDBG(fmt, args...) \ #define TDBG(fmt, args...) \ pr_debug("%s:%d " fmt, __func__, __LINE__, ## args) pr_debug("%s:%d " fmt, __func__, __LINE__, ## args) #define PRT_STAT(fmt, args...) \ pr_err(fmt, ## args) static struct dentry *dent; static struct dentry *dent; static struct dentry *dfile_gsi_ev_dump; static struct dentry *dfile_gsi_ch_dump; static struct dentry *dfile_gsi_ee_dump; static struct dentry *dfile_gsi_map; static struct dentry *dfile_gsi_stats; static char dbg_buff[4096]; static char dbg_buff[4096]; static void gsi_wq_print_dp_stats(struct work_struct *work); static DECLARE_DELAYED_WORK(gsi_print_dp_stats_work, gsi_wq_print_dp_stats); static void gsi_wq_update_dp_stats(struct work_struct *work); static DECLARE_DELAYED_WORK(gsi_update_dp_stats_work, gsi_wq_update_dp_stats); static ssize_t gsi_dump_evt(struct file *file, static ssize_t gsi_dump_evt(struct file *file, const char __user *buf, size_t count, loff_t *ppos) const char __user *buf, size_t count, loff_t *ppos) { { Loading Loading @@ -354,35 +356,374 @@ static ssize_t gsi_dump_map(struct file *file, return count; return count; } } static ssize_t gsi_dump_stats(struct file *file, static void gsi_dump_ch_stats(struct gsi_chan_ctx *ctx) const char __user *buf, size_t count, loff_t *ppos) { { struct gsi_chan_ctx *ctx; if (!ctx->allocated) int i; return; for (i = 0; i < GSI_MAX_CHAN; i++) { ctx = &gsi_ctx->chan[i]; if (ctx->allocated) { PRT_STAT("CH%2d:\n", ctx->props.ch_id); TERR("CH%2d:\n", ctx->props.ch_id); PRT_STAT("queued=%lu compl=%lu\n", TERR("queued=%lu compl=%lu\n", ctx->stats.queued, ctx->stats.queued, ctx->stats.completed); ctx->stats.completed); TERR("cb->poll=%lu poll->cb=%lu\n", PRT_STAT("cb->poll=%lu poll->cb=%lu\n", ctx->stats.callback_to_poll, ctx->stats.callback_to_poll, ctx->stats.poll_to_callback); ctx->stats.poll_to_callback); TERR("invalid_tre_error=%lu\n", PRT_STAT("invalid_tre_error=%lu\n", ctx->stats.invalid_tre_error); ctx->stats.invalid_tre_error); TERR("poll_ok=%lu poll_empty=%lu\n", PRT_STAT("poll_ok=%lu poll_empty=%lu\n", ctx->stats.poll_ok, ctx->stats.poll_empty); ctx->stats.poll_ok, ctx->stats.poll_empty); if (ctx->evtr) if (ctx->evtr) TERR("compl_evt=%lu\n", PRT_STAT("compl_evt=%lu\n", ctx->evtr->stats.completed); ctx->evtr->stats.completed); PRT_STAT("ch_below_lo=%lu\n", ctx->stats.dp.ch_below_lo); PRT_STAT("ch_below_hi=%lu\n", ctx->stats.dp.ch_below_hi); PRT_STAT("ch_above_hi=%lu\n", ctx->stats.dp.ch_above_hi); PRT_STAT("time_empty=%lums\n", ctx->stats.dp.empty_time); PRT_STAT("\n"); } static ssize_t gsi_dump_stats(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { int ch_id; int min, max; if (sizeof(dbg_buff) < count + 1) goto error; if (copy_from_user(dbg_buff, buf, count)) goto error; dbg_buff[count] = '\0'; if (kstrtos32(dbg_buff, 0, &ch_id)) goto error; if (ch_id == -1) { min = 0; max = GSI_MAX_CHAN; } else if (ch_id < 0 || ch_id >= GSI_MAX_CHAN || !gsi_ctx->chan[ch_id].allocated) { goto error; } else { min = ch_id; max = ch_id + 1; } for (ch_id = min; ch_id < max; ch_id++) gsi_dump_ch_stats(&gsi_ctx->chan[ch_id]); return count; error: TERR("Usage: echo ch_id > stats. Use -1 for all\n"); return -EFAULT; } static int gsi_dbg_create_stats_wq(void) { gsi_ctx->dp_stat_wq = create_singlethread_workqueue("gsi_stat"); if (!gsi_ctx->dp_stat_wq) { TERR("failed create workqueue\n"); return -ENOMEM; } return 0; } static void gsi_dbg_destroy_stats_wq(void) { cancel_delayed_work_sync(&gsi_update_dp_stats_work); cancel_delayed_work_sync(&gsi_print_dp_stats_work); flush_workqueue(gsi_ctx->dp_stat_wq); destroy_workqueue(gsi_ctx->dp_stat_wq); gsi_ctx->dp_stat_wq = NULL; } static ssize_t gsi_enable_dp_stats(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { int ch_id; bool enable; int ret; if (sizeof(dbg_buff) < count + 1) goto error; if (copy_from_user(dbg_buff, buf, count)) goto error; dbg_buff[count] = '\0'; if (dbg_buff[0] != '+' && dbg_buff[0] != '-') goto error; enable = (dbg_buff[0] == '+'); if (kstrtos32(dbg_buff + 1, 0, &ch_id)) goto error; if (ch_id < 0 || ch_id >= GSI_MAX_CHAN || !gsi_ctx->chan[ch_id].allocated) { goto error; } if (gsi_ctx->chan[ch_id].props.prot == GSI_CHAN_PROT_GPI) { TERR("valid for non GPI channels only\n"); goto error; } if (gsi_ctx->chan[ch_id].enable_dp_stats == enable) { TERR("ch_%d: already enabled/disabled\n", ch_id); return -EFAULT; } gsi_ctx->chan[ch_id].enable_dp_stats = enable; if (enable) gsi_ctx->num_ch_dp_stats++; else gsi_ctx->num_ch_dp_stats--; if (enable) { if (gsi_ctx->num_ch_dp_stats == 1) { ret = gsi_dbg_create_stats_wq(); if (ret) return ret; } cancel_delayed_work_sync(&gsi_update_dp_stats_work); queue_delayed_work(gsi_ctx->dp_stat_wq, &gsi_update_dp_stats_work, msecs_to_jiffies(10)); } else if (!enable && gsi_ctx->num_ch_dp_stats == 0) { gsi_dbg_destroy_stats_wq(); } return count; error: TERR("Usage: echo [+-]ch_id > enable_dp_stats\n"); return -EFAULT; } static ssize_t gsi_set_max_elem_dp_stats(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { u32 ch_id; u32 max_elem; unsigned long missing; char *sptr, *token; if (sizeof(dbg_buff) < count + 1) goto error; missing = copy_from_user(dbg_buff, buf, count); if (missing) goto error; dbg_buff[count] = '\0'; sptr = dbg_buff; token = strsep(&sptr, " "); if (!token) { TERR("\n"); goto error; } if (kstrtou32(token, 0, &ch_id)) { TERR("\n"); goto error; } token = strsep(&sptr, " "); if (!token) { /* get */ if (kstrtou32(dbg_buff, 0, &ch_id)) goto error; if (ch_id >= GSI_MAX_CHAN) goto error; PRT_STAT("ch %d: max_re_expected=%d\n", ch_id, gsi_ctx->chan[ch_id].props.max_re_expected); return count; } if (kstrtou32(token, 0, &max_elem)) { TERR("\n"); TERR("\n"); goto error; } TDBG("ch_id=%u max_elem=%u\n", ch_id, max_elem); if (ch_id >= GSI_MAX_CHAN) { TERR("invalid chan id %u\n", ch_id); goto error; } gsi_ctx->chan[ch_id].props.max_re_expected = max_elem; return count; error: TERR("Usage: (set) echo <ch_id> <max_elem> > max_elem_dp_stats\n"); TERR("Usage: (get) echo <ch_id> > max_elem_dp_stats\n"); return -EFAULT; } static void gsi_wq_print_dp_stats(struct work_struct *work) { int ch_id; for (ch_id = 0; ch_id < GSI_MAX_CHAN; ch_id++) { if (gsi_ctx->chan[ch_id].print_dp_stats) gsi_dump_ch_stats(&gsi_ctx->chan[ch_id]); } queue_delayed_work(gsi_ctx->dp_stat_wq, &gsi_print_dp_stats_work, msecs_to_jiffies(1000)); } static void gsi_dbg_update_ch_dp_stats(struct gsi_chan_ctx *ctx) { uint16_t start_hw; uint16_t end_hw; uint64_t rp_hw; uint64_t wp_hw; int ee = gsi_ctx->per.ee; uint16_t used_hw; rp_hw = gsi_readl(gsi_ctx->base + GSI_EE_n_GSI_CH_k_CNTXT_4_OFFS(ctx->props.ch_id, ee)); rp_hw |= ((uint64_t)gsi_readl(gsi_ctx->base + GSI_EE_n_GSI_CH_k_CNTXT_5_OFFS(ctx->props.ch_id, ee))) << 32; wp_hw = gsi_readl(gsi_ctx->base + GSI_EE_n_GSI_CH_k_CNTXT_6_OFFS(ctx->props.ch_id, ee)); wp_hw |= ((uint64_t)gsi_readl(gsi_ctx->base + GSI_EE_n_GSI_CH_k_CNTXT_7_OFFS(ctx->props.ch_id, ee))) << 32; start_hw = gsi_find_idx_from_addr(&ctx->ring, rp_hw); end_hw = gsi_find_idx_from_addr(&ctx->ring, wp_hw); if (end_hw >= start_hw) used_hw = end_hw - start_hw; else used_hw = ctx->ring.max_num_elem + 1 - (start_hw - end_hw); TERR("ch %d used %d\n", ctx->props.ch_id, used_hw); gsi_update_ch_dp_stats(ctx, used_hw); } static void gsi_wq_update_dp_stats(struct work_struct *work) { int ch_id; for (ch_id = 0; ch_id < GSI_MAX_CHAN; ch_id++) { if (gsi_ctx->chan[ch_id].allocated && gsi_ctx->chan[ch_id].props.prot != GSI_CHAN_PROT_GPI && gsi_ctx->chan[ch_id].enable_dp_stats) gsi_dbg_update_ch_dp_stats(&gsi_ctx->chan[ch_id]); } queue_delayed_work(gsi_ctx->dp_stat_wq, &gsi_update_dp_stats_work, msecs_to_jiffies(10)); } static ssize_t gsi_rst_stats(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { int ch_id; int min, max; if (sizeof(dbg_buff) < count + 1) goto error; if (copy_from_user(dbg_buff, buf, count)) goto error; dbg_buff[count] = '\0'; if (kstrtos32(dbg_buff, 0, &ch_id)) goto error; if (ch_id == -1) { min = 0; max = GSI_MAX_CHAN; } else if (ch_id < 0 || ch_id >= GSI_MAX_CHAN || !gsi_ctx->chan[ch_id].allocated) { goto error; } min = ch_id; max = ch_id + 1; for (ch_id = min; ch_id < max; ch_id++) memset(&gsi_ctx->chan[ch_id].stats, 0, sizeof(gsi_ctx->chan[ch_id].stats)); return count; error: TERR("Usage: echo ch_id > rst_stats. Use -1 for all\n"); return -EFAULT; } static ssize_t gsi_print_dp_stats(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { int ch_id; bool enable; int ret; if (sizeof(dbg_buff) < count + 1) goto error; if (copy_from_user(dbg_buff, buf, count)) goto error; dbg_buff[count] = '\0'; if (dbg_buff[0] != '+' && dbg_buff[0] != '-') goto error; enable = (dbg_buff[0] == '+'); if (kstrtos32(dbg_buff + 1, 0, &ch_id)) goto error; if (ch_id < 0 || ch_id >= GSI_MAX_CHAN || !gsi_ctx->chan[ch_id].allocated) { goto error; } if (gsi_ctx->chan[ch_id].print_dp_stats == enable) { TERR("ch_%d: already enabled/disabled\n", ch_id); return -EFAULT; } } gsi_ctx->chan[ch_id].print_dp_stats = enable; if (enable) gsi_ctx->num_ch_dp_stats++; else gsi_ctx->num_ch_dp_stats--; if (enable) { if (gsi_ctx->num_ch_dp_stats == 1) { ret = gsi_dbg_create_stats_wq(); if (ret) return ret; } cancel_delayed_work_sync(&gsi_print_dp_stats_work); queue_delayed_work(gsi_ctx->dp_stat_wq, &gsi_print_dp_stats_work, msecs_to_jiffies(10)); } else if (!enable && gsi_ctx->num_ch_dp_stats == 0) { gsi_dbg_destroy_stats_wq(); } } return count; return count; error: TERR("Usage: echo [+-]ch_id > print_dp_stats\n"); return -EFAULT; } } const struct file_operations gsi_ev_dump_ops = { const struct file_operations gsi_ev_dump_ops = { Loading @@ -405,8 +746,25 @@ const struct file_operations gsi_stats_ops = { .write = gsi_dump_stats, .write = gsi_dump_stats, }; }; const struct file_operations gsi_enable_dp_stats_ops = { .write = gsi_enable_dp_stats, }; const struct file_operations gsi_max_elem_dp_stats_ops = { .write = gsi_set_max_elem_dp_stats, }; const struct file_operations gsi_rst_stats_ops = { .write = gsi_rst_stats, }; const struct file_operations gsi_print_dp_stats_ops = { .write = gsi_print_dp_stats, }; void gsi_debugfs_init(void) void gsi_debugfs_init(void) { { static struct dentry *dfile; const mode_t read_only_mode = S_IRUSR | S_IRGRP | S_IROTH; const mode_t read_only_mode = S_IRUSR | S_IRGRP | S_IROTH; const mode_t write_only_mode = S_IWUSR | S_IWGRP; const mode_t write_only_mode = S_IWUSR | S_IWGRP; Loading @@ -416,37 +774,65 @@ void gsi_debugfs_init(void) return; return; } } dfile_gsi_ev_dump = debugfs_create_file("ev_dump", write_only_mode, dfile = debugfs_create_file("ev_dump", write_only_mode, dent, 0, &gsi_ev_dump_ops); dent, 0, &gsi_ev_dump_ops); if (!dfile_gsi_ev_dump || IS_ERR(dfile_gsi_ev_dump)) { if (!dfile || IS_ERR(dfile)) { TERR("fail to create ev_dump file\n"); TERR("fail to create ev_dump file\n"); goto fail; goto fail; } } dfile_gsi_ch_dump = debugfs_create_file("ch_dump", write_only_mode, dfile = debugfs_create_file("ch_dump", write_only_mode, dent, 0, &gsi_ch_dump_ops); dent, 0, &gsi_ch_dump_ops); if (!dfile_gsi_ch_dump || IS_ERR(dfile_gsi_ch_dump)) { if (!dfile || IS_ERR(dfile)) { TERR("fail to create ch_dump file\n"); TERR("fail to create ch_dump file\n"); goto fail; goto fail; } } dfile_gsi_ee_dump = debugfs_create_file("ee_dump", read_only_mode, dent, dfile = debugfs_create_file("ee_dump", read_only_mode, dent, 0, &gsi_ee_dump_ops); 0, &gsi_ee_dump_ops); if (!dfile_gsi_ee_dump || IS_ERR(dfile_gsi_ee_dump)) { if (!dfile || IS_ERR(dfile)) { TERR("fail to create ee_dump file\n"); TERR("fail to create ee_dump file\n"); goto fail; goto fail; } } dfile_gsi_map = debugfs_create_file("map", read_only_mode, dent, dfile = debugfs_create_file("map", read_only_mode, dent, 0, &gsi_map_ops); 0, &gsi_map_ops); if (!dfile_gsi_map || IS_ERR(dfile_gsi_map)) { if (!dfile || IS_ERR(dfile)) { TERR("fail to create map file\n"); TERR("fail to create map file\n"); goto fail; goto fail; } } dfile_gsi_stats = debugfs_create_file("stats", read_only_mode, dent, dfile = debugfs_create_file("stats", write_only_mode, dent, 0, &gsi_stats_ops); 0, &gsi_stats_ops); if (!dfile_gsi_stats || IS_ERR(dfile_gsi_stats)) { if (!dfile || IS_ERR(dfile)) { TERR("fail to create stats file\n"); goto fail; } dfile = debugfs_create_file("enable_dp_stats", write_only_mode, dent, 0, &gsi_enable_dp_stats_ops); if (!dfile || IS_ERR(dfile)) { TERR("fail to create stats file\n"); goto fail; } dfile = debugfs_create_file("max_elem_dp_stats", write_only_mode, dent, 0, &gsi_max_elem_dp_stats_ops); if (!dfile || IS_ERR(dfile)) { TERR("fail to create stats file\n"); goto fail; } dfile = debugfs_create_file("rst_stats", write_only_mode, dent, 0, &gsi_rst_stats_ops); if (!dfile || IS_ERR(dfile)) { TERR("fail to create stats file\n"); goto fail; } dfile = debugfs_create_file("print_dp_stats", write_only_mode, dent, 0, &gsi_print_dp_stats_ops); if (!dfile || IS_ERR(dfile)) { TERR("fail to create stats file\n"); TERR("fail to create stats file\n"); goto fail; goto fail; } } Loading
drivers/platform/msm/ipa/ipa_v3/ipa_dp.c +4 −2 Original line number Original line Diff line number Diff line Loading @@ -3570,10 +3570,12 @@ static int ipa_gsi_setup_channel(struct ipa3_ep_context *ep) memset(&gsi_channel_props, 0, sizeof(gsi_channel_props)); memset(&gsi_channel_props, 0, sizeof(gsi_channel_props)); gsi_channel_props.prot = GSI_CHAN_PROT_GPI; gsi_channel_props.prot = GSI_CHAN_PROT_GPI; if (IPA_CLIENT_IS_PROD(ep->client)) if (IPA_CLIENT_IS_PROD(ep->client)) { gsi_channel_props.dir = GSI_CHAN_DIR_TO_GSI; gsi_channel_props.dir = GSI_CHAN_DIR_TO_GSI; else } else { gsi_channel_props.dir = GSI_CHAN_DIR_FROM_GSI; gsi_channel_props.dir = GSI_CHAN_DIR_FROM_GSI; gsi_channel_props.max_re_expected = ep->sys->rx_pool_sz; } gsi_ep_info = ipa3_get_gsi_ep_info(ipa3_get_ep_mapping(ep->client)); gsi_ep_info = ipa3_get_gsi_ep_info(ipa3_get_ep_mapping(ep->client)); if (!gsi_ep_info) { if (!gsi_ep_info) { Loading
include/linux/msm_gsi.h +5 −1 Original line number Original line Diff line number Diff line /* Copyright (c) 2015, The Linux Foundation. All rights reserved. /* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. * * * This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * it under the terms of the GNU General Public License version 2 and Loading Loading @@ -280,6 +280,9 @@ enum gsi_chan_use_db_eng { * @re_size: size of channel ring element * @re_size: size of channel ring element * @ring_len: length of ring in bytes (must be integral multiple of * @ring_len: length of ring in bytes (must be integral multiple of * re_size) * re_size) * @max_re_expected: maximal number of ring elements expected to be queued. * used for data path statistics gathering. if 0 provided * ring_len / re_size will be used. * @ring_base_addr: physical base address of ring. Address must be aligned to * @ring_base_addr: physical base address of ring. Address must be aligned to * ring_len rounded to power of two * ring_len rounded to power of two * @ring_base_vaddr: virtual base address of ring (set to NULL when not * @ring_base_vaddr: virtual base address of ring (set to NULL when not Loading Loading @@ -333,6 +336,7 @@ struct gsi_chan_props { unsigned long evt_ring_hdl; unsigned long evt_ring_hdl; enum gsi_chan_ring_elem_size re_size; enum gsi_chan_ring_elem_size re_size; uint16_t ring_len; uint16_t ring_len; uint16_t max_re_expected; uint64_t ring_base_addr; uint64_t ring_base_addr; void *ring_base_vaddr; void *ring_base_vaddr; enum gsi_chan_use_db_eng use_db_eng; enum gsi_chan_use_db_eng use_db_eng; Loading