IPVS stats are limited to 2^(32-10) conns/s and packets/s, 2^(32-5) bytes/s. It is time to use 64 bits: * Change all conn/packet kernel counters to 64-bit and update them in u64_stats_update_{begin,end} section * In kernel use struct ip_vs_kstats instead of the user-space struct ip_vs_stats_user and use new func ip_vs_export_stats_user to export it to sockopt users to preserve compatibility with 32-bit values * Rename cpu counters "ustats" to "cnt" * To netlink users provide additionally 64-bit stats: IPVS_SVC_ATTR_STATS64 and IPVS_DEST_ATTR_STATS64. Old stats remain for old binaries. * We can use ip_vs_copy_stats in ip_vs_stats_percpu_show Thanks to Chris Caputo for providing initial patch for ip_vs_est.c
Unified · Split
| @@ -365,15 +365,15 @@ struct ip_vs_seq { · 364 unmodified lines | ||
| 365 | 365 | |
| 366 | 366 | /* counters per cpu */ |
| 367 | 367 | struct ip_vs_counters { |
| 368 | − __u32 conns; /* connections scheduled */ | |
| 369 | − __u32 inpkts; /* incoming packets */ | |
| 370 | − __u32 outpkts; /* outgoing packets */ | |
| 368 | + __u64 conns; /* connections scheduled */ | |
| 369 | + __u64 inpkts; /* incoming packets */ | |
| 370 | + __u64 outpkts; /* outgoing packets */ | |
| 371 | 371 | __u64 inbytes; /* incoming bytes */ |
| 372 | 372 | __u64 outbytes; /* outgoing bytes */ |
| 373 | 373 | }; |
| 374 | 374 | /* Stats per cpu */ |
| 375 | 375 | struct ip_vs_cpu_stats { |
| 376 | − struct ip_vs_counters ustats; | |
| 376 | + struct ip_vs_counters cnt; | |
| 377 | 377 | struct u64_stats_sync syncp; |
| 378 | 378 | }; |
| 379 | 379 | |
| @@ -383,23 +383,40 @@ struct ip_vs_estimator { · 3 unmodified lines | ||
| 383 | 383 | |
| 384 | 384 | u64 last_inbytes; |
| 385 | 385 | u64 last_outbytes; |
| 386 | − u32 last_conns; | |
| 387 | − u32 last_inpkts; | |
| 388 | − u32 last_outpkts; | |
| 389 | − | |
| 390 | − u32 cps; | |
| 391 | − u32 inpps; | |
| 392 | − u32 outpps; | |
| 393 | − u32 inbps; | |
| 394 | − u32 outbps; | |
| 386 | + u64 last_conns; | |
| 387 | + u64 last_inpkts; | |
| 388 | + u64 last_outpkts; | |
| 389 | + | |
| 390 | + u64 cps; | |
| 391 | + u64 inpps; | |
| 392 | + u64 outpps; | |
| 393 | + u64 inbps; | |
| 394 | + u64 outbps; | |
| 395 | +}; | |
| 396 | + | |
| 397 | +/* | |
| 398 | + * IPVS statistics object, 64-bit kernel version of struct ip_vs_stats_user | |
| 399 | + */ | |
| 400 | +struct ip_vs_kstats { | |
| 401 | + u64 conns; /* connections scheduled */ | |
| 402 | + u64 inpkts; /* incoming packets */ | |
| 403 | + u64 outpkts; /* outgoing packets */ | |
| 404 | + u64 inbytes; /* incoming bytes */ | |
| 405 | + u64 outbytes; /* outgoing bytes */ | |
| 406 | + | |
| 407 | + u64 cps; /* current connection rate */ | |
| 408 | + u64 inpps; /* current in packet rate */ | |
| 409 | + u64 outpps; /* current out packet rate */ | |
| 410 | + u64 inbps; /* current in byte rate */ | |
| 411 | + u64 outbps; /* current out byte rate */ | |
| 395 | 412 | }; |
| 396 | 413 | |
| 397 | 414 | struct ip_vs_stats { |
| 398 | − struct ip_vs_stats_user ustats; /* statistics */ | |
| 415 | + struct ip_vs_kstats kstats; /* kernel statistics */ | |
| 399 | 416 | struct ip_vs_estimator est; /* estimator */ |
| 400 | 417 | struct ip_vs_cpu_stats __percpu *cpustats; /* per cpu counters */ |
| 401 | 418 | spinlock_t lock; /* spin lock */ |
| 402 | − struct ip_vs_stats_user ustats0; /* reset values */ | |
| 419 | + struct ip_vs_kstats kstats0; /* reset values */ | |
| 403 | 420 | }; |
| 404 | 421 | |
| 405 | 422 | struct dst_entry; |
| @@ -1388,8 +1405,7 @@ void ip_vs_sync_conn(struct net *net, struct ip_vs_conn *cp, int pkts); · 982 unmodified lines | ||
| 1388 | 1405 | void ip_vs_start_estimator(struct net *net, struct ip_vs_stats *stats); |
| 1389 | 1406 | void ip_vs_stop_estimator(struct net *net, struct ip_vs_stats *stats); |
| 1390 | 1407 | void ip_vs_zero_estimator(struct ip_vs_stats *stats); |
| 1391 | −void ip_vs_read_estimator(struct ip_vs_stats_user *dst, | |
| 1392 | − struct ip_vs_stats *stats); | |
| 1408 | +void ip_vs_read_estimator(struct ip_vs_kstats *dst, struct ip_vs_stats *stats); | |
| 1393 | 1409 | |
| 1394 | 1410 | /* Various IPVS packet transmitters (from ip_vs_xmit.c) */ |
| 1395 | 1411 | int ip_vs_null_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, |
| @@ -358,6 +358,8 @@ enum { · 357 unmodified lines | ||
| 358 | 358 | |
| 359 | 359 | IPVS_SVC_ATTR_PE_NAME, /* name of ct retriever */ |
| 360 | 360 | |
| 361 | + IPVS_SVC_ATTR_STATS64, /* nested attribute for service stats */ | |
| 362 | + | |
| 361 | 363 | __IPVS_SVC_ATTR_MAX, |
| 362 | 364 | }; |
| 363 | 365 | |
| @@ -387,6 +389,8 @@ enum { · 23 unmodified lines | ||
| 387 | 389 | |
| 388 | 390 | IPVS_DEST_ATTR_ADDR_FAMILY, /* Address family of address */ |
| 389 | 391 | |
| 392 | + IPVS_DEST_ATTR_STATS64, /* nested attribute for dest stats */ | |
| 393 | + | |
| 390 | 394 | __IPVS_DEST_ATTR_MAX, |
| 391 | 395 | }; |
| 392 | 396 | |
| @@ -410,7 +414,8 @@ enum { · 17 unmodified lines | ||
| 410 | 414 | /* |
| 411 | 415 | * Attributes used to describe service or destination entry statistics |
| 412 | 416 | * |
| 413 | − * Used inside nested attributes IPVS_SVC_ATTR_STATS and IPVS_DEST_ATTR_STATS | |
| 417 | + * Used inside nested attributes IPVS_SVC_ATTR_STATS, IPVS_DEST_ATTR_STATS, | |
| 418 | + * IPVS_SVC_ATTR_STATS64 and IPVS_DEST_ATTR_STATS64. | |
| 414 | 419 | */ |
| 415 | 420 | enum { |
| 416 | 421 | IPVS_STATS_ATTR_UNSPEC = 0, |
| @@ -119,24 +119,24 @@ ip_vs_in_stats(struct ip_vs_conn *cp, struct sk_buff *skb) · 118 unmodified lines | ||
| 119 | 119 | struct ip_vs_service *svc; |
| 120 | 120 | |
| 121 | 121 | s = this_cpu_ptr(dest->stats.cpustats); |
| 122 | − s->ustats.inpkts++; | |
| 123 | 122 | u64_stats_update_begin(&s->syncp); |
| 124 | − s->ustats.inbytes += skb->len; | |
| 123 | + s->cnt.inpkts++; | |
| 124 | + s->cnt.inbytes += skb->len; | |
| 125 | 125 | u64_stats_update_end(&s->syncp); |
| 126 | 126 | |
| 127 | 127 | rcu_read_lock(); |
| 128 | 128 | svc = rcu_dereference(dest->svc); |
| 129 | 129 | s = this_cpu_ptr(svc->stats.cpustats); |
| 130 | − s->ustats.inpkts++; | |
| 131 | 130 | u64_stats_update_begin(&s->syncp); |
| 132 | − s->ustats.inbytes += skb->len; | |
| 131 | + s->cnt.inpkts++; | |
| 132 | + s->cnt.inbytes += skb->len; | |
| 133 | 133 | u64_stats_update_end(&s->syncp); |
| 134 | 134 | rcu_read_unlock(); |
| 135 | 135 | |
| 136 | 136 | s = this_cpu_ptr(ipvs->tot_stats.cpustats); |
| 137 | − s->ustats.inpkts++; | |
| 138 | 137 | u64_stats_update_begin(&s->syncp); |
| 139 | − s->ustats.inbytes += skb->len; | |
| 138 | + s->cnt.inpkts++; | |
| 139 | + s->cnt.inbytes += skb->len; | |
| 140 | 140 | u64_stats_update_end(&s->syncp); |
| 141 | 141 | } |
| 142 | 142 | } |
| @@ -153,24 +153,24 @@ ip_vs_out_stats(struct ip_vs_conn *cp, struct sk_buff *skb) · 10 unmodified lines | ||
| 153 | 153 | struct ip_vs_service *svc; |
| 154 | 154 | |
| 155 | 155 | s = this_cpu_ptr(dest->stats.cpustats); |
| 156 | − s->ustats.outpkts++; | |
| 157 | 156 | u64_stats_update_begin(&s->syncp); |
| 158 | − s->ustats.outbytes += skb->len; | |
| 157 | + s->cnt.outpkts++; | |
| 158 | + s->cnt.outbytes += skb->len; | |
| 159 | 159 | u64_stats_update_end(&s->syncp); |
| 160 | 160 | |
| 161 | 161 | rcu_read_lock(); |
| 162 | 162 | svc = rcu_dereference(dest->svc); |
| 163 | 163 | s = this_cpu_ptr(svc->stats.cpustats); |
| 164 | − s->ustats.outpkts++; | |
| 165 | 164 | u64_stats_update_begin(&s->syncp); |
| 166 | − s->ustats.outbytes += skb->len; | |
| 165 | + s->cnt.outpkts++; | |
| 166 | + s->cnt.outbytes += skb->len; | |
| 167 | 167 | u64_stats_update_end(&s->syncp); |
| 168 | 168 | rcu_read_unlock(); |
| 169 | 169 | |
| 170 | 170 | s = this_cpu_ptr(ipvs->tot_stats.cpustats); |
| 171 | − s->ustats.outpkts++; | |
| 172 | 171 | u64_stats_update_begin(&s->syncp); |
| 173 | − s->ustats.outbytes += skb->len; | |
| 172 | + s->cnt.outpkts++; | |
| 173 | + s->cnt.outbytes += skb->len; | |
| 174 | 174 | u64_stats_update_end(&s->syncp); |
| 175 | 175 | } |
| 176 | 176 | } |
| @@ -183,13 +183,19 @@ ip_vs_conn_stats(struct ip_vs_conn *cp, struct ip_vs_service *svc) · 6 unmodified lines | ||
| 183 | 183 | struct ip_vs_cpu_stats *s; |
| 184 | 184 | |
| 185 | 185 | s = this_cpu_ptr(cp->dest->stats.cpustats); |
| 186 | − s->ustats.conns++; | |
| 186 | + u64_stats_update_begin(&s->syncp); | |
| 187 | + s->cnt.conns++; | |
| 188 | + u64_stats_update_end(&s->syncp); | |
| 187 | 189 | |
| 188 | 190 | s = this_cpu_ptr(svc->stats.cpustats); |
| 189 | − s->ustats.conns++; | |
| 191 | + u64_stats_update_begin(&s->syncp); | |
| 192 | + s->cnt.conns++; | |
| 193 | + u64_stats_update_end(&s->syncp); | |
| 190 | 194 | |
| 191 | 195 | s = this_cpu_ptr(ipvs->tot_stats.cpustats); |
| 192 | − s->ustats.conns++; | |
| 196 | + u64_stats_update_begin(&s->syncp); | |
| 197 | + s->cnt.conns++; | |
| 198 | + u64_stats_update_end(&s->syncp); | |
| 193 | 199 | } |
| 194 | 200 | |
| 195 | 201 | |
| @@ -729,9 +729,9 @@ static void ip_vs_trash_cleanup(struct net *net) · 728 unmodified lines | ||
| 729 | 729 | } |
| 730 | 730 | |
| 731 | 731 | static void |
| 732 | −ip_vs_copy_stats(struct ip_vs_stats_user *dst, struct ip_vs_stats *src) | |
| 732 | +ip_vs_copy_stats(struct ip_vs_kstats *dst, struct ip_vs_stats *src) | |
| 733 | 733 | { |
| 734 | −#define IP_VS_SHOW_STATS_COUNTER(c) dst->c = src->ustats.c - src->ustats0.c | |
| 734 | +#define IP_VS_SHOW_STATS_COUNTER(c) dst->c = src->kstats.c - src->kstats0.c | |
| 735 | 735 | |
| 736 | 736 | spin_lock_bh(&src->lock); |
| 737 | 737 | |
| @@ -746,6 +746,21 @@ ip_vs_copy_stats(struct ip_vs_stats_user *dst, struct ip_vs_stats *src) · 8 unmodified lines | ||
| 746 | 746 | spin_unlock_bh(&src->lock); |
| 747 | 747 | } |
| 748 | 748 | |
| 749 | +static void | |
| 750 | +ip_vs_export_stats_user(struct ip_vs_stats_user *dst, struct ip_vs_kstats *src) | |
| 751 | +{ | |
| 752 | + dst->conns = (u32)src->conns; | |
| 753 | + dst->inpkts = (u32)src->inpkts; | |
| 754 | + dst->outpkts = (u32)src->outpkts; | |
| 755 | + dst->inbytes = src->inbytes; | |
| 756 | + dst->outbytes = src->outbytes; | |
| 757 | + dst->cps = (u32)src->cps; | |
| 758 | + dst->inpps = (u32)src->inpps; | |
| 759 | + dst->outpps = (u32)src->outpps; | |
| 760 | + dst->inbps = (u32)src->inbps; | |
| 761 | + dst->outbps = (u32)src->outbps; | |
| 762 | +} | |
| 763 | + | |
| 749 | 764 | static void |
| 750 | 765 | ip_vs_zero_stats(struct ip_vs_stats *stats) |
| 751 | 766 | { |
| @@ -753,7 +768,7 @@ ip_vs_zero_stats(struct ip_vs_stats *stats) · 1 unmodified line | ||
| 753 | 768 | |
| 754 | 769 | /* get current counters as zero point, rates are zeroed */ |
| 755 | 770 | |
| 756 | −#define IP_VS_ZERO_STATS_COUNTER(c) stats->ustats0.c = stats->ustats.c | |
| 771 | +#define IP_VS_ZERO_STATS_COUNTER(c) stats->kstats0.c = stats->kstats.c | |
| 757 | 772 | |
| 758 | 773 | IP_VS_ZERO_STATS_COUNTER(conns); |
| 759 | 774 | IP_VS_ZERO_STATS_COUNTER(inpkts); |
| @@ -2044,7 +2059,7 @@ static const struct file_operations ip_vs_info_fops = { · 1284 unmodified lines | ||
| 2044 | 2059 | static int ip_vs_stats_show(struct seq_file *seq, void *v) |
| 2045 | 2060 | { |
| 2046 | 2061 | struct net *net = seq_file_single_net(seq); |
| 2047 | − struct ip_vs_stats_user show; | |
| 2062 | + struct ip_vs_kstats show; | |
| 2048 | 2063 | |
| 2049 | 2064 | /* 01234567 01234567 01234567 0123456701234567 0123456701234567 */ |
| 2050 | 2065 | seq_puts(seq, |
| @@ -2053,17 +2068,22 @@ static int ip_vs_stats_show(struct seq_file *seq, void *v) · 2 unmodified lines | ||
| 2053 | 2068 | " Conns Packets Packets Bytes Bytes\n"); |
| 2054 | 2069 | |
| 2055 | 2070 | ip_vs_copy_stats(&show, &net_ipvs(net)->tot_stats); |
| 2056 | − seq_printf(seq, "%8X %8X %8X %16LX %16LX\n\n", show.conns, | |
| 2057 | − show.inpkts, show.outpkts, | |
| 2058 | − (unsigned long long) show.inbytes, | |
| 2059 | − (unsigned long long) show.outbytes); | |
| 2060 | − | |
| 2061 | −/* 01234567 01234567 01234567 0123456701234567 0123456701234567 */ | |
| 2071 | + seq_printf(seq, "%8LX %8LX %8LX %16LX %16LX\n\n", | |
| 2072 | + (unsigned long long)show.conns, | |
| 2073 | + (unsigned long long)show.inpkts, | |
| 2074 | + (unsigned long long)show.outpkts, | |
| 2075 | + (unsigned long long)show.inbytes, | |
| 2076 | + (unsigned long long)show.outbytes); | |
| 2077 | + | |
| 2078 | +/* 01234567 01234567 01234567 0123456701234567 0123456701234567*/ | |
| 2062 | 2079 | seq_puts(seq, |
| 2063 | − " Conns/s Pkts/s Pkts/s Bytes/s Bytes/s\n"); | |
| 2064 | − seq_printf(seq, "%8X %8X %8X %16X %16X\n", | |
| 2065 | − show.cps, show.inpps, show.outpps, | |
| 2066 | − show.inbps, show.outbps); | |
| 2080 | + " Conns/s Pkts/s Pkts/s Bytes/s Bytes/s\n"); | |
| 2081 | + seq_printf(seq, "%8LX %8LX %8LX %16LX %16LX\n", | |
| 2082 | + (unsigned long long)show.cps, | |
| 2083 | + (unsigned long long)show.inpps, | |
| 2084 | + (unsigned long long)show.outpps, | |
| 2085 | + (unsigned long long)show.inbps, | |
| 2086 | + (unsigned long long)show.outbps); | |
| 2067 | 2087 | |
| 2068 | 2088 | return 0; |
| 2069 | 2089 | } |
| @@ -2086,7 +2106,7 @@ static int ip_vs_stats_percpu_show(struct seq_file *seq, void *v) · 16 unmodified lines | ||
| 2086 | 2106 | struct net *net = seq_file_single_net(seq); |
| 2087 | 2107 | struct ip_vs_stats *tot_stats = &net_ipvs(net)->tot_stats; |
| 2088 | 2108 | struct ip_vs_cpu_stats __percpu *cpustats = tot_stats->cpustats; |
| 2089 | − struct ip_vs_stats_user rates; | |
| 2109 | + struct ip_vs_kstats kstats; | |
| 2090 | 2110 | int i; |
| 2091 | 2111 | |
| 2092 | 2112 | /* 01234567 01234567 01234567 0123456701234567 0123456701234567 */ |
| @@ -2098,41 +2118,41 @@ static int ip_vs_stats_percpu_show(struct seq_file *seq, void *v) · 5 unmodified lines | ||
| 2098 | 2118 | for_each_possible_cpu(i) { |
| 2099 | 2119 | struct ip_vs_cpu_stats *u = per_cpu_ptr(cpustats, i); |
| 2100 | 2120 | unsigned int start; |
| 2101 | − __u64 inbytes, outbytes; | |
| 2121 | + u64 conns, inpkts, outpkts, inbytes, outbytes; | |
| 2102 | 2122 | |
| 2103 | 2123 | do { |
| 2104 | 2124 | start = u64_stats_fetch_begin_irq(&u->syncp); |
| 2105 | − inbytes = u->ustats.inbytes; | |
| 2106 | − outbytes = u->ustats.outbytes; | |
| 2125 | + conns = u->cnt.conns; | |
| 2126 | + inpkts = u->cnt.inpkts; | |
| 2127 | + outpkts = u->cnt.outpkts; | |
| 2128 | + inbytes = u->cnt.inbytes; | |
| 2129 | + outbytes = u->cnt.outbytes; | |
| 2107 | 2130 | } while (u64_stats_fetch_retry_irq(&u->syncp, start)); |
| 2108 | 2131 | |
| 2109 | − seq_printf(seq, "%3X %8X %8X %8X %16LX %16LX\n", | |
| 2110 | − i, u->ustats.conns, u->ustats.inpkts, | |
| 2111 | − u->ustats.outpkts, (__u64)inbytes, | |
| 2112 | − (__u64)outbytes); | |
| 2132 | + seq_printf(seq, "%3X %8LX %8LX %8LX %16LX %16LX\n", | |
| 2133 | + i, (u64)conns, (u64)inpkts, | |
| 2134 | + (u64)outpkts, (u64)inbytes, | |
| 2135 | + (u64)outbytes); | |
| 2113 | 2136 | } |
| 2114 | 2137 | |
| 2115 | − spin_lock_bh(&tot_stats->lock); | |
| 2116 | − | |
| 2117 | − seq_printf(seq, " ~ %8X %8X %8X %16LX %16LX\n\n", | |
| 2118 | − tot_stats->ustats.conns, tot_stats->ustats.inpkts, | |
| 2119 | − tot_stats->ustats.outpkts, | |
| 2120 | − (unsigned long long) tot_stats->ustats.inbytes, | |
| 2121 | − (unsigned long long) tot_stats->ustats.outbytes); | |
| 2122 | − | |
| 2123 | − ip_vs_read_estimator(&rates, tot_stats); | |
| 2138 | + ip_vs_copy_stats(&kstats, tot_stats); | |
| 2124 | 2139 | |
| 2125 | − spin_unlock_bh(&tot_stats->lock); | |
| 2140 | + seq_printf(seq, " ~ %8LX %8LX %8LX %16LX %16LX\n\n", | |
| 2141 | + (unsigned long long)kstats.conns, | |
| 2142 | + (unsigned long long)kstats.inpkts, | |
| 2143 | + (unsigned long long)kstats.outpkts, | |
| 2144 | + (unsigned long long)kstats.inbytes, | |
| 2145 | + (unsigned long long)kstats.outbytes); | |
| 2126 | 2146 | |
| 2127 | −/* 01234567 01234567 01234567 0123456701234567 0123456701234567 */ | |
| 2147 | +/* ... 01234567 01234567 01234567 0123456701234567 0123456701234567 */ | |
| 2128 | 2148 | seq_puts(seq, |
| 2129 | − " Conns/s Pkts/s Pkts/s Bytes/s Bytes/s\n"); | |
| 2130 | − seq_printf(seq, " %8X %8X %8X %16X %16X\n", | |
| 2131 | − rates.cps, | |
| 2132 | − rates.inpps, | |
| 2133 | − rates.outpps, | |
| 2134 | − rates.inbps, | |
| 2135 | − rates.outbps); | |
| 2149 | + " Conns/s Pkts/s Pkts/s Bytes/s Bytes/s\n"); | |
| 2150 | + seq_printf(seq, " %8LX %8LX %8LX %16LX %16LX\n", | |
| 2151 | + kstats.cps, | |
| 2152 | + kstats.inpps, | |
| 2153 | + kstats.outpps, | |
| 2154 | + kstats.inbps, | |
| 2155 | + kstats.outbps); | |
| 2136 | 2156 | |
| 2137 | 2157 | return 0; |
| 2138 | 2158 | } |
| @@ -2400,6 +2420,7 @@ static void · 261 unmodified lines | ||
| 2400 | 2420 | ip_vs_copy_service(struct ip_vs_service_entry *dst, struct ip_vs_service *src) |
| 2401 | 2421 | { |
| 2402 | 2422 | struct ip_vs_scheduler *sched; |
| 2423 | + struct ip_vs_kstats kstats; | |
| 2403 | 2424 | |
| 2404 | 2425 | sched = rcu_dereference_protected(src->scheduler, 1); |
| 2405 | 2426 | dst->protocol = src->protocol; |
| @@ -2411,7 +2432,8 @@ ip_vs_copy_service(struct ip_vs_service_entry *dst, struct ip_vs_service *src) · 5 unmodified lines | ||
| 2411 | 2432 | dst->timeout = src->timeout / HZ; |
| 2412 | 2433 | dst->netmask = src->netmask; |
| 2413 | 2434 | dst->num_dests = src->num_dests; |
| 2414 | − ip_vs_copy_stats(&dst->stats, &src->stats); | |
| 2435 | + ip_vs_copy_stats(&kstats, &src->stats); | |
| 2436 | + ip_vs_export_stats_user(&dst->stats, &kstats); | |
| 2415 | 2437 | } |
| 2416 | 2438 | |
| 2417 | 2439 | static inline int |
| @@ -2485,6 +2507,7 @@ __ip_vs_get_dest_entries(struct net *net, const struct ip_vs_get_dests *get, · 67 unmodified lines | ||
| 2485 | 2507 | int count = 0; |
| 2486 | 2508 | struct ip_vs_dest *dest; |
| 2487 | 2509 | struct ip_vs_dest_entry entry; |
| 2510 | + struct ip_vs_kstats kstats; | |
| 2488 | 2511 | |
| 2489 | 2512 | memset(&entry, 0, sizeof(entry)); |
| 2490 | 2513 | list_for_each_entry(dest, &svc->destinations, n_list) { |
| @@ -2506,7 +2529,8 @@ __ip_vs_get_dest_entries(struct net *net, const struct ip_vs_get_dests *get, · 15 unmodified lines | ||
| 2506 | 2529 | entry.activeconns = atomic_read(&dest->activeconns); |
| 2507 | 2530 | entry.inactconns = atomic_read(&dest->inactconns); |
| 2508 | 2531 | entry.persistconns = atomic_read(&dest->persistconns); |
| 2509 | − ip_vs_copy_stats(&entry.stats, &dest->stats); | |
| 2532 | + ip_vs_copy_stats(&kstats, &dest->stats); | |
| 2533 | + ip_vs_export_stats_user(&entry.stats, &kstats); | |
| 2510 | 2534 | if (copy_to_user(&uptr->entrytable[count], |
| 2511 | 2535 | &entry, sizeof(entry))) { |
| 2512 | 2536 | ret = -EFAULT; |
| @@ -2798,25 +2822,51 @@ static const struct nla_policy ip_vs_dest_policy[IPVS_DEST_ATTR_MAX + 1] = { · 285 unmodified lines | ||
| 2798 | 2822 | }; |
| 2799 | 2823 | |
| 2800 | 2824 | static int ip_vs_genl_fill_stats(struct sk_buff *skb, int container_type, |
| 2801 | − struct ip_vs_stats *stats) | |
| 2825 | + struct ip_vs_kstats *kstats) | |
| 2826 | +{ | |
| 2827 | + struct nlattr *nl_stats = nla_nest_start(skb, container_type); | |
| 2828 | + | |
| 2829 | + if (!nl_stats) | |
| 2830 | + return -EMSGSIZE; | |
| 2831 | + | |
| 2832 | + if (nla_put_u32(skb, IPVS_STATS_ATTR_CONNS, (u32)kstats->conns) || | |
| 2833 | + nla_put_u32(skb, IPVS_STATS_ATTR_INPKTS, (u32)kstats->inpkts) || | |
| 2834 | + nla_put_u32(skb, IPVS_STATS_ATTR_OUTPKTS, (u32)kstats->outpkts) || | |
| 2835 | + nla_put_u64(skb, IPVS_STATS_ATTR_INBYTES, kstats->inbytes) || | |
| 2836 | + nla_put_u64(skb, IPVS_STATS_ATTR_OUTBYTES, kstats->outbytes) || | |
| 2837 | + nla_put_u32(skb, IPVS_STATS_ATTR_CPS, (u32)kstats->cps) || | |
| 2838 | + nla_put_u32(skb, IPVS_STATS_ATTR_INPPS, (u32)kstats->inpps) || | |
| 2839 | + nla_put_u32(skb, IPVS_STATS_ATTR_OUTPPS, (u32)kstats->outpps) || | |
| 2840 | + nla_put_u32(skb, IPVS_STATS_ATTR_INBPS, (u32)kstats->inbps) || | |
| 2841 | + nla_put_u32(skb, IPVS_STATS_ATTR_OUTBPS, (u32)kstats->outbps)) | |
| 2842 | + goto nla_put_failure; | |
| 2843 | + nla_nest_end(skb, nl_stats); | |
| 2844 | + | |
| 2845 | + return 0; | |
| 2846 | + | |
| 2847 | +nla_put_failure: | |
| 2848 | + nla_nest_cancel(skb, nl_stats); | |
| 2849 | + return -EMSGSIZE; | |
| 2850 | +} | |
| 2851 | + | |
| 2852 | +static int ip_vs_genl_fill_stats64(struct sk_buff *skb, int container_type, | |
| 2853 | + struct ip_vs_kstats *kstats) | |
| 2802 | 2854 | { |
| 2803 | − struct ip_vs_stats_user ustats; | |
| 2804 | 2855 | struct nlattr *nl_stats = nla_nest_start(skb, container_type); |
| 2856 | + | |
| 2805 | 2857 | if (!nl_stats) |
| 2806 | 2858 | return -EMSGSIZE; |
| 2807 | 2859 | |
| 2808 | − ip_vs_copy_stats(&ustats, stats); | |
| 2809 | − | |
| 2810 | − if (nla_put_u32(skb, IPVS_STATS_ATTR_CONNS, ustats.conns) || | |
| 2811 | − nla_put_u32(skb, IPVS_STATS_ATTR_INPKTS, ustats.inpkts) || | |
| 2812 | − nla_put_u32(skb, IPVS_STATS_ATTR_OUTPKTS, ustats.outpkts) || | |
| 2813 | − nla_put_u64(skb, IPVS_STATS_ATTR_INBYTES, ustats.inbytes) || | |
| 2814 | − nla_put_u64(skb, IPVS_STATS_ATTR_OUTBYTES, ustats.outbytes) || | |
| 2815 | − nla_put_u32(skb, IPVS_STATS_ATTR_CPS, ustats.cps) || | |
| 2816 | − nla_put_u32(skb, IPVS_STATS_ATTR_INPPS, ustats.inpps) || | |
| 2817 | − nla_put_u32(skb, IPVS_STATS_ATTR_OUTPPS, ustats.outpps) || | |
| 2818 | − nla_put_u32(skb, IPVS_STATS_ATTR_INBPS, ustats.inbps) || | |
| 2819 | − nla_put_u32(skb, IPVS_STATS_ATTR_OUTBPS, ustats.outbps)) | |
| 2860 | + if (nla_put_u64(skb, IPVS_STATS_ATTR_CONNS, kstats->conns) || | |
| 2861 | + nla_put_u64(skb, IPVS_STATS_ATTR_INPKTS, kstats->inpkts) || | |
| 2862 | + nla_put_u64(skb, IPVS_STATS_ATTR_OUTPKTS, kstats->outpkts) || | |
| 2863 | + nla_put_u64(skb, IPVS_STATS_ATTR_INBYTES, kstats->inbytes) || | |
| 2864 | + nla_put_u64(skb, IPVS_STATS_ATTR_OUTBYTES, kstats->outbytes) || | |
| 2865 | + nla_put_u64(skb, IPVS_STATS_ATTR_CPS, kstats->cps) || | |
| 2866 | + nla_put_u64(skb, IPVS_STATS_ATTR_INPPS, kstats->inpps) || | |
| 2867 | + nla_put_u64(skb, IPVS_STATS_ATTR_OUTPPS, kstats->outpps) || | |
| 2868 | + nla_put_u64(skb, IPVS_STATS_ATTR_INBPS, kstats->inbps) || | |
| 2869 | + nla_put_u64(skb, IPVS_STATS_ATTR_OUTBPS, kstats->outbps)) | |
| 2820 | 2870 | goto nla_put_failure; |
| 2821 | 2871 | nla_nest_end(skb, nl_stats); |
| 2822 | 2872 | |
| @@ -2835,6 +2885,7 @@ static int ip_vs_genl_fill_service(struct sk_buff *skb, · 12 unmodified lines | ||
| 2835 | 2885 | struct nlattr *nl_service; |
| 2836 | 2886 | struct ip_vs_flags flags = { .flags = svc->flags, |
| 2837 | 2887 | .mask = ~0 }; |
| 2888 | + struct ip_vs_kstats kstats; | |
| 2838 | 2889 | |
| 2839 | 2890 | nl_service = nla_nest_start(skb, IPVS_CMD_ATTR_SERVICE); |
| 2840 | 2891 | if (!nl_service) |
| @@ -2860,7 +2911,10 @@ static int ip_vs_genl_fill_service(struct sk_buff *skb, · 19 unmodified lines | ||
| 2860 | 2911 | nla_put_u32(skb, IPVS_SVC_ATTR_TIMEOUT, svc->timeout / HZ) || |
| 2861 | 2912 | nla_put_be32(skb, IPVS_SVC_ATTR_NETMASK, svc->netmask)) |
| 2862 | 2913 | goto nla_put_failure; |
| 2863 | − if (ip_vs_genl_fill_stats(skb, IPVS_SVC_ATTR_STATS, &svc->stats)) | |
| 2914 | + ip_vs_copy_stats(&kstats, &svc->stats); | |
| 2915 | + if (ip_vs_genl_fill_stats(skb, IPVS_SVC_ATTR_STATS, &kstats)) | |
| 2916 | + goto nla_put_failure; | |
| 2917 | + if (ip_vs_genl_fill_stats64(skb, IPVS_SVC_ATTR_STATS64, &kstats)) | |
| 2864 | 2918 | goto nla_put_failure; |
| 2865 | 2919 | |
| 2866 | 2920 | nla_nest_end(skb, nl_service); |
| @@ -3032,6 +3086,7 @@ static struct ip_vs_service *ip_vs_genl_find_service(struct net *net, · 165 unmodified lines | ||
| 3032 | 3086 | static int ip_vs_genl_fill_dest(struct sk_buff *skb, struct ip_vs_dest *dest) |
| 3033 | 3087 | { |
| 3034 | 3088 | struct nlattr *nl_dest; |
| 3089 | + struct ip_vs_kstats kstats; | |
| 3035 | 3090 | |
| 3036 | 3091 | nl_dest = nla_nest_start(skb, IPVS_CMD_ATTR_DEST); |
| 3037 | 3092 | if (!nl_dest) |
| @@ -3054,7 +3109,10 @@ static int ip_vs_genl_fill_dest(struct sk_buff *skb, struct ip_vs_dest *dest) · 16 unmodified lines | ||
| 3054 | 3109 | atomic_read(&dest->persistconns)) || |
| 3055 | 3110 | nla_put_u16(skb, IPVS_DEST_ATTR_ADDR_FAMILY, dest->af)) |
| 3056 | 3111 | goto nla_put_failure; |
| 3057 | − if (ip_vs_genl_fill_stats(skb, IPVS_DEST_ATTR_STATS, &dest->stats)) | |
| 3112 | + ip_vs_copy_stats(&kstats, &dest->stats); | |
| 3113 | + if (ip_vs_genl_fill_stats(skb, IPVS_DEST_ATTR_STATS, &kstats)) | |
| 3114 | + goto nla_put_failure; | |
| 3115 | + if (ip_vs_genl_fill_stats64(skb, IPVS_DEST_ATTR_STATS64, &kstats)) | |
| 3058 | 3116 | goto nla_put_failure; |
| 3059 | 3117 | |
| 3060 | 3118 | nla_nest_end(skb, nl_dest); |
| @@ -45,17 +45,19 @@ · 44 unmodified lines | ||
| 45 | 45 | |
| 46 | 46 | NOTES. |
| 47 | 47 | |
| 48 | − * The stored value for average bps is scaled by 2^5, so that maximal | |
| 49 | − rate is ~2.15Gbits/s, average pps and cps are scaled by 2^10. | |
| 48 | + * Average bps is scaled by 2^5, while average pps and cps are scaled by 2^10. | |
| 50 | 49 | |
| 51 | − * A lot code is taken from net/sched/estimator.c | |
| 50 | + * Netlink users can see 64-bit values but sockopt users are restricted | |
| 51 | + to 32-bit values for conns, packets, bps, cps and pps. | |
| 52 | + | |
| 53 | + * A lot of code is taken from net/core/gen_estimator.c | |
| 52 | 54 | */ |
| 53 | 55 | |
| 54 | 56 | |
| 55 | 57 | /* |
| 56 | 58 | * Make a summary from each cpu |
| 57 | 59 | */ |
| 58 | −static void ip_vs_read_cpu_stats(struct ip_vs_stats_user *sum, | |
| 60 | +static void ip_vs_read_cpu_stats(struct ip_vs_kstats *sum, | |
| 59 | 61 | struct ip_vs_cpu_stats __percpu *stats) |
| 60 | 62 | { |
| 61 | 63 | int i; |
| @@ -64,27 +66,31 @@ static void ip_vs_read_cpu_stats(struct ip_vs_stats_user *sum, · 2 unmodified lines | ||
| 64 | 66 | for_each_possible_cpu(i) { |
| 65 | 67 | struct ip_vs_cpu_stats *s = per_cpu_ptr(stats, i); |
| 66 | 68 | unsigned int start; |
| 67 | − __u64 inbytes, outbytes; | |
| 69 | + u64 conns, inpkts, outpkts, inbytes, outbytes; | |
| 70 | + | |
| 68 | 71 | if (add) { |
| 69 | − sum->conns += s->ustats.conns; | |
| 70 | − sum->inpkts += s->ustats.inpkts; | |
| 71 | − sum->outpkts += s->ustats.outpkts; | |
| 72 | 72 | do { |
| 73 | 73 | start = u64_stats_fetch_begin(&s->syncp); |
| 74 | − inbytes = s->ustats.inbytes; | |
| 75 | − outbytes = s->ustats.outbytes; | |
| 74 | + conns = s->cnt.conns; | |
| 75 | + inpkts = s->cnt.inpkts; | |
| 76 | + outpkts = s->cnt.outpkts; | |
| 77 | + inbytes = s->cnt.inbytes; | |
| 78 | + outbytes = s->cnt.outbytes; | |
| 76 | 79 | } while (u64_stats_fetch_retry(&s->syncp, start)); |
| 80 | + sum->conns += conns; | |
| 81 | + sum->inpkts += inpkts; | |
| 82 | + sum->outpkts += outpkts; | |
| 77 | 83 | sum->inbytes += inbytes; |
| 78 | 84 | sum->outbytes += outbytes; |
| 79 | 85 | } else { |
| 80 | 86 | add = true; |
| 81 | − sum->conns = s->ustats.conns; | |
| 82 | − sum->inpkts = s->ustats.inpkts; | |
| 83 | − sum->outpkts = s->ustats.outpkts; | |
| 84 | 87 | do { |
| 85 | 88 | start = u64_stats_fetch_begin(&s->syncp); |
| 86 | − sum->inbytes = s->ustats.inbytes; | |
| 87 | − sum->outbytes = s->ustats.outbytes; | |
| 89 | + sum->conns = s->cnt.conns; | |
| 90 | + sum->inpkts = s->cnt.inpkts; | |
| 91 | + sum->outpkts = s->cnt.outpkts; | |
| 92 | + sum->inbytes = s->cnt.inbytes; | |
| 93 | + sum->outbytes = s->cnt.outbytes; | |
| 88 | 94 | } while (u64_stats_fetch_retry(&s->syncp, start)); |
| 89 | 95 | } |
| 90 | 96 | } |
| @@ -95,10 +101,7 @@ static void estimation_timer(unsigned long arg) · 4 unmodified lines | ||
| 95 | 101 | { |
| 96 | 102 | struct ip_vs_estimator *e; |
| 97 | 103 | struct ip_vs_stats *s; |
| 98 | − u32 n_conns; | |
| 99 | − u32 n_inpkts, n_outpkts; | |
| 100 | − u64 n_inbytes, n_outbytes; | |
| 101 | − u32 rate; | |
| 104 | + u64 rate; | |
| 102 | 105 | struct net *net = (struct net *)arg; |
| 103 | 106 | struct netns_ipvs *ipvs; |
| 104 | 107 | |
| @@ -108,33 +111,29 @@ static void estimation_timer(unsigned long arg) · 3 unmodified lines | ||
| 108 | 111 | s = container_of(e, struct ip_vs_stats, est); |
| 109 | 112 | |
| 110 | 113 | spin_lock(&s->lock); |
| 111 | − ip_vs_read_cpu_stats(&s->ustats, s->cpustats); | |
| 112 | − n_conns = s->ustats.conns; | |
| 113 | − n_inpkts = s->ustats.inpkts; | |
| 114 | − n_outpkts = s->ustats.outpkts; | |
| 115 | − n_inbytes = s->ustats.inbytes; | |
| 116 | − n_outbytes = s->ustats.outbytes; | |
| 114 | + ip_vs_read_cpu_stats(&s->kstats, s->cpustats); | |
| 117 | 115 | |
| 118 | 116 | /* scaled by 2^10, but divided 2 seconds */ |
| 119 | − rate = (n_conns - e->last_conns) << 9; | |
| 120 | − e->last_conns = n_conns; | |
| 121 | − e->cps += ((long)rate - (long)e->cps) >> 2; | |
| 122 | − | |
| 123 | − rate = (n_inpkts - e->last_inpkts) << 9; | |
| 124 | − e->last_inpkts = n_inpkts; | |
| 125 | − e->inpps += ((long)rate - (long)e->inpps) >> 2; | |
| 126 | − | |
| 127 | − rate = (n_outpkts - e->last_outpkts) << 9; | |
| 128 | − e->last_outpkts = n_outpkts; | |
| 129 | − e->outpps += ((long)rate - (long)e->outpps) >> 2; | |
| 130 | − | |
| 131 | − rate = (n_inbytes - e->last_inbytes) << 4; | |
| 132 | − e->last_inbytes = n_inbytes; | |
| 133 | − e->inbps += ((long)rate - (long)e->inbps) >> 2; | |
| 134 | − | |
| 135 | − rate = (n_outbytes - e->last_outbytes) << 4; | |
| 136 | − e->last_outbytes = n_outbytes; | |
| 137 | − e->outbps += ((long)rate - (long)e->outbps) >> 2; | |
| 117 | + rate = (s->kstats.conns - e->last_conns) << 9; | |
| 118 | + e->last_conns = s->kstats.conns; | |
| 119 | + e->cps += ((s64)rate - (s64)e->cps) >> 2; | |
| 120 | + | |
| 121 | + rate = (s->kstats.inpkts - e->last_inpkts) << 9; | |
| 122 | + e->last_inpkts = s->kstats.inpkts; | |
| 123 | + e->inpps += ((s64)rate - (s64)e->inpps) >> 2; | |
| 124 | + | |
| 125 | + rate = (s->kstats.outpkts - e->last_outpkts) << 9; | |
| 126 | + e->last_outpkts = s->kstats.outpkts; | |
| 127 | + e->outpps += ((s64)rate - (s64)e->outpps) >> 2; | |
| 128 | + | |
| 129 | + /* scaled by 2^5, but divided 2 seconds */ | |
| 130 | + rate = (s->kstats.inbytes - e->last_inbytes) << 4; | |
| 131 | + e->last_inbytes = s->kstats.inbytes; | |
| 132 | + e->inbps += ((s64)rate - (s64)e->inbps) >> 2; | |
| 133 | + | |
| 134 | + rate = (s->kstats.outbytes - e->last_outbytes) << 4; | |
| 135 | + e->last_outbytes = s->kstats.outbytes; | |
| 136 | + e->outbps += ((s64)rate - (s64)e->outbps) >> 2; | |
| 138 | 137 | spin_unlock(&s->lock); |
| 139 | 138 | } |
| 140 | 139 | spin_unlock(&ipvs->est_lock); |
| @@ -166,14 +165,14 @@ void ip_vs_stop_estimator(struct net *net, struct ip_vs_stats *stats) · 25 unmodified lines | ||
| 166 | 165 | void ip_vs_zero_estimator(struct ip_vs_stats *stats) |
| 167 | 166 | { |
| 168 | 167 | struct ip_vs_estimator *est = &stats->est; |
| 169 | − struct ip_vs_stats_user *u = &stats->ustats; | |
| 168 | + struct ip_vs_kstats *k = &stats->kstats; | |
| 170 | 169 | |
| 171 | 170 | /* reset counters, caller must hold the stats->lock lock */ |
| 172 | − est->last_inbytes = u->inbytes; | |
| 173 | − est->last_outbytes = u->outbytes; | |
| 174 | − est->last_conns = u->conns; | |
| 175 | − est->last_inpkts = u->inpkts; | |
| 176 | − est->last_outpkts = u->outpkts; | |
| 171 | + est->last_inbytes = k->inbytes; | |
| 172 | + est->last_outbytes = k->outbytes; | |
| 173 | + est->last_conns = k->conns; | |
| 174 | + est->last_inpkts = k->inpkts; | |
| 175 | + est->last_outpkts = k->outpkts; | |
| 177 | 176 | est->cps = 0; |
| 178 | 177 | est->inpps = 0; |
| 179 | 178 | est->outpps = 0; |
| @@ -182,8 +181,7 @@ void ip_vs_zero_estimator(struct ip_vs_stats *stats) · 2 unmodified lines | ||
| 182 | 181 | } |
| 183 | 182 | |
| 184 | 183 | /* Get decoded rates */ |
| 185 | −void ip_vs_read_estimator(struct ip_vs_stats_user *dst, | |
| 186 | − struct ip_vs_stats *stats) | |
| 184 | +void ip_vs_read_estimator(struct ip_vs_kstats *dst, struct ip_vs_stats *stats) | |
| 187 | 185 | { |
| 188 | 186 | struct ip_vs_estimator *e = &stats->est; |
| 189 | 187 | |