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