sdp_proc.c revision 331769
1/* 2 * Copyright (c) 2008 Mellanox Technologies Ltd. All rights reserved. 3 * 4 * This software is available to you under a choice of one of two 5 * licenses. You may choose to be licensed under the terms of the GNU 6 * General Public License (GPL) Version 2, available from the file 7 * COPYING in the main directory of this source tree, or the 8 * OpenIB.org BSD license below: 9 * 10 * Redistribution and use in source and binary forms, with or 11 * without modification, are permitted provided that the following 12 * conditions are met: 13 * 14 * - Redistributions of source code must retain the above 15 * copyright notice, this list of conditions and the following 16 * disclaimer. 17 * 18 * - Redistributions in binary form must reproduce the above 19 * copyright notice, this list of conditions and the following 20 * disclaimer in the documentation and/or other materials 21 * provided with the distribution. 22 * 23 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 24 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 25 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 26 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 27 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 28 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 29 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 30 * SOFTWARE. 31 */ 32 33#include <linux/proc_fs.h> 34#include "sdp.h" 35 36#ifdef CONFIG_PROC_FS 37 38#define PROC_SDP_STATS "sdpstats" 39#define PROC_SDP_PERF "sdpprf" 40 41/* just like TCP fs */ 42struct sdp_seq_afinfo { 43 struct module *owner; 44 char *name; 45 sa_family_t family; 46 int (*seq_show) (struct seq_file *m, void *v); 47 struct file_operations *seq_fops; 48}; 49 50struct sdp_iter_state { 51 sa_family_t family; 52 int num; 53 struct seq_operations seq_ops; 54}; 55 56static void *sdp_get_idx(struct seq_file *seq, loff_t pos) 57{ 58 int i = 0; 59 struct sdp_sock *ssk; 60 61 if (!list_empty(&sock_list)) 62 list_for_each_entry(ssk, &sock_list, sock_list) { 63 if (i == pos) 64 return ssk; 65 i++; 66 } 67 68 return NULL; 69} 70 71static void *sdp_seq_start(struct seq_file *seq, loff_t *pos) 72{ 73 void *start = NULL; 74 struct sdp_iter_state *st = seq->private; 75 76 st->num = 0; 77 78 if (!*pos) 79 return SEQ_START_TOKEN; 80 81 spin_lock_irq(&sock_list_lock); 82 start = sdp_get_idx(seq, *pos - 1); 83 if (start) 84 sock_hold((struct socket *)start, SOCK_REF_SEQ); 85 spin_unlock_irq(&sock_list_lock); 86 87 return start; 88} 89 90static void *sdp_seq_next(struct seq_file *seq, void *v, loff_t *pos) 91{ 92 struct sdp_iter_state *st = seq->private; 93 void *next = NULL; 94 95 spin_lock_irq(&sock_list_lock); 96 if (v == SEQ_START_TOKEN) 97 next = sdp_get_idx(seq, 0); 98 else 99 next = sdp_get_idx(seq, *pos); 100 if (next) 101 sock_hold((struct socket *)next, SOCK_REF_SEQ); 102 spin_unlock_irq(&sock_list_lock); 103 104 *pos += 1; 105 st->num++; 106 107 return next; 108} 109 110static void sdp_seq_stop(struct seq_file *seq, void *v) 111{ 112} 113 114#define TMPSZ 150 115 116static int sdp_seq_show(struct seq_file *seq, void *v) 117{ 118 struct sdp_iter_state *st; 119 struct socket *sk = v; 120 char tmpbuf[TMPSZ + 1]; 121 unsigned int dest; 122 unsigned int src; 123 int uid; 124 unsigned long inode; 125 __u16 destp; 126 __u16 srcp; 127 __u32 rx_queue, tx_queue; 128 129 if (v == SEQ_START_TOKEN) { 130 seq_printf(seq, "%-*s\n", TMPSZ - 1, 131 " sl local_address rem_address " 132 "uid inode rx_queue tx_queue state"); 133 goto out; 134 } 135 136 st = seq->private; 137 138 dest = inet_sk(sk)->daddr; 139 src = inet_sk(sk)->rcv_saddr; 140 destp = ntohs(inet_sk(sk)->dport); 141 srcp = ntohs(inet_sk(sk)->sport); 142 uid = sock_i_uid(sk); 143 inode = sock_i_ino(sk); 144 rx_queue = rcv_nxt(sdp_sk(sk)) - sdp_sk(sk)->copied_seq; 145 tx_queue = sdp_sk(sk)->write_seq - sdp_sk(sk)->tx_ring.una_seq; 146 147 sprintf(tmpbuf, "%4d: %08X:%04X %08X:%04X %5d %lu %08X:%08X %X", 148 st->num, src, srcp, dest, destp, uid, inode, 149 rx_queue, tx_queue, sk->sk_state); 150 151 seq_printf(seq, "%-*s\n", TMPSZ - 1, tmpbuf); 152 153 sock_put(sk, SOCK_REF_SEQ); 154out: 155 return 0; 156} 157 158static int sdp_seq_open(struct inode *inode, struct file *file) 159{ 160 struct sdp_seq_afinfo *afinfo = PDE(inode)->data; 161 struct seq_file *seq; 162 struct sdp_iter_state *s; 163 int rc; 164 165 if (unlikely(afinfo == NULL)) 166 return -EINVAL; 167 168/* Workaround bogus warning by memtrack */ 169#define _kzalloc(size,flags) kzalloc(size,flags) 170#undef kzalloc 171 s = kzalloc(sizeof(*s), GFP_KERNEL); 172#define kzalloc(s,f) _kzalloc(s,f) 173 if (!s) 174 return -ENOMEM; 175 s->family = afinfo->family; 176 s->seq_ops.start = sdp_seq_start; 177 s->seq_ops.next = sdp_seq_next; 178 s->seq_ops.show = afinfo->seq_show; 179 s->seq_ops.stop = sdp_seq_stop; 180 181 rc = seq_open(file, &s->seq_ops); 182 if (rc) 183 goto out_kfree; 184 seq = file->private_data; 185 seq->private = s; 186out: 187 return rc; 188out_kfree: 189 kfree(s); 190 goto out; 191} 192 193 194static struct file_operations sdp_seq_fops; 195static struct sdp_seq_afinfo sdp_seq_afinfo = { 196 .owner = THIS_MODULE, 197 .name = "sdp", 198 .family = AF_INET_SDP, 199 .seq_show = sdp_seq_show, 200 .seq_fops = &sdp_seq_fops, 201}; 202 203#ifdef SDPSTATS_ON 204DEFINE_PER_CPU(struct sdpstats, sdpstats); 205 206static void sdpstats_seq_hist(struct seq_file *seq, char *str, u32 *h, int n, 207 int is_log) 208{ 209 int i; 210 u32 max = 0; 211 212 seq_printf(seq, "%s:\n", str); 213 214 for (i = 0; i < n; i++) { 215 if (h[i] > max) 216 max = h[i]; 217 } 218 219 if (max == 0) { 220 seq_printf(seq, " - all values are 0\n"); 221 return; 222 } 223 224 for (i = 0; i < n; i++) { 225 char s[51]; 226 int j = 50 * h[i] / max; 227 int val = is_log ? (i == n-1 ? 0 : 1<<i) : i; 228 memset(s, '*', j); 229 s[j] = '\0'; 230 231 seq_printf(seq, "%10d | %-50s - %d\n", val, s, h[i]); 232 } 233} 234 235#define SDPSTATS_COUNTER_GET(var) ({ \ 236 u32 __val = 0; \ 237 unsigned int __i; \ 238 for_each_possible_cpu(__i) \ 239 __val += per_cpu(sdpstats, __i).var; \ 240 __val; \ 241}) 242 243#define SDPSTATS_HIST_GET(hist, hist_len, sum) ({ \ 244 unsigned int __i; \ 245 for_each_possible_cpu(__i) { \ 246 unsigned int __j; \ 247 u32 *h = per_cpu(sdpstats, __i).hist; \ 248 for (__j = 0; __j < hist_len; __j++) { \ 249 sum[__j] += h[__j]; \ 250 } \ 251 } \ 252}) 253 254#define __sdpstats_seq_hist(seq, msg, hist, is_log) ({ \ 255 u32 tmp_hist[SDPSTATS_MAX_HIST_SIZE]; \ 256 int hist_len = ARRAY_SIZE(__get_cpu_var(sdpstats).hist);\ 257 memset(tmp_hist, 0, sizeof(tmp_hist)); \ 258 SDPSTATS_HIST_GET(hist, hist_len, tmp_hist); \ 259 sdpstats_seq_hist(seq, msg, tmp_hist, hist_len, is_log);\ 260}) 261 262static int sdpstats_seq_show(struct seq_file *seq, void *v) 263{ 264 int i; 265 266 seq_printf(seq, "SDP statistics:\n"); 267 268 __sdpstats_seq_hist(seq, "sendmsg_seglen", sendmsg_seglen, 1); 269 __sdpstats_seq_hist(seq, "send_size", send_size, 1); 270 __sdpstats_seq_hist(seq, "credits_before_update", 271 credits_before_update, 0); 272 273 seq_printf(seq, "sdp_sendmsg() calls\t\t: %d\n", 274 SDPSTATS_COUNTER_GET(sendmsg)); 275 seq_printf(seq, "bcopy segments \t\t: %d\n", 276 SDPSTATS_COUNTER_GET(sendmsg_bcopy_segment)); 277 seq_printf(seq, "bzcopy segments \t\t: %d\n", 278 SDPSTATS_COUNTER_GET(sendmsg_bzcopy_segment)); 279 seq_printf(seq, "zcopy segments \t\t: %d\n", 280 SDPSTATS_COUNTER_GET(sendmsg_zcopy_segment)); 281 seq_printf(seq, "post_send_credits \t\t: %d\n", 282 SDPSTATS_COUNTER_GET(post_send_credits)); 283 seq_printf(seq, "memcpy_count \t\t: %u\n", 284 SDPSTATS_COUNTER_GET(memcpy_count)); 285 286 for (i = 0; i < ARRAY_SIZE(__get_cpu_var(sdpstats).post_send); i++) { 287 if (mid2str(i)) { 288 seq_printf(seq, "post_send %-20s\t: %d\n", 289 mid2str(i), 290 SDPSTATS_COUNTER_GET(post_send[i])); 291 } 292 } 293 294 seq_printf(seq, "\n"); 295 seq_printf(seq, "post_recv \t\t: %d\n", 296 SDPSTATS_COUNTER_GET(post_recv)); 297 seq_printf(seq, "BZCopy poll miss \t\t: %d\n", 298 SDPSTATS_COUNTER_GET(bzcopy_poll_miss)); 299 seq_printf(seq, "send_wait_for_mem \t\t: %d\n", 300 SDPSTATS_COUNTER_GET(send_wait_for_mem)); 301 seq_printf(seq, "send_miss_no_credits\t\t: %d\n", 302 SDPSTATS_COUNTER_GET(send_miss_no_credits)); 303 304 seq_printf(seq, "rx_poll_miss \t\t: %d\n", SDPSTATS_COUNTER_GET(rx_poll_miss)); 305 seq_printf(seq, "tx_poll_miss \t\t: %d\n", SDPSTATS_COUNTER_GET(tx_poll_miss)); 306 seq_printf(seq, "tx_poll_busy \t\t: %d\n", SDPSTATS_COUNTER_GET(tx_poll_busy)); 307 seq_printf(seq, "tx_poll_hit \t\t: %d\n", SDPSTATS_COUNTER_GET(tx_poll_hit)); 308 309 seq_printf(seq, "CQ stats:\n"); 310 seq_printf(seq, "- RX interrupts\t\t: %d\n", SDPSTATS_COUNTER_GET(rx_int_count)); 311 seq_printf(seq, "- TX interrupts\t\t: %d\n", SDPSTATS_COUNTER_GET(tx_int_count)); 312 313 seq_printf(seq, "ZCopy stats:\n"); 314 seq_printf(seq, "- TX timeout\t\t: %d\n", SDPSTATS_COUNTER_GET(zcopy_tx_timeout)); 315 seq_printf(seq, "- TX cross send\t\t: %d\n", SDPSTATS_COUNTER_GET(zcopy_cross_send)); 316 seq_printf(seq, "- TX aborted by peer\t: %d\n", SDPSTATS_COUNTER_GET(zcopy_tx_aborted)); 317 seq_printf(seq, "- TX error\t\t: %d\n", SDPSTATS_COUNTER_GET(zcopy_tx_error)); 318 return 0; 319} 320 321static ssize_t sdpstats_write(struct file *file, const char __user *buf, 322 size_t count, loff_t *offs) 323{ 324 int i; 325 326 for_each_possible_cpu(i) 327 memset(&per_cpu(sdpstats, i), 0, sizeof(struct sdpstats)); 328 printk(KERN_WARNING "Cleared sdp statistics\n"); 329 330 return count; 331} 332 333static int sdpstats_seq_open(struct inode *inode, struct file *file) 334{ 335 return single_open(file, sdpstats_seq_show, NULL); 336} 337 338static struct file_operations sdpstats_fops = { 339 .owner = THIS_MODULE, 340 .open = sdpstats_seq_open, 341 .read = seq_read, 342 .write = sdpstats_write, 343 .llseek = seq_lseek, 344 .release = single_release, 345}; 346 347#endif 348 349#ifdef SDP_PROFILING 350struct sdpprf_log sdpprf_log[SDPPRF_LOG_SIZE]; 351int sdpprf_log_count; 352 353static unsigned long long start_t; 354 355static int sdpprf_show(struct seq_file *m, void *v) 356{ 357 struct sdpprf_log *l = v; 358 unsigned long nsec_rem, t; 359 360 if (!sdpprf_log_count) { 361 seq_printf(m, "No performance logs\n"); 362 goto out; 363 } 364 365 t = l->time - start_t; 366 nsec_rem = do_div(t, 1000000000); 367 368 seq_printf(m, "%-6d: [%5lu.%06lu] %-50s - [%d{%d} %d:%d] " 369 "mb: %p %s:%d\n", 370 l->idx, (unsigned long)t, nsec_rem/1000, 371 l->msg, l->pid, l->cpu, l->sk_num, l->sk_dport, 372 l->mb, l->func, l->line); 373out: 374 return 0; 375} 376 377static void *sdpprf_start(struct seq_file *p, loff_t *pos) 378{ 379 int idx = *pos; 380 381 if (!*pos) { 382 if (!sdpprf_log_count) 383 return SEQ_START_TOKEN; 384 } 385 386 if (*pos >= MIN(sdpprf_log_count, SDPPRF_LOG_SIZE - 1)) 387 return NULL; 388 389 if (sdpprf_log_count >= SDPPRF_LOG_SIZE - 1) { 390 int off = sdpprf_log_count & (SDPPRF_LOG_SIZE - 1); 391 idx = (idx + off) & (SDPPRF_LOG_SIZE - 1); 392 393 } 394 395 if (!start_t) 396 start_t = sdpprf_log[idx].time; 397 return &sdpprf_log[idx]; 398} 399 400static void *sdpprf_next(struct seq_file *p, void *v, loff_t *pos) 401{ 402 struct sdpprf_log *l = v; 403 404 if (++*pos >= MIN(sdpprf_log_count, SDPPRF_LOG_SIZE - 1)) 405 return NULL; 406 407 ++l; 408 if (l - &sdpprf_log[0] >= SDPPRF_LOG_SIZE - 1) 409 return &sdpprf_log[0]; 410 411 return l; 412} 413 414static void sdpprf_stop(struct seq_file *p, void *v) 415{ 416} 417 418static struct seq_operations sdpprf_ops = { 419 .start = sdpprf_start, 420 .stop = sdpprf_stop, 421 .next = sdpprf_next, 422 .show = sdpprf_show, 423}; 424 425static int sdpprf_open(struct inode *inode, struct file *file) 426{ 427 int res; 428 429 res = seq_open(file, &sdpprf_ops); 430 431 return res; 432} 433 434static ssize_t sdpprf_write(struct file *file, const char __user *buf, 435 size_t count, loff_t *offs) 436{ 437 sdpprf_log_count = 0; 438 printk(KERN_INFO "Cleared sdpprf statistics\n"); 439 440 return count; 441} 442 443static struct file_operations sdpprf_fops = { 444 .open = sdpprf_open, 445 .read = seq_read, 446 .llseek = seq_lseek, 447 .release = seq_release, 448 .write = sdpprf_write, 449}; 450#endif /* SDP_PROFILING */ 451 452int __init sdp_proc_init(void) 453{ 454 struct proc_dir_entry *p = NULL; 455#ifdef SDPSTATS_ON 456 struct proc_dir_entry *stats = NULL; 457#endif 458#ifdef SDP_PROFILING 459 struct proc_dir_entry *prof = NULL; 460#endif 461 462 sdp_seq_afinfo.seq_fops->owner = sdp_seq_afinfo.owner; 463 sdp_seq_afinfo.seq_fops->open = sdp_seq_open; 464 sdp_seq_afinfo.seq_fops->read = seq_read; 465 sdp_seq_afinfo.seq_fops->llseek = seq_lseek; 466 sdp_seq_afinfo.seq_fops->release = seq_release_private; 467 468 p = proc_net_fops_create(&init_net, sdp_seq_afinfo.name, S_IRUGO, 469 sdp_seq_afinfo.seq_fops); 470 if (p) 471 p->data = &sdp_seq_afinfo; 472 else 473 goto no_mem; 474 475#ifdef SDPSTATS_ON 476 477 stats = proc_net_fops_create(&init_net, PROC_SDP_STATS, 478 S_IRUGO | S_IWUGO, &sdpstats_fops); 479 if (!stats) 480 goto no_mem_stats; 481 482#endif 483 484#ifdef SDP_PROFILING 485 prof = proc_net_fops_create(&init_net, PROC_SDP_PERF, 486 S_IRUGO | S_IWUGO, &sdpprf_fops); 487 if (!prof) 488 goto no_mem_prof; 489#endif 490 491 return 0; 492 493#ifdef SDP_PROFILING 494no_mem_prof: 495#endif 496 497#ifdef SDPSTATS_ON 498 proc_net_remove(&init_net, PROC_SDP_STATS); 499 500no_mem_stats: 501#endif 502 proc_net_remove(&init_net, sdp_seq_afinfo.name); 503 504no_mem: 505 return -ENOMEM; 506} 507 508void sdp_proc_unregister(void) 509{ 510 proc_net_remove(&init_net, sdp_seq_afinfo.name); 511 memset(sdp_seq_afinfo.seq_fops, 0, sizeof(*sdp_seq_afinfo.seq_fops)); 512 513#ifdef SDPSTATS_ON 514 proc_net_remove(&init_net, PROC_SDP_STATS); 515#endif 516#ifdef SDP_PROFILING 517 proc_net_remove(&init_net, PROC_SDP_PERF); 518#endif 519} 520 521#else /* CONFIG_PROC_FS */ 522 523int __init sdp_proc_init(void) 524{ 525 return 0; 526} 527 528void sdp_proc_unregister(void) 529{ 530 531} 532#endif /* CONFIG_PROC_FS */ 533