| /* |
| * Copyright (c) 2006 Oracle. All rights reserved. |
| * |
| * This software is available to you under a choice of one of two |
| * licenses. You may choose to be licensed under the terms of the GNU |
| * General Public License (GPL) Version 2, available from the file |
| * COPYING in the main directory of this source tree, or the |
| * OpenIB.org BSD license below: |
| * |
| * Redistribution and use in source and binary forms, with or |
| * without modification, are permitted provided that the following |
| * conditions are met: |
| * |
| * - Redistributions of source code must retain the above |
| * copyright notice, this list of conditions and the following |
| * disclaimer. |
| * |
| * - Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following |
| * disclaimer in the documentation and/or other materials |
| * provided with the distribution. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
| * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
| * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
| * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS |
| * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN |
| * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
| * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
| * SOFTWARE. |
| * |
| */ |
| #include <linux/percpu.h> |
| #include <linux/seq_file.h> |
| #include <linux/proc_fs.h> |
| #include <linux/export.h> |
| |
| #include "rds.h" |
| |
| DEFINE_PER_CPU_SHARED_ALIGNED(struct rds_statistics, rds_stats); |
| EXPORT_PER_CPU_SYMBOL_GPL(rds_stats); |
| |
| /* :.,$s/unsigned long\>.*\<s_\(.*\);/"\1",/g */ |
| |
| static const char *const rds_stat_names[] = { |
| "conn_reset", |
| "recv_drop_bad_checksum", |
| "recv_drop_old_seq", |
| "recv_drop_no_sock", |
| "recv_drop_dead_sock", |
| "recv_deliver_raced", |
| "recv_delivered", |
| "recv_queued", |
| "recv_immediate_retry", |
| "recv_delayed_retry", |
| "recv_ack_required", |
| "recv_rdma_bytes", |
| "recv_ping", |
| "send_queue_empty", |
| "send_queue_full", |
| "send_lock_contention", |
| "send_lock_queue_raced", |
| "send_immediate_retry", |
| "send_delayed_retry", |
| "send_drop_acked", |
| "send_ack_required", |
| "send_queued", |
| "send_rdma", |
| "send_rdma_bytes", |
| "send_pong", |
| "page_remainder_hit", |
| "page_remainder_miss", |
| "copy_to_user", |
| "copy_from_user", |
| "cong_update_queued", |
| "cong_update_received", |
| "cong_send_error", |
| "cong_send_blocked", |
| "recv_bytes_added_to_sock", |
| "recv_bytes_freed_fromsock", |
| "send_stuck_rm", |
| }; |
| |
| void rds_stats_info_copy(struct rds_info_iterator *iter, |
| uint64_t *values, const char *const *names, size_t nr) |
| { |
| struct rds_info_counter ctr; |
| size_t i; |
| |
| for (i = 0; i < nr; i++) { |
| BUG_ON(strlen(names[i]) >= sizeof(ctr.name)); |
| strncpy(ctr.name, names[i], sizeof(ctr.name) - 1); |
| ctr.name[sizeof(ctr.name) - 1] = '\0'; |
| ctr.value = values[i]; |
| |
| rds_info_copy(iter, &ctr, sizeof(ctr)); |
| } |
| } |
| EXPORT_SYMBOL_GPL(rds_stats_info_copy); |
| |
| /* |
| * This gives global counters across all the transports. The strings |
| * are copied in so that the tool doesn't need knowledge of the specific |
| * stats that we're exporting. Some are pretty implementation dependent |
| * and may change over time. That doesn't stop them from being useful. |
| * |
| * This is the only function in the chain that knows about the byte granular |
| * length in userspace. It converts it to number of stat entries that the |
| * rest of the functions operate in. |
| */ |
| static void rds_stats_info(struct socket *sock, unsigned int len, |
| struct rds_info_iterator *iter, |
| struct rds_info_lengths *lens) |
| { |
| struct rds_statistics stats = {0, }; |
| uint64_t *src; |
| uint64_t *sum; |
| size_t i; |
| int cpu; |
| unsigned int avail; |
| |
| avail = len / sizeof(struct rds_info_counter); |
| |
| if (avail < ARRAY_SIZE(rds_stat_names)) { |
| avail = 0; |
| goto trans; |
| } |
| |
| for_each_online_cpu(cpu) { |
| src = (uint64_t *)&(per_cpu(rds_stats, cpu)); |
| sum = (uint64_t *)&stats; |
| for (i = 0; i < sizeof(stats) / sizeof(uint64_t); i++) |
| *(sum++) += *(src++); |
| } |
| |
| rds_stats_info_copy(iter, (uint64_t *)&stats, rds_stat_names, |
| ARRAY_SIZE(rds_stat_names)); |
| avail -= ARRAY_SIZE(rds_stat_names); |
| |
| trans: |
| lens->each = sizeof(struct rds_info_counter); |
| lens->nr = rds_trans_stats_info_copy(iter, avail) + |
| ARRAY_SIZE(rds_stat_names); |
| } |
| |
| void rds_stats_exit(void) |
| { |
| rds_info_deregister_func(RDS_INFO_COUNTERS, rds_stats_info); |
| } |
| |
| int rds_stats_init(void) |
| { |
| rds_info_register_func(RDS_INFO_COUNTERS, rds_stats_info); |
| return 0; |
| } |