1 /* -*- mode: c; c-basic-offset: 8; -*- 2 * vim: noexpandtab sw=8 ts=8 sts=0: 3 * 4 * netdebug.c 5 * 6 * debug functionality for o2net 7 * 8 * Copyright (C) 2005, 2008 Oracle. All rights reserved. 9 * 10 * This program is free software; you can redistribute it and/or 11 * modify it under the terms of the GNU General Public 12 * License as published by the Free Software Foundation; either 13 * version 2 of the License, or (at your option) any later version. 14 * 15 * This program is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 18 * General Public License for more details. 19 * 20 * You should have received a copy of the GNU General Public 21 * License along with this program; if not, write to the 22 * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 23 * Boston, MA 021110-1307, USA. 24 * 25 */ 26 27 #ifdef CONFIG_DEBUG_FS 28 29 #include <linux/module.h> 30 #include <linux/types.h> 31 #include <linux/slab.h> 32 #include <linux/idr.h> 33 #include <linux/kref.h> 34 #include <linux/seq_file.h> 35 #include <linux/debugfs.h> 36 37 #include <linux/uaccess.h> 38 39 #include "tcp.h" 40 #include "nodemanager.h" 41 #define MLOG_MASK_PREFIX ML_TCP 42 #include "masklog.h" 43 44 #include "tcp_internal.h" 45 46 #define O2NET_DEBUG_DIR "o2net" 47 #define SC_DEBUG_NAME "sock_containers" 48 #define NST_DEBUG_NAME "send_tracking" 49 50 static struct dentry *o2net_dentry; 51 static struct dentry *sc_dentry; 52 static struct dentry *nst_dentry; 53 54 static DEFINE_SPINLOCK(o2net_debug_lock); 55 56 static LIST_HEAD(sock_containers); 57 static LIST_HEAD(send_tracking); 58 59 void o2net_debug_add_nst(struct o2net_send_tracking *nst) 60 { 61 spin_lock(&o2net_debug_lock); 62 list_add(&nst->st_net_debug_item, &send_tracking); 63 spin_unlock(&o2net_debug_lock); 64 } 65 66 void o2net_debug_del_nst(struct o2net_send_tracking *nst) 67 { 68 spin_lock(&o2net_debug_lock); 69 if (!list_empty(&nst->st_net_debug_item)) 70 list_del_init(&nst->st_net_debug_item); 71 spin_unlock(&o2net_debug_lock); 72 } 73 74 static struct o2net_send_tracking 75 *next_nst(struct o2net_send_tracking *nst_start) 76 { 77 struct o2net_send_tracking *nst, *ret = NULL; 78 79 assert_spin_locked(&o2net_debug_lock); 80 81 list_for_each_entry(nst, &nst_start->st_net_debug_item, 82 st_net_debug_item) { 83 /* discover the head of the list */ 84 if (&nst->st_net_debug_item == &send_tracking) 85 break; 86 87 /* use st_task to detect real nsts in the list */ 88 if (nst->st_task != NULL) { 89 ret = nst; 90 break; 91 } 92 } 93 94 return ret; 95 } 96 97 static void *nst_seq_start(struct seq_file *seq, loff_t *pos) 98 { 99 struct o2net_send_tracking *nst, *dummy_nst = seq->private; 100 101 spin_lock(&o2net_debug_lock); 102 nst = next_nst(dummy_nst); 103 spin_unlock(&o2net_debug_lock); 104 105 return nst; 106 } 107 108 static void *nst_seq_next(struct seq_file *seq, void *v, loff_t *pos) 109 { 110 struct o2net_send_tracking *nst, *dummy_nst = seq->private; 111 112 spin_lock(&o2net_debug_lock); 113 nst = next_nst(dummy_nst); 114 list_del_init(&dummy_nst->st_net_debug_item); 115 if (nst) 116 list_add(&dummy_nst->st_net_debug_item, 117 &nst->st_net_debug_item); 118 spin_unlock(&o2net_debug_lock); 119 120 return nst; /* unused, just needs to be null when done */ 121 } 122 123 static int nst_seq_show(struct seq_file *seq, void *v) 124 { 125 struct o2net_send_tracking *nst, *dummy_nst = seq->private; 126 127 spin_lock(&o2net_debug_lock); 128 nst = next_nst(dummy_nst); 129 130 if (nst != NULL) { 131 /* get_task_comm isn't exported. oh well. */ 132 seq_printf(seq, "%p:\n" 133 " pid: %lu\n" 134 " tgid: %lu\n" 135 " process name: %s\n" 136 " node: %u\n" 137 " sc: %p\n" 138 " message id: %d\n" 139 " message type: %u\n" 140 " message key: 0x%08x\n" 141 " sock acquiry: %lu.%ld\n" 142 " send start: %lu.%ld\n" 143 " wait start: %lu.%ld\n", 144 nst, (unsigned long)nst->st_task->pid, 145 (unsigned long)nst->st_task->tgid, 146 nst->st_task->comm, nst->st_node, 147 nst->st_sc, nst->st_id, nst->st_msg_type, 148 nst->st_msg_key, 149 nst->st_sock_time.tv_sec, 150 (long)nst->st_sock_time.tv_usec, 151 nst->st_send_time.tv_sec, 152 (long)nst->st_send_time.tv_usec, 153 nst->st_status_time.tv_sec, 154 (long)nst->st_status_time.tv_usec); 155 } 156 157 spin_unlock(&o2net_debug_lock); 158 159 return 0; 160 } 161 162 static void nst_seq_stop(struct seq_file *seq, void *v) 163 { 164 } 165 166 static struct seq_operations nst_seq_ops = { 167 .start = nst_seq_start, 168 .next = nst_seq_next, 169 .stop = nst_seq_stop, 170 .show = nst_seq_show, 171 }; 172 173 static int nst_fop_open(struct inode *inode, struct file *file) 174 { 175 struct o2net_send_tracking *dummy_nst; 176 struct seq_file *seq; 177 int ret; 178 179 dummy_nst = kmalloc(sizeof(struct o2net_send_tracking), GFP_KERNEL); 180 if (dummy_nst == NULL) { 181 ret = -ENOMEM; 182 goto out; 183 } 184 dummy_nst->st_task = NULL; 185 186 ret = seq_open(file, &nst_seq_ops); 187 if (ret) 188 goto out; 189 190 seq = file->private_data; 191 seq->private = dummy_nst; 192 o2net_debug_add_nst(dummy_nst); 193 194 dummy_nst = NULL; 195 196 out: 197 kfree(dummy_nst); 198 return ret; 199 } 200 201 static int nst_fop_release(struct inode *inode, struct file *file) 202 { 203 struct seq_file *seq = file->private_data; 204 struct o2net_send_tracking *dummy_nst = seq->private; 205 206 o2net_debug_del_nst(dummy_nst); 207 return seq_release_private(inode, file); 208 } 209 210 static struct file_operations nst_seq_fops = { 211 .open = nst_fop_open, 212 .read = seq_read, 213 .llseek = seq_lseek, 214 .release = nst_fop_release, 215 }; 216 217 void o2net_debug_add_sc(struct o2net_sock_container *sc) 218 { 219 spin_lock(&o2net_debug_lock); 220 list_add(&sc->sc_net_debug_item, &sock_containers); 221 spin_unlock(&o2net_debug_lock); 222 } 223 224 void o2net_debug_del_sc(struct o2net_sock_container *sc) 225 { 226 spin_lock(&o2net_debug_lock); 227 list_del_init(&sc->sc_net_debug_item); 228 spin_unlock(&o2net_debug_lock); 229 } 230 231 static struct o2net_sock_container 232 *next_sc(struct o2net_sock_container *sc_start) 233 { 234 struct o2net_sock_container *sc, *ret = NULL; 235 236 assert_spin_locked(&o2net_debug_lock); 237 238 list_for_each_entry(sc, &sc_start->sc_net_debug_item, 239 sc_net_debug_item) { 240 /* discover the head of the list miscast as a sc */ 241 if (&sc->sc_net_debug_item == &sock_containers) 242 break; 243 244 /* use sc_page to detect real scs in the list */ 245 if (sc->sc_page != NULL) { 246 ret = sc; 247 break; 248 } 249 } 250 251 return ret; 252 } 253 254 static void *sc_seq_start(struct seq_file *seq, loff_t *pos) 255 { 256 struct o2net_sock_container *sc, *dummy_sc = seq->private; 257 258 spin_lock(&o2net_debug_lock); 259 sc = next_sc(dummy_sc); 260 spin_unlock(&o2net_debug_lock); 261 262 return sc; 263 } 264 265 static void *sc_seq_next(struct seq_file *seq, void *v, loff_t *pos) 266 { 267 struct o2net_sock_container *sc, *dummy_sc = seq->private; 268 269 spin_lock(&o2net_debug_lock); 270 sc = next_sc(dummy_sc); 271 list_del_init(&dummy_sc->sc_net_debug_item); 272 if (sc) 273 list_add(&dummy_sc->sc_net_debug_item, &sc->sc_net_debug_item); 274 spin_unlock(&o2net_debug_lock); 275 276 return sc; /* unused, just needs to be null when done */ 277 } 278 279 #define TV_SEC_USEC(TV) TV.tv_sec, (long)TV.tv_usec 280 281 static int sc_seq_show(struct seq_file *seq, void *v) 282 { 283 struct o2net_sock_container *sc, *dummy_sc = seq->private; 284 285 spin_lock(&o2net_debug_lock); 286 sc = next_sc(dummy_sc); 287 288 if (sc != NULL) { 289 struct inet_sock *inet = NULL; 290 291 __be32 saddr = 0, daddr = 0; 292 __be16 sport = 0, dport = 0; 293 294 if (sc->sc_sock) { 295 inet = inet_sk(sc->sc_sock->sk); 296 /* the stack's structs aren't sparse endian clean */ 297 saddr = (__force __be32)inet->saddr; 298 daddr = (__force __be32)inet->daddr; 299 sport = (__force __be16)inet->sport; 300 dport = (__force __be16)inet->dport; 301 } 302 303 /* XXX sigh, inet-> doesn't have sparse annotation so any 304 * use of it here generates a warning with -Wbitwise */ 305 seq_printf(seq, "%p:\n" 306 " krefs: %d\n" 307 " sock: %u.%u.%u.%u:%u -> " 308 "%u.%u.%u.%u:%u\n" 309 " remote node: %s\n" 310 " page off: %zu\n" 311 " handshake ok: %u\n" 312 " timer: %lu.%ld\n" 313 " data ready: %lu.%ld\n" 314 " advance start: %lu.%ld\n" 315 " advance stop: %lu.%ld\n" 316 " func start: %lu.%ld\n" 317 " func stop: %lu.%ld\n" 318 " func key: %u\n" 319 " func type: %u\n", 320 sc, 321 atomic_read(&sc->sc_kref.refcount), 322 NIPQUAD(saddr), inet ? ntohs(sport) : 0, 323 NIPQUAD(daddr), inet ? ntohs(dport) : 0, 324 sc->sc_node->nd_name, 325 sc->sc_page_off, 326 sc->sc_handshake_ok, 327 TV_SEC_USEC(sc->sc_tv_timer), 328 TV_SEC_USEC(sc->sc_tv_data_ready), 329 TV_SEC_USEC(sc->sc_tv_advance_start), 330 TV_SEC_USEC(sc->sc_tv_advance_stop), 331 TV_SEC_USEC(sc->sc_tv_func_start), 332 TV_SEC_USEC(sc->sc_tv_func_stop), 333 sc->sc_msg_key, 334 sc->sc_msg_type); 335 } 336 337 338 spin_unlock(&o2net_debug_lock); 339 340 return 0; 341 } 342 343 static void sc_seq_stop(struct seq_file *seq, void *v) 344 { 345 } 346 347 static struct seq_operations sc_seq_ops = { 348 .start = sc_seq_start, 349 .next = sc_seq_next, 350 .stop = sc_seq_stop, 351 .show = sc_seq_show, 352 }; 353 354 static int sc_fop_open(struct inode *inode, struct file *file) 355 { 356 struct o2net_sock_container *dummy_sc; 357 struct seq_file *seq; 358 int ret; 359 360 dummy_sc = kmalloc(sizeof(struct o2net_sock_container), GFP_KERNEL); 361 if (dummy_sc == NULL) { 362 ret = -ENOMEM; 363 goto out; 364 } 365 dummy_sc->sc_page = NULL; 366 367 ret = seq_open(file, &sc_seq_ops); 368 if (ret) 369 goto out; 370 371 seq = file->private_data; 372 seq->private = dummy_sc; 373 o2net_debug_add_sc(dummy_sc); 374 375 dummy_sc = NULL; 376 377 out: 378 kfree(dummy_sc); 379 return ret; 380 } 381 382 static int sc_fop_release(struct inode *inode, struct file *file) 383 { 384 struct seq_file *seq = file->private_data; 385 struct o2net_sock_container *dummy_sc = seq->private; 386 387 o2net_debug_del_sc(dummy_sc); 388 return seq_release_private(inode, file); 389 } 390 391 static struct file_operations sc_seq_fops = { 392 .open = sc_fop_open, 393 .read = seq_read, 394 .llseek = seq_lseek, 395 .release = sc_fop_release, 396 }; 397 398 int o2net_debugfs_init(void) 399 { 400 o2net_dentry = debugfs_create_dir(O2NET_DEBUG_DIR, NULL); 401 if (!o2net_dentry) { 402 mlog_errno(-ENOMEM); 403 goto bail; 404 } 405 406 nst_dentry = debugfs_create_file(NST_DEBUG_NAME, S_IFREG|S_IRUSR, 407 o2net_dentry, NULL, 408 &nst_seq_fops); 409 if (!nst_dentry) { 410 mlog_errno(-ENOMEM); 411 goto bail; 412 } 413 414 sc_dentry = debugfs_create_file(SC_DEBUG_NAME, S_IFREG|S_IRUSR, 415 o2net_dentry, NULL, 416 &sc_seq_fops); 417 if (!sc_dentry) { 418 mlog_errno(-ENOMEM); 419 goto bail; 420 } 421 422 return 0; 423 bail: 424 if (sc_dentry) 425 debugfs_remove(sc_dentry); 426 if (nst_dentry) 427 debugfs_remove(nst_dentry); 428 if (o2net_dentry) 429 debugfs_remove(o2net_dentry); 430 return -ENOMEM; 431 } 432 433 void o2net_debugfs_exit(void) 434 { 435 if (sc_dentry) 436 debugfs_remove(sc_dentry); 437 if (nst_dentry) 438 debugfs_remove(nst_dentry); 439 if (o2net_dentry) 440 debugfs_remove(o2net_dentry); 441 } 442 443 #endif /* CONFIG_DEBUG_FS */ 444