// SPDX-License-Identifier: GPL-2.0 /* Copyright (C) Intel Corporation * Author: Shao Guohua */ #include #include /*for proc api */ #include #include #include #include #include #include #include "datapath.h" #include "datapath_instance.h" #include "datapath_swdev_api.h" /*meter alloc,add macros*/ #define DP_METER_ALLOC(inst, id, flag) \ dp_port_prop[(inst)].info.dp_meter_alloc(inst, &(id), (flag)) #define DP_METER_CFGAPI(inst, func, dev, meter, flag, mtr_subif) \ dp_port_prop[(inst)].info.func((dev), &(meter), (flag), (mtr_subif)) #define DP_DBGFS_NAME "dp" #define DBGFS_DBG "dbg" #define DBGFS_PORT "port" #define DBGFS_INST_DEV "inst_dev" #define DBGFS_INST_MOD "inst_mod" #define DBGFS_INST_HAL "inst_hal" #define DBGFS_INST "inst" #define DBGFS_QOS "qos" #define DBGFS_EVENT "event" #define DBGFS_MIB_GLOBAL "mib_global_stats" #define DBGFS_BR_VLAN "vlan" #define DBGFS_PCE "pce" #define DEBUGFS_DBG DP_DEBUGFS_PATH "/" DBGFS_DBG typedef int (*print_ctp_bp_t)(struct seq_file *s, int inst, struct pmac_port_info *port, int subif_index, u32 flag); static int tmp_inst; #if defined(CONFIG_INTEL_DATAPATH_DBG) && CONFIG_INTEL_DATAPATH_DBG static void proc_dbg_read(struct seq_file *s); static ssize_t proc_dbg_write(struct file *, const char *, size_t, loff_t *); #endif #define PROC_WRITE_PORT_DEF -1 static int proc_write_port = PROC_WRITE_PORT_DEF; static int proc_write_vap = PROC_WRITE_PORT_DEF; static bool dump_one_port = false; static bool dump_one_vap = false; static int proc_port_dump(struct seq_file *s, int pos); static int proc_port_init(void); static ssize_t proc_port_write(struct file *file, const char *buf, size_t count, loff_t *ppos); int proc_dp_event_list_dump(struct seq_file *s, int pos); #if !IS_ENABLED(CONFIG_INTEL_DATAPATH_HAL_GSWIP30) static u32 br_hash_index; static struct br_info *brdev_info; static int proc_brvlan_start(void) { br_hash_index = 0; brdev_info = hlist_entry_safe((get_dp_g_bridge_id_entry_hash_table_info( br_hash_index)) ->first, struct br_info, br_hlist); return 0; } static int proc_brvlan_dump(struct seq_file *s, int pos) { struct bridge_member_port *tmp = NULL; struct vlist_entry *vlist_entry = NULL; int bp_entry = 0, vlan_entry = 0, i = 0; while (!brdev_info) { br_hash_index++; pos = 0; if (br_hash_index == BR_ID_ENTRY_HASH_TABLE_SIZE) { seq_puts(s, "end\n"); return -1; } brdev_info = hlist_entry_safe((get_dp_g_bridge_id_entry_hash_table_info( br_hash_index)) ->first, struct br_info, br_hlist); } if (!brdev_info->br_vlan_en) goto EXIT; if (pos == 0) { seq_printf(s, "%-10s%-10s%-30s%-10s%-30s\n", "Br", "BrFid", "BrVlanID (EntryPtr)", "Bp", "Bp (VlanID/FiD/RefCnt/ EntryPtr)"); for (i = 0; i < 90; i++) seq_printf(s, "="); seq_printf(s, "\n"); } seq_printf(s, "%-10s", brdev_info->dev->name); seq_printf(s, "%-10d", brdev_info->fid); bp_entry = 0; list_for_each_entry (vlist_entry, &brdev_info->br_vlan_list, list) { if (!vlist_entry->vlan_entry) continue; if (bp_entry) { seq_printf(s, "\n"); seq_printf(s, "%-10s%-10s", "", ""); } seq_printf(s, "%-4d %-25x", vlist_entry->vlan_entry->vlan_id, (u32)vlist_entry->vlan_entry); bp_entry = 1; } if (bp_entry == 0) seq_printf(s, "%-30s", ""); bp_entry = 0; list_for_each_entry (tmp, &brdev_info->bp_list, list) { if (bp_entry) { seq_printf(s, "\n\n"); seq_printf(s, "%-10s%-10s%-30s", "", "", ""); } seq_printf(s, "%-10d", tmp->bportid); bp_entry = 1; vlan_entry = 0; list_for_each_entry (vlist_entry, &tmp->bport_vlan_list, list) { if (!vlist_entry->vlan_entry) continue; if (vlan_entry) { seq_printf(s, "\n"); seq_printf(s, "%-10s%-10s%-30s%-10s", "", "", "", ""); } seq_printf(s, "%-d/%-d/%-d/ %-x", vlist_entry->vlan_entry->vlan_id, vlist_entry->vlan_entry->fid, vlist_entry->vlan_entry->ref_cnt, (u32)vlist_entry->vlan_entry); vlan_entry = 1; } } seq_puts(s, "\n"); EXIT: brdev_info = hlist_entry_safe((brdev_info)->br_hlist.next, struct br_info, br_hlist); if (!seq_has_overflowed(s)) pos++; return pos; } #endif static int proc_dp_mib_global_stats(struct seq_file *s, int pos) { struct mib_global_stats stats = { 0 }; int cpu; if (!capable(CAP_SYS_PACCT)) return -1; for_each_online_cpu (cpu) { stats.rx_rxif_pkts += MIB_G_STATS_GET(rx_rxif_pkts, cpu); stats.rx_txif_pkts += MIB_G_STATS_GET(rx_txif_pkts, cpu); stats.rx_rxif_clone += MIB_G_STATS_GET(rx_rxif_clone, cpu); stats.rx_drop += MIB_G_STATS_GET(rx_drop, cpu); stats.tx_pkts += MIB_G_STATS_GET(tx_pkts, cpu); stats.tx_drop += MIB_G_STATS_GET(tx_drop, cpu); } seq_printf(s, "MIB Global Rx/Tx counters\n"); seq_printf(s, "rx_rxif_pkts = %llu\n", stats.rx_rxif_pkts); seq_printf(s, "rx_txif_pkts = %llu\n", stats.rx_txif_pkts); seq_printf(s, "rx_rxif_clone = %llu\n", stats.rx_rxif_clone); seq_printf(s, "rx_drop = %llu\n", stats.rx_drop); seq_printf(s, "tx_pkts = %llu\n", stats.tx_pkts); seq_printf(s, "tx_drop = %llu\n", stats.tx_drop); return -1; } ssize_t proc_dp_mib_g_stats_write(struct file *file, const char *buf, size_t count, loff_t *ppos) { int len, cpu; char str[2]; if (!capable(CAP_SYS_ADMIN)) return -EPERM; /* only clear action is supported i.e. '0' */ if (count > 2) goto usage; len = count; len -= copy_from_user(str, buf, count); str[len] = 0; if (str[0] != '0') goto usage; for_each_online_cpu (cpu) { MIB_G_STATS_RESET(rx_rxif_pkts, cpu); MIB_G_STATS_RESET(rx_txif_pkts, cpu); MIB_G_STATS_RESET(rx_rxif_clone, cpu); MIB_G_STATS_RESET(rx_drop, cpu); MIB_G_STATS_RESET(tx_pkts, cpu); MIB_G_STATS_RESET(tx_drop, cpu); } return count; usage: pr_info("usage: to clear stats\n"); pr_info(" echo 0 > %s\n", DBGFS_MIB_GLOBAL); return count; } int proc_port_init(void) { tmp_inst = 0; dump_one_port = false; dump_one_vap = false; /* check if we have been requsted to dump only specific port */ if (proc_write_port != PROC_WRITE_PORT_DEF) { dump_one_port = true; if (proc_write_vap != PROC_WRITE_PORT_DEF) dump_one_vap = true; return proc_write_port; /* change to requested port id */ } return 0; } int dump_dc_info(struct seq_file *s, struct pmac_port_info *port) { u8 cid, pid; int i = 0; u16 nid; for (i = 0; i < port->num_tx_ring; i++) { seq_printf(s, " DC TxRing: %d\n", i); seq_printf(s, " TXIN DeqRingSize/paddr: %d/0x%p\n", port->tx_ring[i].in_deq_ring_size, port->tx_ring[i].in_deq_paddr); seq_printf(s, " TXOUT FreeRingSize/paddr: %d/0x%p\n", port->tx_ring[i].out_free_ring_size, port->tx_ring[i].out_free_paddr); seq_printf(s, " TXOUT PolicyBase/Poolid: %d/%d\n", port->tx_ring[i].txout_policy_base, port->tx_ring[i].tx_poolid); seq_printf(s, " PolicyNum: %d\n", port->tx_ring[i].policy_num); seq_printf(s, " NumOfTxPkt/TxPktSize: %d/%d\n", port->tx_ring[i].num_tx_pkt, port->tx_ring[i].tx_pkt_size); } for (i = 0; i < port->num_rx_ring; i++) { seq_printf(s, " DC RxRing: %d\n", i); seq_printf(s, " RXOUT EnqRingSize/paddr/pid: %d/0x%p/%d\n", port->rx_ring[i].out_enq_ring_size, port->rx_ring[i].out_enq_paddr, port->rx_ring[i].out_enq_port_id); seq_printf(s, " RXOUT NumOfDmaCh: %d\n", port->rx_ring[i].num_out_tx_dma_ch); dp_dma_parse_id(port->rx_ring[i].out_dma_ch_to_gswip, &cid, &pid, &nid); seq_printf(s, " RXOUT dma-ctrl/port/chan: %d/%d/%d\n", cid, pid, nid); seq_printf(s, " RXOUT NumOfCqmDqPort/pid: %d/%d\n", port->rx_ring[i].num_out_cqm_deq_port, port->rx_ring[i].out_cqm_deq_port_id); seq_printf(s, " RXIN InAllocRingSize/Paddr: %d/0x%p\n", port->rx_ring[i].in_alloc_ring_size, port->rx_ring[i].in_alloc_paddr); seq_printf(s, " NumPkt/Pktsize/Policybase: %d/%d/%d\n", port->rx_ring[i].num_pkt, port->rx_ring[i].rx_pkt_size, port->rx_ring[i].rx_policy_base); seq_printf(s, " PreFillPktNum/PktBase: %d/0x%p\n", port->rx_ring[i].prefill_pkt_num, port->rx_ring[i].pkt_base_paddr); } #if !IS_ENABLED(CONFIG_INTEL_DATAPATH_HAL_GSWIP30) seq_printf(s, " UMT id/CqmDeqPid/msg_mode: %d/%d/%d\n", port->umt.ctl.id, port->umt.res.cqm_dq_pid, port->umt.ctl.msg_mode); seq_printf(s, " UMT period/daddr: %d/0x%08x\n", port->umt.ctl.msg_interval, port->umt.ctl.daddr); #endif return 0; } void dump_subif_info(struct seq_file *s, struct pmac_port_info *port, int n) { struct dp_subif_info *sif = get_dp_port_subif(port, n); struct dev_mib *mib = get_dp_port_subif_mib(sif); print_ctp_bp_t print_ctp_bp = DP_CB(tmp_inst, proc_print_ctp_bp_info); struct dma_chan_info *dma; struct cqm_port_info *cqm; int i, cqm_p; u8 cid, pid; u16 nid; if (!sif->flags) return; seq_printf(s, " [%02d]:%s=0x%04x %s=0x%0lx(%s=%s),%s=%s\n", n, "subif", sif->subif, "netif", (uintptr_t)sif->netif, "netif", sif->netif ? sif->netif->name : "NULL/DSL", "device_name", sif->device_name); seq_printf(s, " subif_flag: "); for (i = 0; i < get_dp_port_type_str_size(); i++) { if (sif->subif_flag & dp_port_flag[i]) { seq_puts(s, dp_port_type_str[i]); seq_puts(s, " "); } } seq_printf(s, "\n"); seq_printf(s, " data_flag_ops =0x%08x\n", sif->data_flag_ops); seq_printf(s, " rx_fn_rxif_pkt =0x%08x\n", STATS_GET(mib->rx_fn_rxif_pkt)); seq_printf(s, " rx_fn_txif_pkt =0x%08x\n", STATS_GET(mib->rx_fn_txif_pkt)); seq_printf(s, " rx_fn_dropped =0x%08x\n", STATS_GET(mib->rx_fn_dropped)); seq_printf(s, " tx_cbm_pkt =0x%08x\n", STATS_GET(mib->tx_cbm_pkt)); seq_printf(s, " tx_tso_pkt =0x%08x\n", STATS_GET(mib->tx_tso_pkt)); seq_printf(s, " tx_pkt_dropped =0x%08x\n", STATS_GET(mib->tx_pkt_dropped)); seq_printf(s, " tx_clone_pkt =0x%08x\n", STATS_GET(mib->tx_clone_pkt)); seq_printf(s, " tx_hdr_room_pkt=0x%08x\n", STATS_GET(mib->tx_hdr_room_pkt)); #if IS_ENABLED(CONFIG_INTEL_DATAPATH_SWITCHDEV) seq_printf(s, " swdev: %d\n", sif->swdev_en); #endif seq_printf(s, " subif_groupid: %d\n", sif->subif_groupid); if (print_ctp_bp) print_ctp_bp(s, tmp_inst, port, n, 0); seq_printf(s, " subif_qid: %d\n", sif->num_qid); seq_printf(s, " dqport_idx: %d\n", sif->cqm_port_idx); for (i = 0; i < sif->num_qid; i++) { seq_printf(s, "%13s[%02d]:qid/node: %d/%d\n", "", i, sif->qid_list[i], sif->q_node[i]); cqm_p = sif->cqm_deq_port[i]; cqm = get_dp_deqport_info(tmp_inst, cqm_p); seq_printf(s, "%18sport/node: %d/%d(ref=%d)\n", "", cqm_p, sif->qos_deq_port[i], cqm->ref_cnt); dp_dma_parse_id(cqm->dma_chan, &cid, &pid, &nid); dma = dp_dma_chan_tbl[tmp_inst]; if (port->num_dma_chan && dma) { dma += cqm->dma_ch_offset; seq_printf(s, "%18stx_dma_ch: 0x%x(ref=%d,dma-ctrl=%d,port=%d,channel=%d)\n", "", cqm->dma_chan, atomic_read(&dma->ref_cnt), cid, pid, nid); } } seq_printf(s, " mac_learn_dis: %d\n", sif->mac_learn_dis); seq_printf(s, " gpid: %d\n", sif->gpid); seq_printf(s, " cpu_port_en: %d (%s)\n", sif->cpu_port_en, sif->cpu_port_en ? "Enabled" : "Disabled"); seq_printf(s, " ctp_dev: "); if (sif->ctp_dev && sif->ctp_dev->name) seq_puts(s, sif->ctp_dev->name); else seq_puts(s, "NULL"); seq_printf(s, "\n"); seq_printf(s, " rx_en_flag: %d\n", STATS_GET(sif->rx_flag)); seq_printf(s, " tx_policy: %d/%d\n", sif->policy_base, sif->policy_num); } int proc_port_dump(struct seq_file *s, int pos) { int (*print_ctp_bp)(struct seq_file * s, int inst, struct pmac_port_info *port, int subif_index, u32 flag); struct dp_subif_info *sif; struct pmac_port_info *port; u16 start = 0; u8 cid, pid; u16 nid; int i, start_vap, end_vap; struct inst_info *info = NULL; struct cqm_port_info *cqm_info; if (!capable(CAP_SYS_PACCT)) return -1; port = get_dp_port_info(tmp_inst, pos); if (!port) { pr_err("Why port is NULL\n"); return -1; } info = &dp_port_prop[tmp_inst].info; print_ctp_bp = DP_CB(tmp_inst, proc_print_ctp_bp_info); DP_CB(tmp_inst, get_itf_start_end) (port->itf_info, &start, NULL); if (port->status == PORT_FREE) { if (pos == 0) { sif = get_dp_port_subif(port, 0); seq_printf(s, "Reserved Port: %s=0x%08x %s=0x%08x\n", "rx_err_drop", STATS_GET(port->rx_err_drop), "tx_err_drop", STATS_GET(port->tx_err_drop)); seq_printf(s, " qid/node: %d/%d\n", sif->qid, sif->q_node[0]); seq_printf(s, " port/node: %d/%d\n", sif->cqm_deq_port[0], sif->qos_deq_port[0]); } else { seq_printf(s, "%02d: %s=0x%08x %s=0x%08x\n", pos, "rx_err_drop", STATS_GET(port->rx_err_drop), "tx_err_drop", STATS_GET(port->tx_err_drop)); } goto EXIT; } seq_printf(s, "%02d:%s=0x%0lx(%s:%8s) %s=%02d %s=%02d %s=%d(%s) %s=%d\n", pos, "module", (uintptr_t)port->owner, "name", port->owner->name, "dev_port", port->dev_port, "dp_port", port->port_id, "itf_base", start, port->itf_info ? "Enabled" : "Not Enabled", "ctp_max", port->ctp_max); seq_printf(s, " status: %s\n", dp_port_status_str[port->status]); seq_puts(s, " allocate_flags: "); for (i = 0; i < get_dp_port_type_str_size(); i++) { if (port->alloc_flags & dp_port_flag[i]) seq_printf(s, "%s ", dp_port_type_str[i]); } seq_puts(s, "\n"); seq_printf(s, " mode: %d\n", port->cqe_lu_mode); seq_printf(s, " LCT: %d\n", port->lct_idx); seq_printf(s, " subif_max: %d\n", port->subif_max); #if IS_ENABLED(CONFIG_INTEL_DATAPATH_SWITCHDEV) seq_printf(s, " p_swdev: %d\n", port->swdev_en); #endif seq_printf(s, " cb->rx_fn: %pF\n", port->cb.rx_fn); seq_printf(s, " cb->restart_fn: %pF\n", port->cb.restart_fn); seq_printf(s, " cb->stop_fn: %pF\n", port->cb.stop_fn); seq_printf(s, " cb->get_subifid_fn:%pF\n", port->cb.get_subifid_fn); seq_printf(s, " num_subif: %d\n", port->num_subif); seq_printf(s, " vap_offset/mask: %d/0x%x\n", port->vap_offset, port->vap_mask); seq_printf(s, " flag_other: 0x%x\n", port->flag_other); seq_printf(s, " resv_queue: %d\n", port->num_resv_q); seq_printf(s, " resv_queue_base: %d\n", port->res_qid_base); seq_printf(s, " deq_port_base: %d\n", port->deq_port_base); seq_printf(s, " deq_port_num: %d\n", port->deq_port_num); seq_printf(s, " num_dma_chan: %d\n", port->num_dma_chan); seq_printf(s, " dma_chan: 0x%x\n", port->dma_chan); seq_printf(s, " gpid_base: %d\n", port->gpid_base); seq_printf(s, " gpid_num: %d\n", port->gpid_num); seq_printf(s, " policy_base: %d\n", port->policy_base); seq_printf(s, " policy_num: %d\n", port->policy_num); seq_printf(s, " tx_pkt_credit: %d\n", port->tx_pkt_credit); seq_printf(s, " tx_b_credit: %02d\n", port->tx_b_credit); seq_printf(s, " txpush_addr: 0x%px\n", port->txpush_addr); seq_printf(s, " txpush_addr_qos: 0x%px\n", port->txpush_addr_qos); seq_printf(s, " tx_ring_size: %d\n", port->tx_ring_size); seq_printf(s, " tx_ring_offset: %d(to next dequeue port)\n", port->tx_ring_offset); if (port->num_rx_ring || port->num_tx_ring) dump_dc_info(s, port); /* check if there is a request to dump only specific subif info */ if (dump_one_vap) { start_vap = proc_write_vap; end_vap = proc_write_vap + 1; } else { start_vap = 0; if (pos == 0) end_vap = info->cap.max_num_subif_per_port; else end_vap = port->subif_max; } for (i = start_vap; i < end_vap; i++) dump_subif_info(s, port, i); seq_printf(s, " rx_err_drop=0x%08x tx_err_drop=0x%08x\n", STATS_GET(port->rx_err_drop), STATS_GET(port->tx_err_drop)); EXIT: if (pos == 0 && reinsert_deq_port) { cqm_info = get_dp_deqport_info(tmp_inst, reinsert_deq_port); seq_printf(s, " insertion_qid: %d\n", cqm_info->qid[0]); seq_printf(s, " insertion_deqport: %d\n", reinsert_deq_port); dp_dma_parse_id(cqm_info->dma_chan, &cid, &pid, &nid); seq_printf(s, " insertion_tx_dma_ch: 0x%x(dma-ctrl=%d,port=%d,channel=%d)\n", cqm_info->dma_chan, cid, pid, nid); } if (!seq_has_overflowed(s)) pos++; if ((pos >= MAX_DP_PORTS) || dump_one_port) { tmp_inst++; pos = 0; } if (tmp_inst >= dp_inst_num) pos = -1; /*end of the loop */ return pos; } int display_port_info(int inst, u8 pos, int start_vap, int end_vap, u32 flag) { int i; int ret; struct pmac_port_info *port = get_dp_port_info(inst, pos); u16 start = 0; if (!port) { pr_err("Why port is NULL\n"); return -1; } if (port->status == PORT_FREE) { if (pos == 0) pr_info("%s:rx_err_drop=0x%08x tx_err_drop=0x%08x\n", "Reserved Port", STATS_GET(port->rx_err_drop), STATS_GET(port->tx_err_drop)); else pr_info("%02d:rx_err_drop=0x%08x tx_err_drop=0x%08x\n", pos, STATS_GET(port->rx_err_drop), STATS_GET(port->tx_err_drop)); goto EXIT; } DP_CB(tmp_inst, get_itf_start_end) (port->itf_info, &start, NULL); pr_info("%02d: %s=0x0x%0lx(name:%8s) %s=%02d %s=%02d itf_base=%d(%s)\n", pos, "module", (uintptr_t)port->owner, port->owner->name, "dev_port", port->dev_port, "dp_port", port->port_id, start, port->itf_info ? "Enabled" : "Not Enabled"); pr_info(" status: %s\n", dp_port_status_str[port->status]); pr_info(" allocate_flags: "); for (i = 0; i < get_dp_port_type_str_size(); i++) { if (port->alloc_flags & dp_port_flag[i]) pr_info("%s ", dp_port_type_str[i]); } pr_info("\n"); if (!flag) { pr_info(" cb->rx_fn: %pF\n", (uintptr_t)port->cb.rx_fn); pr_info(" cb->restart_fn: %pF\n", (uintptr_t)port->cb.restart_fn); pr_info(" cb->stop_fn: %pF\n", (uintptr_t)port->cb.stop_fn); pr_info(" cb->get_subifid_fn:%pF\n", (uintptr_t)port->cb.get_subifid_fn); pr_info(" num_subif: %02d\n", port->num_subif); } for (i = start_vap; i < end_vap; i++) { struct dp_subif_info *sif = get_dp_port_subif(port, i); struct dev_mib *mib = get_dp_port_subif_mib(sif); if (sif->flags) { pr_info(" [%02d]:%s=0x%04x %s=0x%0lx(%s=%s),%s=%s\n", i, "subif", sif->subif, "netif", (uintptr_t)sif->netif, "device_name", sif->netif ? sif->netif->name : "NULL/DSL", "name", sif->device_name); pr_info(" : rx_fn_rxif_pkt =0x%08x\n", STATS_GET(mib->rx_fn_rxif_pkt)); pr_info(" : rx_fn_txif_pkt =0x%08x\n", STATS_GET(mib->rx_fn_txif_pkt)); pr_info(" : rx_fn_dropped =0x%08x\n", STATS_GET(mib->rx_fn_dropped)); pr_info(" : tx_cbm_pkt =0x%08x\n", STATS_GET(mib->tx_cbm_pkt)); pr_info(" : tx_tso_pkt =0x%08x\n", STATS_GET(mib->tx_tso_pkt)); pr_info(" : tx_pkt_dropped =0x%08x\n", STATS_GET(mib->tx_pkt_dropped)); pr_info(" : tx_clone_pkt =0x%08x\n", STATS_GET(mib->tx_clone_pkt)); pr_info(" : tx_hdr_room_pkt=0x%08x\n", STATS_GET(mib->tx_hdr_room_pkt)); } } ret = pr_info(" rx_err_drop=0x%08x tx_err_drop=0x%08x\n", STATS_GET(port->rx_err_drop), STATS_GET(port->tx_err_drop)); EXIT: return 0; } static int set_switchdev(int inst, int ep, int vap, bool enable) { #if IS_ENABLED(CONFIG_INTEL_DATAPATH_SWITCHDEV) struct pmac_port_info *port = get_dp_port_info(inst, ep); struct dp_subif_info *sif = get_dp_port_subif(port, vap); struct net_device *dev = sif->netif; struct net_device *br_dev; struct dp_dev *dp_dev; if (port->status != PORT_SUBIF_REGISTERED || !sif->flags) { pr_err("subif not registered\n"); return DP_FAILURE; } if (!sif->swdev_en == !enable) return DP_SUCCESS; dp_dev = dp_dev_lookup(dev); if (!dp_dev) { pr_err("dp_dev lookup failed\n"); return DP_FAILURE; } rtnl_lock(); br_dev = netdev_master_upper_dev_get(dev); rtnl_unlock(); if (enable) { sif->swdev_en = 1; if (dp_register_switchdev_ops(dp_dev, 0)) { pr_err("fail to register swdev ops\n"); return DP_FAILURE; } pr_info("swdev is enabled\n"); if (br_dev) { pr_info("master upper device (bridge?) detected\n"); pr_info("please reinit network for switchdev to be effective\n"); } } else { if (br_dev) { pr_err("master upper device (bridge?) detected\n"); pr_err("please remove link to upper dev before disabling switchdev\n"); return DP_FAILURE; } if (dp_register_switchdev_ops(dp_dev, 1)) { pr_err("fail to deregister swdev ops\n"); return DP_FAILURE; } sif->swdev_en = 0; pr_info("swdev is disabled\n"); } return DP_SUCCESS; #endif } #define DBG_PORT DP_DEBUGFS_PATH "/" DBGFS_PORT ssize_t proc_port_write(struct file *file, const char *buf, size_t count, loff_t *ppos) { char str[64]; /*later need to put real inst value */ struct inst_info *info = &dp_port_prop[0].info; struct pmac_port_info *port; int index_start = 0, index_end = MAX_DP_PORTS; int vap_start = 0, vap_end = info->cap.max_num_subif_per_port; char *param_list[10]; int swdev_en, len, num, inst, i; if (!capable(CAP_SYS_PACCT)) return count; len = (sizeof(str) > count) ? count : sizeof(str) - 1; len -= copy_from_user(str, buf, len); str[len] = 0; memset(param_list, 0, sizeof(param_list)); num = dp_split_buffer(str, param_list, ARRAY_SIZE(param_list)); if (num <= 1) goto help; if (param_list[1]) { index_start = dp_atoi(param_list[1]); index_end = index_start + 1; } if (param_list[2]) { vap_start = dp_atoi(param_list[2]); vap_end = vap_start + 1; } if (index_start >= MAX_DP_PORTS) { pr_err("invalid port index, expect 0 ~ %d\n", MAX_DP_PORTS - 1); return count; } if (vap_start >= info->cap.max_num_subif_per_port) { pr_err("invalid VAP index, expect 0 ~ %d\n", info->cap.max_num_subif_per_port - 1); return count; } if (strcasecmp(param_list[0], "help") == 0) { goto help; } else if (strcasecmp(param_list[0], "mib") == 0) { for (inst = 0; inst < dp_inst_num; inst++) for (i = index_start; i < index_end; i++) display_port_info(inst, i, vap_start, vap_end, 1); } else if (strcasecmp(param_list[0], "port") == 0) { if (proc_write_port != index_start) { pr_info("%s: changed port id to be dumped to %d\n", __func__, index_start); proc_write_port = index_start; } if (param_list[2] && (proc_write_vap != vap_start)) { pr_info("%s: changed sif id to be dumped to %d\n", __func__, vap_start); proc_write_vap = vap_start; } pr_info("%s: run 'cat %s' to dump requested port info\n", __func__, DEBUGFS_DBG); } else if (strcasecmp(param_list[0], "swdev") == 0) { if (num != 4) goto help; DP_LIB_LOCK(&dp_lock); port = get_dp_port_info(0, index_start); if (!port->num_subif) { pr_err("No subif available for port index %d\n", index_start); goto unlock; } if (vap_start >= port->num_subif) { pr_err("invalid VAP index, expect 0 ~ %d\n", port->num_subif - 1); goto unlock; } swdev_en = dp_atoi(param_list[3]); set_switchdev(0, index_start, vap_start, !!swdev_en); unlock: DP_LIB_UNLOCK(&dp_lock); } else { goto help; } return count; help: pr_info("usage:\n"); pr_info(" echo mib [ep][vap] > %s\n", DBG_PORT); pr_info(" echo port [ep][vap] > %s\n", DBG_PORT); pr_info(" to print all port info- echo port -1 > %s\n", DBG_PORT); pr_info(" echo swdev [ep][vap][swdev_en] > %s\n", DBG_PORT); return count; } #if defined(CONFIG_INTEL_DATAPATH_DBG) && CONFIG_INTEL_DATAPATH_DBG void proc_dbg_read(struct seq_file *s) { int i; if (!capable(CAP_SYS_ADMIN)) return; seq_printf(s, "dp_dbg_flag=0x%08x\n", dp_dbg_flag); seq_printf(s, "Supported Flags =%d\n", get_dp_dbg_flag_str_size()); seq_printf(s, "Enabled Flags(0x%0x):", dp_dbg_flag); for (i = 0; i < get_dp_dbg_flag_str_size(); i++) if ((dp_dbg_flag & dp_dbg_flag_list[i]) == dp_dbg_flag_list[i]) seq_printf(s, "%s ", dp_dbg_flag_str[i]); seq_puts(s, "\n\n"); seq_printf(s, "dp_drop_all_tcp_err=%d @ 0x%px\n", dp_drop_all_tcp_err, &dp_drop_all_tcp_err); seq_printf(s, "dp_pkt_size_check=%d @ 0x%px\n", dp_pkt_size_check, &dp_pkt_size_check); seq_printf(s, "dp_rx_test_mode=%d @ 0x%px\n", dp_rx_test_mode, &dp_rx_test_mode); seq_printf(s, "dp_dbg_err(flat to print error or not)=%d @ 0x%px\n", dp_dbg_err, &dp_dbg_err); print_parser_status(s); } ssize_t proc_dbg_write(struct file *file, const char *buf, size_t count, loff_t *ppos) { int len, i, j; char str[64]; int num; char *param_list[20]; int f_enable; char *tmp_buf; #define BUF_SIZE_TMP 2000 if (!capable(CAP_SYS_ADMIN)) return -EPERM; len = (sizeof(str) > count) ? count : sizeof(str) - 1; len -= copy_from_user(str, buf, len); str[len] = 0; num = dp_split_buffer(str, param_list, ARRAY_SIZE(param_list)); if (dp_strncmpi(param_list[0], "enable", strlen("enable")) == 0) f_enable = 1; else if (dp_strncmpi(param_list[0], "disable", strlen("disable")) == 0) f_enable = -1; else goto help; if (!param_list[1]) { /*no parameter after enable or disable */ set_ltq_dbg_flag(dp_dbg_flag, f_enable, -1); goto EXIT; } for (i = 1; i < num; i++) { for (j = 0; j < get_dp_dbg_flag_str_size() - 1; j++) if (dp_strncmpi(param_list[i], dp_dbg_flag_str[j], strlen(dp_dbg_flag_str[j]) + 1) == 0) { set_ltq_dbg_flag(dp_dbg_flag, f_enable, dp_dbg_flag_list[j]); break; } } EXIT: return count; help: tmp_buf = devm_kzalloc(&g_dp_dev->dev, BUF_SIZE_TMP, GFP_ATOMIC); if (!tmp_buf) return count; num = 0; num = snprintf(tmp_buf + num, BUF_SIZE_TMP - num - 1, "echo "); for (i = 0; i < get_dp_dbg_flag_str_size(); i++) num += snprintf(tmp_buf + num, BUF_SIZE_TMP - num - 1, "%s ", dp_dbg_flag_str[i]); num += snprintf(tmp_buf + num, BUF_SIZE_TMP - num - 1, " > %s\n", DEBUGFS_DBG); pr_info("%s", tmp_buf); devm_kfree(&g_dp_dev->dev, (tmp_buf)); return count; } #endif static struct dp_proc_entry dp_proc_entries[] = { /*name single_callback_t multi_callback_t/_start write_callback_t */ #if defined(CONFIG_INTEL_DATAPATH_DBG) && CONFIG_INTEL_DATAPATH_DBG { DBGFS_DBG, proc_dbg_read, NULL, NULL, proc_dbg_write }, #endif { DBGFS_PORT, NULL, proc_port_dump, proc_port_init, proc_port_write }, { DBGFS_INST_DEV, NULL, proc_inst_dev_dump, proc_inst_dev_start, NULL }, { DBGFS_INST_MOD, NULL, proc_inst_mod_dump, proc_inst_mod_start, NULL }, { DBGFS_INST_HAL, NULL, proc_inst_hal_dump, NULL, NULL }, { DBGFS_INST, NULL, proc_inst_dump, NULL, NULL }, { DBGFS_QOS, NULL, qos_dump, qos_dump_start, proc_qos_write }, { DBGFS_EVENT, NULL, proc_dp_event_list_dump, NULL, NULL }, { DBGFS_MIB_GLOBAL, NULL, proc_dp_mib_global_stats, NULL, proc_dp_mib_g_stats_write }, #if !IS_ENABLED(CONFIG_INTEL_DATAPATH_HAL_GSWIP30) { DBGFS_BR_VLAN, NULL, proc_brvlan_dump, proc_brvlan_start, NULL }, #endif { DBGFS_PCE, NULL, proc_pce_dump, NULL, proc_pce_write }, /*the last place holder */ { NULL, NULL, NULL, NULL, NULL } }; struct dentry *dp_proc_node; EXPORT_SYMBOL(dp_proc_node); struct dentry *dp_proc_install(void) { dp_proc_node = debugfs_create_dir(DP_DBGFS_NAME, NULL); if (dp_proc_node) { int i; for (i = 0; i < ARRAY_SIZE(dp_proc_entries); i++) dp_proc_entry_create(dp_proc_node, &dp_proc_entries[i]); } else { pr_err("datapath cannot create proc entry"); return NULL; } return dp_proc_node; }