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