162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * net/core/gen_stats.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Authors: Thomas Graf <tgraf@suug.ch> 662306a36Sopenharmony_ci * Jamal Hadi Salim 762306a36Sopenharmony_ci * Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * See Documentation/networking/gen_stats.rst 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/types.h> 1362306a36Sopenharmony_ci#include <linux/kernel.h> 1462306a36Sopenharmony_ci#include <linux/module.h> 1562306a36Sopenharmony_ci#include <linux/interrupt.h> 1662306a36Sopenharmony_ci#include <linux/socket.h> 1762306a36Sopenharmony_ci#include <linux/rtnetlink.h> 1862306a36Sopenharmony_ci#include <linux/gen_stats.h> 1962306a36Sopenharmony_ci#include <net/netlink.h> 2062306a36Sopenharmony_ci#include <net/gen_stats.h> 2162306a36Sopenharmony_ci#include <net/sch_generic.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistatic inline int 2462306a36Sopenharmony_cignet_stats_copy(struct gnet_dump *d, int type, void *buf, int size, int padattr) 2562306a36Sopenharmony_ci{ 2662306a36Sopenharmony_ci if (nla_put_64bit(d->skb, type, size, buf, padattr)) 2762306a36Sopenharmony_ci goto nla_put_failure; 2862306a36Sopenharmony_ci return 0; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cinla_put_failure: 3162306a36Sopenharmony_ci if (d->lock) 3262306a36Sopenharmony_ci spin_unlock_bh(d->lock); 3362306a36Sopenharmony_ci kfree(d->xstats); 3462306a36Sopenharmony_ci d->xstats = NULL; 3562306a36Sopenharmony_ci d->xstats_len = 0; 3662306a36Sopenharmony_ci return -1; 3762306a36Sopenharmony_ci} 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci/** 4062306a36Sopenharmony_ci * gnet_stats_start_copy_compat - start dumping procedure in compatibility mode 4162306a36Sopenharmony_ci * @skb: socket buffer to put statistics TLVs into 4262306a36Sopenharmony_ci * @type: TLV type for top level statistic TLV 4362306a36Sopenharmony_ci * @tc_stats_type: TLV type for backward compatibility struct tc_stats TLV 4462306a36Sopenharmony_ci * @xstats_type: TLV type for backward compatibility xstats TLV 4562306a36Sopenharmony_ci * @lock: statistics lock 4662306a36Sopenharmony_ci * @d: dumping handle 4762306a36Sopenharmony_ci * @padattr: padding attribute 4862306a36Sopenharmony_ci * 4962306a36Sopenharmony_ci * Initializes the dumping handle, grabs the statistic lock and appends 5062306a36Sopenharmony_ci * an empty TLV header to the socket buffer for use a container for all 5162306a36Sopenharmony_ci * other statistic TLVS. 5262306a36Sopenharmony_ci * 5362306a36Sopenharmony_ci * The dumping handle is marked to be in backward compatibility mode telling 5462306a36Sopenharmony_ci * all gnet_stats_copy_XXX() functions to fill a local copy of struct tc_stats. 5562306a36Sopenharmony_ci * 5662306a36Sopenharmony_ci * Returns 0 on success or -1 if the room in the socket buffer was not sufficient. 5762306a36Sopenharmony_ci */ 5862306a36Sopenharmony_ciint 5962306a36Sopenharmony_cignet_stats_start_copy_compat(struct sk_buff *skb, int type, int tc_stats_type, 6062306a36Sopenharmony_ci int xstats_type, spinlock_t *lock, 6162306a36Sopenharmony_ci struct gnet_dump *d, int padattr) 6262306a36Sopenharmony_ci __acquires(lock) 6362306a36Sopenharmony_ci{ 6462306a36Sopenharmony_ci memset(d, 0, sizeof(*d)); 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci if (type) 6762306a36Sopenharmony_ci d->tail = (struct nlattr *)skb_tail_pointer(skb); 6862306a36Sopenharmony_ci d->skb = skb; 6962306a36Sopenharmony_ci d->compat_tc_stats = tc_stats_type; 7062306a36Sopenharmony_ci d->compat_xstats = xstats_type; 7162306a36Sopenharmony_ci d->padattr = padattr; 7262306a36Sopenharmony_ci if (lock) { 7362306a36Sopenharmony_ci d->lock = lock; 7462306a36Sopenharmony_ci spin_lock_bh(lock); 7562306a36Sopenharmony_ci } 7662306a36Sopenharmony_ci if (d->tail) { 7762306a36Sopenharmony_ci int ret = gnet_stats_copy(d, type, NULL, 0, padattr); 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci /* The initial attribute added in gnet_stats_copy() may be 8062306a36Sopenharmony_ci * preceded by a padding attribute, in which case d->tail will 8162306a36Sopenharmony_ci * end up pointing at the padding instead of the real attribute. 8262306a36Sopenharmony_ci * Fix this so gnet_stats_finish_copy() adjusts the length of 8362306a36Sopenharmony_ci * the right attribute. 8462306a36Sopenharmony_ci */ 8562306a36Sopenharmony_ci if (ret == 0 && d->tail->nla_type == padattr) 8662306a36Sopenharmony_ci d->tail = (struct nlattr *)((char *)d->tail + 8762306a36Sopenharmony_ci NLA_ALIGN(d->tail->nla_len)); 8862306a36Sopenharmony_ci return ret; 8962306a36Sopenharmony_ci } 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci return 0; 9262306a36Sopenharmony_ci} 9362306a36Sopenharmony_ciEXPORT_SYMBOL(gnet_stats_start_copy_compat); 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci/** 9662306a36Sopenharmony_ci * gnet_stats_start_copy - start dumping procedure in compatibility mode 9762306a36Sopenharmony_ci * @skb: socket buffer to put statistics TLVs into 9862306a36Sopenharmony_ci * @type: TLV type for top level statistic TLV 9962306a36Sopenharmony_ci * @lock: statistics lock 10062306a36Sopenharmony_ci * @d: dumping handle 10162306a36Sopenharmony_ci * @padattr: padding attribute 10262306a36Sopenharmony_ci * 10362306a36Sopenharmony_ci * Initializes the dumping handle, grabs the statistic lock and appends 10462306a36Sopenharmony_ci * an empty TLV header to the socket buffer for use a container for all 10562306a36Sopenharmony_ci * other statistic TLVS. 10662306a36Sopenharmony_ci * 10762306a36Sopenharmony_ci * Returns 0 on success or -1 if the room in the socket buffer was not sufficient. 10862306a36Sopenharmony_ci */ 10962306a36Sopenharmony_ciint 11062306a36Sopenharmony_cignet_stats_start_copy(struct sk_buff *skb, int type, spinlock_t *lock, 11162306a36Sopenharmony_ci struct gnet_dump *d, int padattr) 11262306a36Sopenharmony_ci{ 11362306a36Sopenharmony_ci return gnet_stats_start_copy_compat(skb, type, 0, 0, lock, d, padattr); 11462306a36Sopenharmony_ci} 11562306a36Sopenharmony_ciEXPORT_SYMBOL(gnet_stats_start_copy); 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci/* Must not be inlined, due to u64_stats seqcount_t lockdep key */ 11862306a36Sopenharmony_civoid gnet_stats_basic_sync_init(struct gnet_stats_basic_sync *b) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci u64_stats_set(&b->bytes, 0); 12162306a36Sopenharmony_ci u64_stats_set(&b->packets, 0); 12262306a36Sopenharmony_ci u64_stats_init(&b->syncp); 12362306a36Sopenharmony_ci} 12462306a36Sopenharmony_ciEXPORT_SYMBOL(gnet_stats_basic_sync_init); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_cistatic void gnet_stats_add_basic_cpu(struct gnet_stats_basic_sync *bstats, 12762306a36Sopenharmony_ci struct gnet_stats_basic_sync __percpu *cpu) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci u64 t_bytes = 0, t_packets = 0; 13062306a36Sopenharmony_ci int i; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci for_each_possible_cpu(i) { 13362306a36Sopenharmony_ci struct gnet_stats_basic_sync *bcpu = per_cpu_ptr(cpu, i); 13462306a36Sopenharmony_ci unsigned int start; 13562306a36Sopenharmony_ci u64 bytes, packets; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci do { 13862306a36Sopenharmony_ci start = u64_stats_fetch_begin(&bcpu->syncp); 13962306a36Sopenharmony_ci bytes = u64_stats_read(&bcpu->bytes); 14062306a36Sopenharmony_ci packets = u64_stats_read(&bcpu->packets); 14162306a36Sopenharmony_ci } while (u64_stats_fetch_retry(&bcpu->syncp, start)); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci t_bytes += bytes; 14462306a36Sopenharmony_ci t_packets += packets; 14562306a36Sopenharmony_ci } 14662306a36Sopenharmony_ci _bstats_update(bstats, t_bytes, t_packets); 14762306a36Sopenharmony_ci} 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_civoid gnet_stats_add_basic(struct gnet_stats_basic_sync *bstats, 15062306a36Sopenharmony_ci struct gnet_stats_basic_sync __percpu *cpu, 15162306a36Sopenharmony_ci struct gnet_stats_basic_sync *b, bool running) 15262306a36Sopenharmony_ci{ 15362306a36Sopenharmony_ci unsigned int start; 15462306a36Sopenharmony_ci u64 bytes = 0; 15562306a36Sopenharmony_ci u64 packets = 0; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci WARN_ON_ONCE((cpu || running) && in_hardirq()); 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci if (cpu) { 16062306a36Sopenharmony_ci gnet_stats_add_basic_cpu(bstats, cpu); 16162306a36Sopenharmony_ci return; 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci do { 16462306a36Sopenharmony_ci if (running) 16562306a36Sopenharmony_ci start = u64_stats_fetch_begin(&b->syncp); 16662306a36Sopenharmony_ci bytes = u64_stats_read(&b->bytes); 16762306a36Sopenharmony_ci packets = u64_stats_read(&b->packets); 16862306a36Sopenharmony_ci } while (running && u64_stats_fetch_retry(&b->syncp, start)); 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci _bstats_update(bstats, bytes, packets); 17162306a36Sopenharmony_ci} 17262306a36Sopenharmony_ciEXPORT_SYMBOL(gnet_stats_add_basic); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_cistatic void gnet_stats_read_basic(u64 *ret_bytes, u64 *ret_packets, 17562306a36Sopenharmony_ci struct gnet_stats_basic_sync __percpu *cpu, 17662306a36Sopenharmony_ci struct gnet_stats_basic_sync *b, bool running) 17762306a36Sopenharmony_ci{ 17862306a36Sopenharmony_ci unsigned int start; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci if (cpu) { 18162306a36Sopenharmony_ci u64 t_bytes = 0, t_packets = 0; 18262306a36Sopenharmony_ci int i; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci for_each_possible_cpu(i) { 18562306a36Sopenharmony_ci struct gnet_stats_basic_sync *bcpu = per_cpu_ptr(cpu, i); 18662306a36Sopenharmony_ci unsigned int start; 18762306a36Sopenharmony_ci u64 bytes, packets; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci do { 19062306a36Sopenharmony_ci start = u64_stats_fetch_begin(&bcpu->syncp); 19162306a36Sopenharmony_ci bytes = u64_stats_read(&bcpu->bytes); 19262306a36Sopenharmony_ci packets = u64_stats_read(&bcpu->packets); 19362306a36Sopenharmony_ci } while (u64_stats_fetch_retry(&bcpu->syncp, start)); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci t_bytes += bytes; 19662306a36Sopenharmony_ci t_packets += packets; 19762306a36Sopenharmony_ci } 19862306a36Sopenharmony_ci *ret_bytes = t_bytes; 19962306a36Sopenharmony_ci *ret_packets = t_packets; 20062306a36Sopenharmony_ci return; 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci do { 20362306a36Sopenharmony_ci if (running) 20462306a36Sopenharmony_ci start = u64_stats_fetch_begin(&b->syncp); 20562306a36Sopenharmony_ci *ret_bytes = u64_stats_read(&b->bytes); 20662306a36Sopenharmony_ci *ret_packets = u64_stats_read(&b->packets); 20762306a36Sopenharmony_ci } while (running && u64_stats_fetch_retry(&b->syncp, start)); 20862306a36Sopenharmony_ci} 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_cistatic int 21162306a36Sopenharmony_ci___gnet_stats_copy_basic(struct gnet_dump *d, 21262306a36Sopenharmony_ci struct gnet_stats_basic_sync __percpu *cpu, 21362306a36Sopenharmony_ci struct gnet_stats_basic_sync *b, 21462306a36Sopenharmony_ci int type, bool running) 21562306a36Sopenharmony_ci{ 21662306a36Sopenharmony_ci u64 bstats_bytes, bstats_packets; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci gnet_stats_read_basic(&bstats_bytes, &bstats_packets, cpu, b, running); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci if (d->compat_tc_stats && type == TCA_STATS_BASIC) { 22162306a36Sopenharmony_ci d->tc_stats.bytes = bstats_bytes; 22262306a36Sopenharmony_ci d->tc_stats.packets = bstats_packets; 22362306a36Sopenharmony_ci } 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci if (d->tail) { 22662306a36Sopenharmony_ci struct gnet_stats_basic sb; 22762306a36Sopenharmony_ci int res; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci memset(&sb, 0, sizeof(sb)); 23062306a36Sopenharmony_ci sb.bytes = bstats_bytes; 23162306a36Sopenharmony_ci sb.packets = bstats_packets; 23262306a36Sopenharmony_ci res = gnet_stats_copy(d, type, &sb, sizeof(sb), TCA_STATS_PAD); 23362306a36Sopenharmony_ci if (res < 0 || sb.packets == bstats_packets) 23462306a36Sopenharmony_ci return res; 23562306a36Sopenharmony_ci /* emit 64bit stats only if needed */ 23662306a36Sopenharmony_ci return gnet_stats_copy(d, TCA_STATS_PKT64, &bstats_packets, 23762306a36Sopenharmony_ci sizeof(bstats_packets), TCA_STATS_PAD); 23862306a36Sopenharmony_ci } 23962306a36Sopenharmony_ci return 0; 24062306a36Sopenharmony_ci} 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci/** 24362306a36Sopenharmony_ci * gnet_stats_copy_basic - copy basic statistics into statistic TLV 24462306a36Sopenharmony_ci * @d: dumping handle 24562306a36Sopenharmony_ci * @cpu: copy statistic per cpu 24662306a36Sopenharmony_ci * @b: basic statistics 24762306a36Sopenharmony_ci * @running: true if @b represents a running qdisc, thus @b's 24862306a36Sopenharmony_ci * internal values might change during basic reads. 24962306a36Sopenharmony_ci * Only used if @cpu is NULL 25062306a36Sopenharmony_ci * 25162306a36Sopenharmony_ci * Context: task; must not be run from IRQ or BH contexts 25262306a36Sopenharmony_ci * 25362306a36Sopenharmony_ci * Appends the basic statistics to the top level TLV created by 25462306a36Sopenharmony_ci * gnet_stats_start_copy(). 25562306a36Sopenharmony_ci * 25662306a36Sopenharmony_ci * Returns 0 on success or -1 with the statistic lock released 25762306a36Sopenharmony_ci * if the room in the socket buffer was not sufficient. 25862306a36Sopenharmony_ci */ 25962306a36Sopenharmony_ciint 26062306a36Sopenharmony_cignet_stats_copy_basic(struct gnet_dump *d, 26162306a36Sopenharmony_ci struct gnet_stats_basic_sync __percpu *cpu, 26262306a36Sopenharmony_ci struct gnet_stats_basic_sync *b, 26362306a36Sopenharmony_ci bool running) 26462306a36Sopenharmony_ci{ 26562306a36Sopenharmony_ci return ___gnet_stats_copy_basic(d, cpu, b, TCA_STATS_BASIC, running); 26662306a36Sopenharmony_ci} 26762306a36Sopenharmony_ciEXPORT_SYMBOL(gnet_stats_copy_basic); 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci/** 27062306a36Sopenharmony_ci * gnet_stats_copy_basic_hw - copy basic hw statistics into statistic TLV 27162306a36Sopenharmony_ci * @d: dumping handle 27262306a36Sopenharmony_ci * @cpu: copy statistic per cpu 27362306a36Sopenharmony_ci * @b: basic statistics 27462306a36Sopenharmony_ci * @running: true if @b represents a running qdisc, thus @b's 27562306a36Sopenharmony_ci * internal values might change during basic reads. 27662306a36Sopenharmony_ci * Only used if @cpu is NULL 27762306a36Sopenharmony_ci * 27862306a36Sopenharmony_ci * Context: task; must not be run from IRQ or BH contexts 27962306a36Sopenharmony_ci * 28062306a36Sopenharmony_ci * Appends the basic statistics to the top level TLV created by 28162306a36Sopenharmony_ci * gnet_stats_start_copy(). 28262306a36Sopenharmony_ci * 28362306a36Sopenharmony_ci * Returns 0 on success or -1 with the statistic lock released 28462306a36Sopenharmony_ci * if the room in the socket buffer was not sufficient. 28562306a36Sopenharmony_ci */ 28662306a36Sopenharmony_ciint 28762306a36Sopenharmony_cignet_stats_copy_basic_hw(struct gnet_dump *d, 28862306a36Sopenharmony_ci struct gnet_stats_basic_sync __percpu *cpu, 28962306a36Sopenharmony_ci struct gnet_stats_basic_sync *b, 29062306a36Sopenharmony_ci bool running) 29162306a36Sopenharmony_ci{ 29262306a36Sopenharmony_ci return ___gnet_stats_copy_basic(d, cpu, b, TCA_STATS_BASIC_HW, running); 29362306a36Sopenharmony_ci} 29462306a36Sopenharmony_ciEXPORT_SYMBOL(gnet_stats_copy_basic_hw); 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci/** 29762306a36Sopenharmony_ci * gnet_stats_copy_rate_est - copy rate estimator statistics into statistics TLV 29862306a36Sopenharmony_ci * @d: dumping handle 29962306a36Sopenharmony_ci * @rate_est: rate estimator 30062306a36Sopenharmony_ci * 30162306a36Sopenharmony_ci * Appends the rate estimator statistics to the top level TLV created by 30262306a36Sopenharmony_ci * gnet_stats_start_copy(). 30362306a36Sopenharmony_ci * 30462306a36Sopenharmony_ci * Returns 0 on success or -1 with the statistic lock released 30562306a36Sopenharmony_ci * if the room in the socket buffer was not sufficient. 30662306a36Sopenharmony_ci */ 30762306a36Sopenharmony_ciint 30862306a36Sopenharmony_cignet_stats_copy_rate_est(struct gnet_dump *d, 30962306a36Sopenharmony_ci struct net_rate_estimator __rcu **rate_est) 31062306a36Sopenharmony_ci{ 31162306a36Sopenharmony_ci struct gnet_stats_rate_est64 sample; 31262306a36Sopenharmony_ci struct gnet_stats_rate_est est; 31362306a36Sopenharmony_ci int res; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci if (!gen_estimator_read(rate_est, &sample)) 31662306a36Sopenharmony_ci return 0; 31762306a36Sopenharmony_ci est.bps = min_t(u64, UINT_MAX, sample.bps); 31862306a36Sopenharmony_ci /* we have some time before reaching 2^32 packets per second */ 31962306a36Sopenharmony_ci est.pps = sample.pps; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci if (d->compat_tc_stats) { 32262306a36Sopenharmony_ci d->tc_stats.bps = est.bps; 32362306a36Sopenharmony_ci d->tc_stats.pps = est.pps; 32462306a36Sopenharmony_ci } 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci if (d->tail) { 32762306a36Sopenharmony_ci res = gnet_stats_copy(d, TCA_STATS_RATE_EST, &est, sizeof(est), 32862306a36Sopenharmony_ci TCA_STATS_PAD); 32962306a36Sopenharmony_ci if (res < 0 || est.bps == sample.bps) 33062306a36Sopenharmony_ci return res; 33162306a36Sopenharmony_ci /* emit 64bit stats only if needed */ 33262306a36Sopenharmony_ci return gnet_stats_copy(d, TCA_STATS_RATE_EST64, &sample, 33362306a36Sopenharmony_ci sizeof(sample), TCA_STATS_PAD); 33462306a36Sopenharmony_ci } 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci return 0; 33762306a36Sopenharmony_ci} 33862306a36Sopenharmony_ciEXPORT_SYMBOL(gnet_stats_copy_rate_est); 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_cistatic void gnet_stats_add_queue_cpu(struct gnet_stats_queue *qstats, 34162306a36Sopenharmony_ci const struct gnet_stats_queue __percpu *q) 34262306a36Sopenharmony_ci{ 34362306a36Sopenharmony_ci int i; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci for_each_possible_cpu(i) { 34662306a36Sopenharmony_ci const struct gnet_stats_queue *qcpu = per_cpu_ptr(q, i); 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci qstats->qlen += qcpu->qlen; 34962306a36Sopenharmony_ci qstats->backlog += qcpu->backlog; 35062306a36Sopenharmony_ci qstats->drops += qcpu->drops; 35162306a36Sopenharmony_ci qstats->requeues += qcpu->requeues; 35262306a36Sopenharmony_ci qstats->overlimits += qcpu->overlimits; 35362306a36Sopenharmony_ci } 35462306a36Sopenharmony_ci} 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_civoid gnet_stats_add_queue(struct gnet_stats_queue *qstats, 35762306a36Sopenharmony_ci const struct gnet_stats_queue __percpu *cpu, 35862306a36Sopenharmony_ci const struct gnet_stats_queue *q) 35962306a36Sopenharmony_ci{ 36062306a36Sopenharmony_ci if (cpu) { 36162306a36Sopenharmony_ci gnet_stats_add_queue_cpu(qstats, cpu); 36262306a36Sopenharmony_ci } else { 36362306a36Sopenharmony_ci qstats->qlen += q->qlen; 36462306a36Sopenharmony_ci qstats->backlog += q->backlog; 36562306a36Sopenharmony_ci qstats->drops += q->drops; 36662306a36Sopenharmony_ci qstats->requeues += q->requeues; 36762306a36Sopenharmony_ci qstats->overlimits += q->overlimits; 36862306a36Sopenharmony_ci } 36962306a36Sopenharmony_ci} 37062306a36Sopenharmony_ciEXPORT_SYMBOL(gnet_stats_add_queue); 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci/** 37362306a36Sopenharmony_ci * gnet_stats_copy_queue - copy queue statistics into statistics TLV 37462306a36Sopenharmony_ci * @d: dumping handle 37562306a36Sopenharmony_ci * @cpu_q: per cpu queue statistics 37662306a36Sopenharmony_ci * @q: queue statistics 37762306a36Sopenharmony_ci * @qlen: queue length statistics 37862306a36Sopenharmony_ci * 37962306a36Sopenharmony_ci * Appends the queue statistics to the top level TLV created by 38062306a36Sopenharmony_ci * gnet_stats_start_copy(). Using per cpu queue statistics if 38162306a36Sopenharmony_ci * they are available. 38262306a36Sopenharmony_ci * 38362306a36Sopenharmony_ci * Returns 0 on success or -1 with the statistic lock released 38462306a36Sopenharmony_ci * if the room in the socket buffer was not sufficient. 38562306a36Sopenharmony_ci */ 38662306a36Sopenharmony_ciint 38762306a36Sopenharmony_cignet_stats_copy_queue(struct gnet_dump *d, 38862306a36Sopenharmony_ci struct gnet_stats_queue __percpu *cpu_q, 38962306a36Sopenharmony_ci struct gnet_stats_queue *q, __u32 qlen) 39062306a36Sopenharmony_ci{ 39162306a36Sopenharmony_ci struct gnet_stats_queue qstats = {0}; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci gnet_stats_add_queue(&qstats, cpu_q, q); 39462306a36Sopenharmony_ci qstats.qlen = qlen; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci if (d->compat_tc_stats) { 39762306a36Sopenharmony_ci d->tc_stats.drops = qstats.drops; 39862306a36Sopenharmony_ci d->tc_stats.qlen = qstats.qlen; 39962306a36Sopenharmony_ci d->tc_stats.backlog = qstats.backlog; 40062306a36Sopenharmony_ci d->tc_stats.overlimits = qstats.overlimits; 40162306a36Sopenharmony_ci } 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci if (d->tail) 40462306a36Sopenharmony_ci return gnet_stats_copy(d, TCA_STATS_QUEUE, 40562306a36Sopenharmony_ci &qstats, sizeof(qstats), 40662306a36Sopenharmony_ci TCA_STATS_PAD); 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci return 0; 40962306a36Sopenharmony_ci} 41062306a36Sopenharmony_ciEXPORT_SYMBOL(gnet_stats_copy_queue); 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci/** 41362306a36Sopenharmony_ci * gnet_stats_copy_app - copy application specific statistics into statistics TLV 41462306a36Sopenharmony_ci * @d: dumping handle 41562306a36Sopenharmony_ci * @st: application specific statistics data 41662306a36Sopenharmony_ci * @len: length of data 41762306a36Sopenharmony_ci * 41862306a36Sopenharmony_ci * Appends the application specific statistics to the top level TLV created by 41962306a36Sopenharmony_ci * gnet_stats_start_copy() and remembers the data for XSTATS if the dumping 42062306a36Sopenharmony_ci * handle is in backward compatibility mode. 42162306a36Sopenharmony_ci * 42262306a36Sopenharmony_ci * Returns 0 on success or -1 with the statistic lock released 42362306a36Sopenharmony_ci * if the room in the socket buffer was not sufficient. 42462306a36Sopenharmony_ci */ 42562306a36Sopenharmony_ciint 42662306a36Sopenharmony_cignet_stats_copy_app(struct gnet_dump *d, void *st, int len) 42762306a36Sopenharmony_ci{ 42862306a36Sopenharmony_ci if (d->compat_xstats) { 42962306a36Sopenharmony_ci d->xstats = kmemdup(st, len, GFP_ATOMIC); 43062306a36Sopenharmony_ci if (!d->xstats) 43162306a36Sopenharmony_ci goto err_out; 43262306a36Sopenharmony_ci d->xstats_len = len; 43362306a36Sopenharmony_ci } 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci if (d->tail) 43662306a36Sopenharmony_ci return gnet_stats_copy(d, TCA_STATS_APP, st, len, 43762306a36Sopenharmony_ci TCA_STATS_PAD); 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci return 0; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_cierr_out: 44262306a36Sopenharmony_ci if (d->lock) 44362306a36Sopenharmony_ci spin_unlock_bh(d->lock); 44462306a36Sopenharmony_ci d->xstats_len = 0; 44562306a36Sopenharmony_ci return -1; 44662306a36Sopenharmony_ci} 44762306a36Sopenharmony_ciEXPORT_SYMBOL(gnet_stats_copy_app); 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci/** 45062306a36Sopenharmony_ci * gnet_stats_finish_copy - finish dumping procedure 45162306a36Sopenharmony_ci * @d: dumping handle 45262306a36Sopenharmony_ci * 45362306a36Sopenharmony_ci * Corrects the length of the top level TLV to include all TLVs added 45462306a36Sopenharmony_ci * by gnet_stats_copy_XXX() calls. Adds the backward compatibility TLVs 45562306a36Sopenharmony_ci * if gnet_stats_start_copy_compat() was used and releases the statistics 45662306a36Sopenharmony_ci * lock. 45762306a36Sopenharmony_ci * 45862306a36Sopenharmony_ci * Returns 0 on success or -1 with the statistic lock released 45962306a36Sopenharmony_ci * if the room in the socket buffer was not sufficient. 46062306a36Sopenharmony_ci */ 46162306a36Sopenharmony_ciint 46262306a36Sopenharmony_cignet_stats_finish_copy(struct gnet_dump *d) 46362306a36Sopenharmony_ci{ 46462306a36Sopenharmony_ci if (d->tail) 46562306a36Sopenharmony_ci d->tail->nla_len = skb_tail_pointer(d->skb) - (u8 *)d->tail; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci if (d->compat_tc_stats) 46862306a36Sopenharmony_ci if (gnet_stats_copy(d, d->compat_tc_stats, &d->tc_stats, 46962306a36Sopenharmony_ci sizeof(d->tc_stats), d->padattr) < 0) 47062306a36Sopenharmony_ci return -1; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci if (d->compat_xstats && d->xstats) { 47362306a36Sopenharmony_ci if (gnet_stats_copy(d, d->compat_xstats, d->xstats, 47462306a36Sopenharmony_ci d->xstats_len, d->padattr) < 0) 47562306a36Sopenharmony_ci return -1; 47662306a36Sopenharmony_ci } 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci if (d->lock) 47962306a36Sopenharmony_ci spin_unlock_bh(d->lock); 48062306a36Sopenharmony_ci kfree(d->xstats); 48162306a36Sopenharmony_ci d->xstats = NULL; 48262306a36Sopenharmony_ci d->xstats_len = 0; 48362306a36Sopenharmony_ci return 0; 48462306a36Sopenharmony_ci} 48562306a36Sopenharmony_ciEXPORT_SYMBOL(gnet_stats_finish_copy); 486