vmxnet3: Make ethtool handlers multiqueue aware
Show per-queue stats in ethtool -S output for vmxnet3 interface. Register dump of ethtool should dump registers for all tx and rx queues. Signed-off-by: Shreyas N Bhatewara <sbhatewara@vmware.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
39d4a96fd7
commit
76d39dae0a
1 changed files with 139 additions and 108 deletions
|
@ -68,76 +68,78 @@ vmxnet3_set_rx_csum(struct net_device *netdev, u32 val)
|
||||||
static const struct vmxnet3_stat_desc
|
static const struct vmxnet3_stat_desc
|
||||||
vmxnet3_tq_dev_stats[] = {
|
vmxnet3_tq_dev_stats[] = {
|
||||||
/* description, offset */
|
/* description, offset */
|
||||||
{ "TSO pkts tx", offsetof(struct UPT1_TxStats, TSOPktsTxOK) },
|
{ "Tx Queue#", 0 },
|
||||||
{ "TSO bytes tx", offsetof(struct UPT1_TxStats, TSOBytesTxOK) },
|
{ " TSO pkts tx", offsetof(struct UPT1_TxStats, TSOPktsTxOK) },
|
||||||
{ "ucast pkts tx", offsetof(struct UPT1_TxStats, ucastPktsTxOK) },
|
{ " TSO bytes tx", offsetof(struct UPT1_TxStats, TSOBytesTxOK) },
|
||||||
{ "ucast bytes tx", offsetof(struct UPT1_TxStats, ucastBytesTxOK) },
|
{ " ucast pkts tx", offsetof(struct UPT1_TxStats, ucastPktsTxOK) },
|
||||||
{ "mcast pkts tx", offsetof(struct UPT1_TxStats, mcastPktsTxOK) },
|
{ " ucast bytes tx", offsetof(struct UPT1_TxStats, ucastBytesTxOK) },
|
||||||
{ "mcast bytes tx", offsetof(struct UPT1_TxStats, mcastBytesTxOK) },
|
{ " mcast pkts tx", offsetof(struct UPT1_TxStats, mcastPktsTxOK) },
|
||||||
{ "bcast pkts tx", offsetof(struct UPT1_TxStats, bcastPktsTxOK) },
|
{ " mcast bytes tx", offsetof(struct UPT1_TxStats, mcastBytesTxOK) },
|
||||||
{ "bcast bytes tx", offsetof(struct UPT1_TxStats, bcastBytesTxOK) },
|
{ " bcast pkts tx", offsetof(struct UPT1_TxStats, bcastPktsTxOK) },
|
||||||
{ "pkts tx err", offsetof(struct UPT1_TxStats, pktsTxError) },
|
{ " bcast bytes tx", offsetof(struct UPT1_TxStats, bcastBytesTxOK) },
|
||||||
{ "pkts tx discard", offsetof(struct UPT1_TxStats, pktsTxDiscard) },
|
{ " pkts tx err", offsetof(struct UPT1_TxStats, pktsTxError) },
|
||||||
|
{ " pkts tx discard", offsetof(struct UPT1_TxStats, pktsTxDiscard) },
|
||||||
};
|
};
|
||||||
|
|
||||||
/* per tq stats maintained by the driver */
|
/* per tq stats maintained by the driver */
|
||||||
static const struct vmxnet3_stat_desc
|
static const struct vmxnet3_stat_desc
|
||||||
vmxnet3_tq_driver_stats[] = {
|
vmxnet3_tq_driver_stats[] = {
|
||||||
/* description, offset */
|
/* description, offset */
|
||||||
{"drv dropped tx total", offsetof(struct vmxnet3_tq_driver_stats,
|
{" drv dropped tx total", offsetof(struct vmxnet3_tq_driver_stats,
|
||||||
drop_total) },
|
drop_total) },
|
||||||
{ " too many frags", offsetof(struct vmxnet3_tq_driver_stats,
|
{ " too many frags", offsetof(struct vmxnet3_tq_driver_stats,
|
||||||
drop_too_many_frags) },
|
drop_too_many_frags) },
|
||||||
{ " giant hdr", offsetof(struct vmxnet3_tq_driver_stats,
|
{ " giant hdr", offsetof(struct vmxnet3_tq_driver_stats,
|
||||||
drop_oversized_hdr) },
|
drop_oversized_hdr) },
|
||||||
{ " hdr err", offsetof(struct vmxnet3_tq_driver_stats,
|
{ " hdr err", offsetof(struct vmxnet3_tq_driver_stats,
|
||||||
drop_hdr_inspect_err) },
|
drop_hdr_inspect_err) },
|
||||||
{ " tso", offsetof(struct vmxnet3_tq_driver_stats,
|
{ " tso", offsetof(struct vmxnet3_tq_driver_stats,
|
||||||
drop_tso) },
|
drop_tso) },
|
||||||
{ "ring full", offsetof(struct vmxnet3_tq_driver_stats,
|
{ " ring full", offsetof(struct vmxnet3_tq_driver_stats,
|
||||||
tx_ring_full) },
|
tx_ring_full) },
|
||||||
{ "pkts linearized", offsetof(struct vmxnet3_tq_driver_stats,
|
{ " pkts linearized", offsetof(struct vmxnet3_tq_driver_stats,
|
||||||
linearized) },
|
linearized) },
|
||||||
{ "hdr cloned", offsetof(struct vmxnet3_tq_driver_stats,
|
{ " hdr cloned", offsetof(struct vmxnet3_tq_driver_stats,
|
||||||
copy_skb_header) },
|
copy_skb_header) },
|
||||||
{ "giant hdr", offsetof(struct vmxnet3_tq_driver_stats,
|
{ " giant hdr", offsetof(struct vmxnet3_tq_driver_stats,
|
||||||
oversized_hdr) },
|
oversized_hdr) },
|
||||||
};
|
};
|
||||||
|
|
||||||
/* per rq stats maintained by the device */
|
/* per rq stats maintained by the device */
|
||||||
static const struct vmxnet3_stat_desc
|
static const struct vmxnet3_stat_desc
|
||||||
vmxnet3_rq_dev_stats[] = {
|
vmxnet3_rq_dev_stats[] = {
|
||||||
{ "LRO pkts rx", offsetof(struct UPT1_RxStats, LROPktsRxOK) },
|
{ "Rx Queue#", 0 },
|
||||||
{ "LRO byte rx", offsetof(struct UPT1_RxStats, LROBytesRxOK) },
|
{ " LRO pkts rx", offsetof(struct UPT1_RxStats, LROPktsRxOK) },
|
||||||
{ "ucast pkts rx", offsetof(struct UPT1_RxStats, ucastPktsRxOK) },
|
{ " LRO byte rx", offsetof(struct UPT1_RxStats, LROBytesRxOK) },
|
||||||
{ "ucast bytes rx", offsetof(struct UPT1_RxStats, ucastBytesRxOK) },
|
{ " ucast pkts rx", offsetof(struct UPT1_RxStats, ucastPktsRxOK) },
|
||||||
{ "mcast pkts rx", offsetof(struct UPT1_RxStats, mcastPktsRxOK) },
|
{ " ucast bytes rx", offsetof(struct UPT1_RxStats, ucastBytesRxOK) },
|
||||||
{ "mcast bytes rx", offsetof(struct UPT1_RxStats, mcastBytesRxOK) },
|
{ " mcast pkts rx", offsetof(struct UPT1_RxStats, mcastPktsRxOK) },
|
||||||
{ "bcast pkts rx", offsetof(struct UPT1_RxStats, bcastPktsRxOK) },
|
{ " mcast bytes rx", offsetof(struct UPT1_RxStats, mcastBytesRxOK) },
|
||||||
{ "bcast bytes rx", offsetof(struct UPT1_RxStats, bcastBytesRxOK) },
|
{ " bcast pkts rx", offsetof(struct UPT1_RxStats, bcastPktsRxOK) },
|
||||||
{ "pkts rx out of buf", offsetof(struct UPT1_RxStats, pktsRxOutOfBuf) },
|
{ " bcast bytes rx", offsetof(struct UPT1_RxStats, bcastBytesRxOK) },
|
||||||
{ "pkts rx err", offsetof(struct UPT1_RxStats, pktsRxError) },
|
{ " pkts rx OOB", offsetof(struct UPT1_RxStats, pktsRxOutOfBuf) },
|
||||||
|
{ " pkts rx err", offsetof(struct UPT1_RxStats, pktsRxError) },
|
||||||
};
|
};
|
||||||
|
|
||||||
/* per rq stats maintained by the driver */
|
/* per rq stats maintained by the driver */
|
||||||
static const struct vmxnet3_stat_desc
|
static const struct vmxnet3_stat_desc
|
||||||
vmxnet3_rq_driver_stats[] = {
|
vmxnet3_rq_driver_stats[] = {
|
||||||
/* description, offset */
|
/* description, offset */
|
||||||
{ "drv dropped rx total", offsetof(struct vmxnet3_rq_driver_stats,
|
{ " drv dropped rx total", offsetof(struct vmxnet3_rq_driver_stats,
|
||||||
drop_total) },
|
drop_total) },
|
||||||
{ " err", offsetof(struct vmxnet3_rq_driver_stats,
|
{ " err", offsetof(struct vmxnet3_rq_driver_stats,
|
||||||
drop_err) },
|
drop_err) },
|
||||||
{ " fcs", offsetof(struct vmxnet3_rq_driver_stats,
|
{ " fcs", offsetof(struct vmxnet3_rq_driver_stats,
|
||||||
drop_fcs) },
|
drop_fcs) },
|
||||||
{ "rx buf alloc fail", offsetof(struct vmxnet3_rq_driver_stats,
|
{ " rx buf alloc fail", offsetof(struct vmxnet3_rq_driver_stats,
|
||||||
rx_buf_alloc_failure) },
|
rx_buf_alloc_failure) },
|
||||||
};
|
};
|
||||||
|
|
||||||
/* gloabl stats maintained by the driver */
|
/* gloabl stats maintained by the driver */
|
||||||
static const struct vmxnet3_stat_desc
|
static const struct vmxnet3_stat_desc
|
||||||
vmxnet3_global_stats[] = {
|
vmxnet3_global_stats[] = {
|
||||||
/* description, offset */
|
/* description, offset */
|
||||||
{ "tx timeout count", offsetof(struct vmxnet3_adapter,
|
{ "tx timeout count", offsetof(struct vmxnet3_adapter,
|
||||||
tx_timeout_count) }
|
tx_timeout_count) }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -193,12 +195,15 @@ vmxnet3_get_stats(struct net_device *netdev)
|
||||||
static int
|
static int
|
||||||
vmxnet3_get_sset_count(struct net_device *netdev, int sset)
|
vmxnet3_get_sset_count(struct net_device *netdev, int sset)
|
||||||
{
|
{
|
||||||
|
struct vmxnet3_adapter *adapter = netdev_priv(netdev);
|
||||||
switch (sset) {
|
switch (sset) {
|
||||||
case ETH_SS_STATS:
|
case ETH_SS_STATS:
|
||||||
return ARRAY_SIZE(vmxnet3_tq_dev_stats) +
|
return (ARRAY_SIZE(vmxnet3_tq_dev_stats) +
|
||||||
ARRAY_SIZE(vmxnet3_tq_driver_stats) +
|
ARRAY_SIZE(vmxnet3_tq_driver_stats)) *
|
||||||
ARRAY_SIZE(vmxnet3_rq_dev_stats) +
|
adapter->num_tx_queues +
|
||||||
ARRAY_SIZE(vmxnet3_rq_driver_stats) +
|
(ARRAY_SIZE(vmxnet3_rq_dev_stats) +
|
||||||
|
ARRAY_SIZE(vmxnet3_rq_driver_stats)) *
|
||||||
|
adapter->num_rx_queues +
|
||||||
ARRAY_SIZE(vmxnet3_global_stats);
|
ARRAY_SIZE(vmxnet3_global_stats);
|
||||||
default:
|
default:
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
|
@ -206,10 +211,16 @@ vmxnet3_get_sset_count(struct net_device *netdev, int sset)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Should be multiple of 4 */
|
||||||
|
#define NUM_TX_REGS 8
|
||||||
|
#define NUM_RX_REGS 12
|
||||||
|
|
||||||
static int
|
static int
|
||||||
vmxnet3_get_regs_len(struct net_device *netdev)
|
vmxnet3_get_regs_len(struct net_device *netdev)
|
||||||
{
|
{
|
||||||
return 20 * sizeof(u32);
|
struct vmxnet3_adapter *adapter = netdev_priv(netdev);
|
||||||
|
return (adapter->num_tx_queues * NUM_TX_REGS * sizeof(u32) +
|
||||||
|
adapter->num_rx_queues * NUM_RX_REGS * sizeof(u32));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -240,29 +251,37 @@ vmxnet3_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo)
|
||||||
static void
|
static void
|
||||||
vmxnet3_get_strings(struct net_device *netdev, u32 stringset, u8 *buf)
|
vmxnet3_get_strings(struct net_device *netdev, u32 stringset, u8 *buf)
|
||||||
{
|
{
|
||||||
|
struct vmxnet3_adapter *adapter = netdev_priv(netdev);
|
||||||
if (stringset == ETH_SS_STATS) {
|
if (stringset == ETH_SS_STATS) {
|
||||||
int i;
|
int i, j;
|
||||||
|
for (j = 0; j < adapter->num_tx_queues; j++) {
|
||||||
|
for (i = 0; i < ARRAY_SIZE(vmxnet3_tq_dev_stats); i++) {
|
||||||
|
memcpy(buf, vmxnet3_tq_dev_stats[i].desc,
|
||||||
|
ETH_GSTRING_LEN);
|
||||||
|
buf += ETH_GSTRING_LEN;
|
||||||
|
}
|
||||||
|
for (i = 0; i < ARRAY_SIZE(vmxnet3_tq_driver_stats);
|
||||||
|
i++) {
|
||||||
|
memcpy(buf, vmxnet3_tq_driver_stats[i].desc,
|
||||||
|
ETH_GSTRING_LEN);
|
||||||
|
buf += ETH_GSTRING_LEN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(vmxnet3_tq_dev_stats); i++) {
|
for (j = 0; j < adapter->num_rx_queues; j++) {
|
||||||
memcpy(buf, vmxnet3_tq_dev_stats[i].desc,
|
for (i = 0; i < ARRAY_SIZE(vmxnet3_rq_dev_stats); i++) {
|
||||||
ETH_GSTRING_LEN);
|
memcpy(buf, vmxnet3_rq_dev_stats[i].desc,
|
||||||
buf += ETH_GSTRING_LEN;
|
ETH_GSTRING_LEN);
|
||||||
}
|
buf += ETH_GSTRING_LEN;
|
||||||
for (i = 0; i < ARRAY_SIZE(vmxnet3_tq_driver_stats); i++) {
|
}
|
||||||
memcpy(buf, vmxnet3_tq_driver_stats[i].desc,
|
for (i = 0; i < ARRAY_SIZE(vmxnet3_rq_driver_stats);
|
||||||
ETH_GSTRING_LEN);
|
i++) {
|
||||||
buf += ETH_GSTRING_LEN;
|
memcpy(buf, vmxnet3_rq_driver_stats[i].desc,
|
||||||
}
|
ETH_GSTRING_LEN);
|
||||||
for (i = 0; i < ARRAY_SIZE(vmxnet3_rq_dev_stats); i++) {
|
buf += ETH_GSTRING_LEN;
|
||||||
memcpy(buf, vmxnet3_rq_dev_stats[i].desc,
|
}
|
||||||
ETH_GSTRING_LEN);
|
|
||||||
buf += ETH_GSTRING_LEN;
|
|
||||||
}
|
|
||||||
for (i = 0; i < ARRAY_SIZE(vmxnet3_rq_driver_stats); i++) {
|
|
||||||
memcpy(buf, vmxnet3_rq_driver_stats[i].desc,
|
|
||||||
ETH_GSTRING_LEN);
|
|
||||||
buf += ETH_GSTRING_LEN;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(vmxnet3_global_stats); i++) {
|
for (i = 0; i < ARRAY_SIZE(vmxnet3_global_stats); i++) {
|
||||||
memcpy(buf, vmxnet3_global_stats[i].desc,
|
memcpy(buf, vmxnet3_global_stats[i].desc,
|
||||||
ETH_GSTRING_LEN);
|
ETH_GSTRING_LEN);
|
||||||
|
@ -310,23 +329,31 @@ vmxnet3_get_ethtool_stats(struct net_device *netdev,
|
||||||
VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, VMXNET3_CMD_GET_STATS);
|
VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, VMXNET3_CMD_GET_STATS);
|
||||||
|
|
||||||
/* this does assume each counter is 64-bit wide */
|
/* this does assume each counter is 64-bit wide */
|
||||||
/* TODO change this for multiple queues */
|
for (j = 0; j < adapter->num_tx_queues; j++) {
|
||||||
|
base = (u8 *)&adapter->tqd_start[j].stats;
|
||||||
|
*buf++ = (u64)j;
|
||||||
|
for (i = 1; i < ARRAY_SIZE(vmxnet3_tq_dev_stats); i++)
|
||||||
|
*buf++ = *(u64 *)(base +
|
||||||
|
vmxnet3_tq_dev_stats[i].offset);
|
||||||
|
|
||||||
base = (u8 *)&adapter->tqd_start[j].stats;
|
base = (u8 *)&adapter->tx_queue[j].stats;
|
||||||
for (i = 0; i < ARRAY_SIZE(vmxnet3_tq_dev_stats); i++)
|
for (i = 0; i < ARRAY_SIZE(vmxnet3_tq_driver_stats); i++)
|
||||||
*buf++ = *(u64 *)(base + vmxnet3_tq_dev_stats[i].offset);
|
*buf++ = *(u64 *)(base +
|
||||||
|
vmxnet3_tq_driver_stats[i].offset);
|
||||||
|
}
|
||||||
|
|
||||||
base = (u8 *)&adapter->tx_queue[j].stats;
|
for (j = 0; j < adapter->num_tx_queues; j++) {
|
||||||
for (i = 0; i < ARRAY_SIZE(vmxnet3_tq_driver_stats); i++)
|
base = (u8 *)&adapter->rqd_start[j].stats;
|
||||||
*buf++ = *(u64 *)(base + vmxnet3_tq_driver_stats[i].offset);
|
*buf++ = (u64) j;
|
||||||
|
for (i = 1; i < ARRAY_SIZE(vmxnet3_rq_dev_stats); i++)
|
||||||
|
*buf++ = *(u64 *)(base +
|
||||||
|
vmxnet3_rq_dev_stats[i].offset);
|
||||||
|
|
||||||
base = (u8 *)&adapter->rqd_start[j].stats;
|
base = (u8 *)&adapter->rx_queue[j].stats;
|
||||||
for (i = 0; i < ARRAY_SIZE(vmxnet3_rq_dev_stats); i++)
|
for (i = 0; i < ARRAY_SIZE(vmxnet3_rq_driver_stats); i++)
|
||||||
*buf++ = *(u64 *)(base + vmxnet3_rq_dev_stats[i].offset);
|
*buf++ = *(u64 *)(base +
|
||||||
|
vmxnet3_rq_driver_stats[i].offset);
|
||||||
base = (u8 *)&adapter->rx_queue[j].stats;
|
}
|
||||||
for (i = 0; i < ARRAY_SIZE(vmxnet3_rq_driver_stats); i++)
|
|
||||||
*buf++ = *(u64 *)(base + vmxnet3_rq_driver_stats[i].offset);
|
|
||||||
|
|
||||||
base = (u8 *)adapter;
|
base = (u8 *)adapter;
|
||||||
for (i = 0; i < ARRAY_SIZE(vmxnet3_global_stats); i++)
|
for (i = 0; i < ARRAY_SIZE(vmxnet3_global_stats); i++)
|
||||||
|
@ -339,7 +366,7 @@ vmxnet3_get_regs(struct net_device *netdev, struct ethtool_regs *regs, void *p)
|
||||||
{
|
{
|
||||||
struct vmxnet3_adapter *adapter = netdev_priv(netdev);
|
struct vmxnet3_adapter *adapter = netdev_priv(netdev);
|
||||||
u32 *buf = p;
|
u32 *buf = p;
|
||||||
int i = 0;
|
int i = 0, j = 0;
|
||||||
|
|
||||||
memset(p, 0, vmxnet3_get_regs_len(netdev));
|
memset(p, 0, vmxnet3_get_regs_len(netdev));
|
||||||
|
|
||||||
|
@ -348,31 +375,35 @@ vmxnet3_get_regs(struct net_device *netdev, struct ethtool_regs *regs, void *p)
|
||||||
/* Update vmxnet3_get_regs_len if we want to dump more registers */
|
/* Update vmxnet3_get_regs_len if we want to dump more registers */
|
||||||
|
|
||||||
/* make each ring use multiple of 16 bytes */
|
/* make each ring use multiple of 16 bytes */
|
||||||
/* TODO change this for multiple queues */
|
for (i = 0; i < adapter->num_tx_queues; i++) {
|
||||||
buf[0] = adapter->tx_queue[i].tx_ring.next2fill;
|
buf[j++] = adapter->tx_queue[i].tx_ring.next2fill;
|
||||||
buf[1] = adapter->tx_queue[i].tx_ring.next2comp;
|
buf[j++] = adapter->tx_queue[i].tx_ring.next2comp;
|
||||||
buf[2] = adapter->tx_queue[i].tx_ring.gen;
|
buf[j++] = adapter->tx_queue[i].tx_ring.gen;
|
||||||
buf[3] = 0;
|
buf[j++] = 0;
|
||||||
|
|
||||||
buf[4] = adapter->tx_queue[i].comp_ring.next2proc;
|
buf[j++] = adapter->tx_queue[i].comp_ring.next2proc;
|
||||||
buf[5] = adapter->tx_queue[i].comp_ring.gen;
|
buf[j++] = adapter->tx_queue[i].comp_ring.gen;
|
||||||
buf[6] = adapter->tx_queue[i].stopped;
|
buf[j++] = adapter->tx_queue[i].stopped;
|
||||||
buf[7] = 0;
|
buf[j++] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
buf[8] = adapter->rx_queue[i].rx_ring[0].next2fill;
|
for (i = 0; i < adapter->num_rx_queues; i++) {
|
||||||
buf[9] = adapter->rx_queue[i].rx_ring[0].next2comp;
|
buf[j++] = adapter->rx_queue[i].rx_ring[0].next2fill;
|
||||||
buf[10] = adapter->rx_queue[i].rx_ring[0].gen;
|
buf[j++] = adapter->rx_queue[i].rx_ring[0].next2comp;
|
||||||
buf[11] = 0;
|
buf[j++] = adapter->rx_queue[i].rx_ring[0].gen;
|
||||||
|
buf[j++] = 0;
|
||||||
|
|
||||||
buf[12] = adapter->rx_queue[i].rx_ring[1].next2fill;
|
buf[j++] = adapter->rx_queue[i].rx_ring[1].next2fill;
|
||||||
buf[13] = adapter->rx_queue[i].rx_ring[1].next2comp;
|
buf[j++] = adapter->rx_queue[i].rx_ring[1].next2comp;
|
||||||
buf[14] = adapter->rx_queue[i].rx_ring[1].gen;
|
buf[j++] = adapter->rx_queue[i].rx_ring[1].gen;
|
||||||
buf[15] = 0;
|
buf[j++] = 0;
|
||||||
|
|
||||||
|
buf[j++] = adapter->rx_queue[i].comp_ring.next2proc;
|
||||||
|
buf[j++] = adapter->rx_queue[i].comp_ring.gen;
|
||||||
|
buf[j++] = 0;
|
||||||
|
buf[j++] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
buf[16] = adapter->rx_queue[i].comp_ring.next2proc;
|
|
||||||
buf[17] = adapter->rx_queue[i].comp_ring.gen;
|
|
||||||
buf[18] = 0;
|
|
||||||
buf[19] = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue