1 // SPDX-License-Identifier: GPL-2.0 2 #include <linux/ceph/ceph_debug.h> 3 4 #include <linux/device.h> 5 #include <linux/slab.h> 6 #include <linux/module.h> 7 #include <linux/ctype.h> 8 #include <linux/debugfs.h> 9 #include <linux/seq_file.h> 10 #include <linux/math64.h> 11 #include <linux/ktime.h> 12 13 #include <linux/ceph/libceph.h> 14 #include <linux/ceph/mon_client.h> 15 #include <linux/ceph/auth.h> 16 #include <linux/ceph/debugfs.h> 17 18 #include "super.h" 19 20 #ifdef CONFIG_DEBUG_FS 21 22 #include "mds_client.h" 23 #include "metric.h" 24 25 static int mdsmap_show(struct seq_file *s, void *p) 26 { 27 int i; 28 struct ceph_fs_client *fsc = s->private; 29 struct ceph_mdsmap *mdsmap; 30 31 if (!fsc->mdsc || !fsc->mdsc->mdsmap) 32 return 0; 33 mdsmap = fsc->mdsc->mdsmap; 34 seq_printf(s, "epoch %d\n", mdsmap->m_epoch); 35 seq_printf(s, "root %d\n", mdsmap->m_root); 36 seq_printf(s, "max_mds %d\n", mdsmap->m_max_mds); 37 seq_printf(s, "session_timeout %d\n", mdsmap->m_session_timeout); 38 seq_printf(s, "session_autoclose %d\n", mdsmap->m_session_autoclose); 39 for (i = 0; i < mdsmap->possible_max_rank; i++) { 40 struct ceph_entity_addr *addr = &mdsmap->m_info[i].addr; 41 int state = mdsmap->m_info[i].state; 42 seq_printf(s, "\tmds%d\t%s\t(%s)\n", i, 43 ceph_pr_addr(addr), 44 ceph_mds_state_name(state)); 45 } 46 return 0; 47 } 48 49 /* 50 * mdsc debugfs 51 */ 52 static int mdsc_show(struct seq_file *s, void *p) 53 { 54 struct ceph_fs_client *fsc = s->private; 55 struct ceph_mds_client *mdsc = fsc->mdsc; 56 struct ceph_mds_request *req; 57 struct rb_node *rp; 58 char *path; 59 60 mutex_lock(&mdsc->mutex); 61 for (rp = rb_first(&mdsc->request_tree); rp; rp = rb_next(rp)) { 62 req = rb_entry(rp, struct ceph_mds_request, r_node); 63 64 if (req->r_request && req->r_session) 65 seq_printf(s, "%lld\tmds%d\t", req->r_tid, 66 req->r_session->s_mds); 67 else if (!req->r_request) 68 seq_printf(s, "%lld\t(no request)\t", req->r_tid); 69 else 70 seq_printf(s, "%lld\t(no session)\t", req->r_tid); 71 72 seq_printf(s, "%s", ceph_mds_op_name(req->r_op)); 73 74 if (test_bit(CEPH_MDS_R_GOT_UNSAFE, &req->r_req_flags)) 75 seq_puts(s, "\t(unsafe)"); 76 else 77 seq_puts(s, "\t"); 78 79 if (req->r_inode) { 80 seq_printf(s, " #%llx", ceph_ino(req->r_inode)); 81 } else if (req->r_dentry) { 82 struct ceph_path_info path_info; 83 path = ceph_mdsc_build_path(mdsc, req->r_dentry, &path_info, 0); 84 if (IS_ERR(path)) 85 path = NULL; 86 spin_lock(&req->r_dentry->d_lock); 87 seq_printf(s, " #%llx/%pd (%s)", 88 ceph_ino(d_inode(req->r_dentry->d_parent)), 89 req->r_dentry, 90 path ? path : ""); 91 spin_unlock(&req->r_dentry->d_lock); 92 ceph_mdsc_free_path_info(&path_info); 93 } else if (req->r_path1) { 94 seq_printf(s, " #%llx/%s", req->r_ino1.ino, 95 req->r_path1); 96 } else { 97 seq_printf(s, " #%llx", req->r_ino1.ino); 98 } 99 100 if (req->r_old_dentry) { 101 struct ceph_path_info path_info; 102 path = ceph_mdsc_build_path(mdsc, req->r_old_dentry, &path_info, 0); 103 if (IS_ERR(path)) 104 path = NULL; 105 spin_lock(&req->r_old_dentry->d_lock); 106 seq_printf(s, " #%llx/%pd (%s)", 107 req->r_old_dentry_dir ? 108 ceph_ino(req->r_old_dentry_dir) : 0, 109 req->r_old_dentry, 110 path ? path : ""); 111 spin_unlock(&req->r_old_dentry->d_lock); 112 ceph_mdsc_free_path_info(&path_info); 113 } else if (req->r_path2 && req->r_op != CEPH_MDS_OP_SYMLINK) { 114 if (req->r_ino2.ino) 115 seq_printf(s, " #%llx/%s", req->r_ino2.ino, 116 req->r_path2); 117 else 118 seq_printf(s, " %s", req->r_path2); 119 } 120 121 seq_puts(s, "\n"); 122 } 123 mutex_unlock(&mdsc->mutex); 124 125 return 0; 126 } 127 128 #define CEPH_LAT_METRIC_SHOW(name, total, avg, min, max, sq) { \ 129 s64 _total, _avg, _min, _max, _sq, _st; \ 130 _avg = ktime_to_us(avg); \ 131 _min = ktime_to_us(min == KTIME_MAX ? 0 : min); \ 132 _max = ktime_to_us(max); \ 133 _total = total - 1; \ 134 _sq = _total > 0 ? DIV64_U64_ROUND_CLOSEST(sq, _total) : 0; \ 135 _st = int_sqrt64(_sq); \ 136 _st = ktime_to_us(_st); \ 137 seq_printf(s, "%-14s%-12lld%-16lld%-16lld%-16lld%lld\n", \ 138 name, total, _avg, _min, _max, _st); \ 139 } 140 141 #define CEPH_SZ_METRIC_SHOW(name, total, avg, min, max, sum) { \ 142 u64 _min = min == U64_MAX ? 0 : min; \ 143 seq_printf(s, "%-14s%-12lld%-16llu%-16llu%-16llu%llu\n", \ 144 name, total, avg, _min, max, sum); \ 145 } 146 147 static int metrics_file_show(struct seq_file *s, void *p) 148 { 149 struct ceph_fs_client *fsc = s->private; 150 struct ceph_client_metric *m = &fsc->mdsc->metric; 151 152 seq_printf(s, "item total\n"); 153 seq_printf(s, "------------------------------------------\n"); 154 seq_printf(s, "%-35s%lld\n", "total inodes", 155 percpu_counter_sum(&m->total_inodes)); 156 seq_printf(s, "%-35s%lld\n", "opened files", 157 atomic64_read(&m->opened_files)); 158 seq_printf(s, "%-35s%lld\n", "pinned i_caps", 159 atomic64_read(&m->total_caps)); 160 seq_printf(s, "%-35s%lld\n", "opened inodes", 161 percpu_counter_sum(&m->opened_inodes)); 162 return 0; 163 } 164 165 static const char * const metric_str[] = { 166 "read", 167 "write", 168 "metadata", 169 "copyfrom" 170 }; 171 static int metrics_latency_show(struct seq_file *s, void *p) 172 { 173 struct ceph_fs_client *fsc = s->private; 174 struct ceph_client_metric *cm = &fsc->mdsc->metric; 175 struct ceph_metric *m; 176 s64 total, avg, min, max, sq; 177 int i; 178 179 seq_printf(s, "item total avg_lat(us) min_lat(us) max_lat(us) stdev(us)\n"); 180 seq_printf(s, "-----------------------------------------------------------------------------------\n"); 181 182 for (i = 0; i < METRIC_MAX; i++) { 183 m = &cm->metric[i]; 184 spin_lock(&m->lock); 185 total = m->total; 186 avg = m->latency_avg; 187 min = m->latency_min; 188 max = m->latency_max; 189 sq = m->latency_sq_sum; 190 spin_unlock(&m->lock); 191 CEPH_LAT_METRIC_SHOW(metric_str[i], total, avg, min, max, sq); 192 } 193 194 return 0; 195 } 196 197 static int metrics_size_show(struct seq_file *s, void *p) 198 { 199 struct ceph_fs_client *fsc = s->private; 200 struct ceph_client_metric *cm = &fsc->mdsc->metric; 201 struct ceph_metric *m; 202 s64 total; 203 u64 sum, avg, min, max; 204 int i; 205 206 seq_printf(s, "item total avg_sz(bytes) min_sz(bytes) max_sz(bytes) total_sz(bytes)\n"); 207 seq_printf(s, "----------------------------------------------------------------------------------------\n"); 208 209 for (i = 0; i < METRIC_MAX; i++) { 210 /* skip 'metadata' as it doesn't use the size metric */ 211 if (i == METRIC_METADATA) 212 continue; 213 m = &cm->metric[i]; 214 spin_lock(&m->lock); 215 total = m->total; 216 sum = m->size_sum; 217 avg = total > 0 ? DIV64_U64_ROUND_CLOSEST(sum, total) : 0; 218 min = m->size_min; 219 max = m->size_max; 220 spin_unlock(&m->lock); 221 CEPH_SZ_METRIC_SHOW(metric_str[i], total, avg, min, max, sum); 222 } 223 224 return 0; 225 } 226 227 static int metrics_caps_show(struct seq_file *s, void *p) 228 { 229 struct ceph_fs_client *fsc = s->private; 230 struct ceph_client_metric *m = &fsc->mdsc->metric; 231 int nr_caps = 0; 232 233 seq_printf(s, "item total miss hit\n"); 234 seq_printf(s, "-------------------------------------------------\n"); 235 236 seq_printf(s, "%-14s%-16lld%-16lld%lld\n", "d_lease", 237 atomic64_read(&m->total_dentries), 238 percpu_counter_sum(&m->d_lease_mis), 239 percpu_counter_sum(&m->d_lease_hit)); 240 241 nr_caps = atomic64_read(&m->total_caps); 242 seq_printf(s, "%-14s%-16d%-16lld%lld\n", "caps", nr_caps, 243 percpu_counter_sum(&m->i_caps_mis), 244 percpu_counter_sum(&m->i_caps_hit)); 245 246 return 0; 247 } 248 249 static int caps_show_cb(struct inode *inode, int mds, void *p) 250 { 251 struct ceph_inode_info *ci = ceph_inode(inode); 252 struct seq_file *s = p; 253 struct ceph_cap *cap; 254 255 spin_lock(&ci->i_ceph_lock); 256 cap = __get_cap_for_mds(ci, mds); 257 if (cap) 258 seq_printf(s, "0x%-17llx%-3d%-17s%-17s\n", ceph_ino(inode), 259 cap->session->s_mds, 260 ceph_cap_string(cap->issued), 261 ceph_cap_string(cap->implemented)); 262 spin_unlock(&ci->i_ceph_lock); 263 return 0; 264 } 265 266 static int caps_show(struct seq_file *s, void *p) 267 { 268 struct ceph_fs_client *fsc = s->private; 269 struct ceph_mds_client *mdsc = fsc->mdsc; 270 int total, avail, used, reserved, min, i; 271 struct cap_wait *cw; 272 273 ceph_reservation_status(fsc, &total, &avail, &used, &reserved, &min); 274 seq_printf(s, "total\t\t%d\n" 275 "avail\t\t%d\n" 276 "used\t\t%d\n" 277 "reserved\t%d\n" 278 "min\t\t%d\n\n", 279 total, avail, used, reserved, min); 280 seq_printf(s, "ino mds issued implemented\n"); 281 seq_printf(s, "--------------------------------------------------\n"); 282 283 mutex_lock(&mdsc->mutex); 284 for (i = 0; i < mdsc->max_sessions; i++) { 285 struct ceph_mds_session *session; 286 287 session = __ceph_lookup_mds_session(mdsc, i); 288 if (!session) 289 continue; 290 mutex_unlock(&mdsc->mutex); 291 mutex_lock(&session->s_mutex); 292 ceph_iterate_session_caps(session, caps_show_cb, s); 293 mutex_unlock(&session->s_mutex); 294 ceph_put_mds_session(session); 295 mutex_lock(&mdsc->mutex); 296 } 297 mutex_unlock(&mdsc->mutex); 298 299 seq_printf(s, "\n\nWaiters:\n--------\n"); 300 seq_printf(s, "tgid ino need want\n"); 301 seq_printf(s, "-----------------------------------------------------\n"); 302 303 spin_lock(&mdsc->caps_list_lock); 304 list_for_each_entry(cw, &mdsc->cap_wait_list, list) { 305 seq_printf(s, "%-13d0x%-17llx%-17s%-17s\n", cw->tgid, cw->ino, 306 ceph_cap_string(cw->need), 307 ceph_cap_string(cw->want)); 308 } 309 spin_unlock(&mdsc->caps_list_lock); 310 311 return 0; 312 } 313 314 static int mds_sessions_show(struct seq_file *s, void *ptr) 315 { 316 struct ceph_fs_client *fsc = s->private; 317 struct ceph_mds_client *mdsc = fsc->mdsc; 318 struct ceph_auth_client *ac = fsc->client->monc.auth; 319 struct ceph_options *opt = fsc->client->options; 320 int mds; 321 322 mutex_lock(&mdsc->mutex); 323 324 /* The 'num' portion of an 'entity name' */ 325 seq_printf(s, "global_id %llu\n", ac->global_id); 326 327 /* The -o name mount argument */ 328 seq_printf(s, "name \"%s\"\n", opt->name ? opt->name : ""); 329 330 /* The list of MDS session rank+state */ 331 for (mds = 0; mds < mdsc->max_sessions; mds++) { 332 struct ceph_mds_session *session = 333 __ceph_lookup_mds_session(mdsc, mds); 334 if (!session) { 335 continue; 336 } 337 mutex_unlock(&mdsc->mutex); 338 seq_printf(s, "mds.%d %s\n", 339 session->s_mds, 340 ceph_session_state_name(session->s_state)); 341 342 ceph_put_mds_session(session); 343 mutex_lock(&mdsc->mutex); 344 } 345 mutex_unlock(&mdsc->mutex); 346 347 return 0; 348 } 349 350 static int status_show(struct seq_file *s, void *p) 351 { 352 struct ceph_fs_client *fsc = s->private; 353 struct ceph_entity_inst *inst = &fsc->client->msgr.inst; 354 struct ceph_entity_addr *client_addr = ceph_client_addr(fsc->client); 355 356 seq_printf(s, "instance: %s.%lld %s/%u\n", ENTITY_NAME(inst->name), 357 ceph_pr_addr(client_addr), le32_to_cpu(client_addr->nonce)); 358 seq_printf(s, "blocklisted: %s\n", str_true_false(fsc->blocklisted)); 359 360 return 0; 361 } 362 363 DEFINE_SHOW_ATTRIBUTE(mdsmap); 364 DEFINE_SHOW_ATTRIBUTE(mdsc); 365 DEFINE_SHOW_ATTRIBUTE(caps); 366 DEFINE_SHOW_ATTRIBUTE(mds_sessions); 367 DEFINE_SHOW_ATTRIBUTE(status); 368 DEFINE_SHOW_ATTRIBUTE(metrics_file); 369 DEFINE_SHOW_ATTRIBUTE(metrics_latency); 370 DEFINE_SHOW_ATTRIBUTE(metrics_size); 371 DEFINE_SHOW_ATTRIBUTE(metrics_caps); 372 373 374 /* 375 * debugfs 376 */ 377 static int congestion_kb_set(void *data, u64 val) 378 { 379 struct ceph_fs_client *fsc = (struct ceph_fs_client *)data; 380 381 fsc->mount_options->congestion_kb = (int)val; 382 return 0; 383 } 384 385 static int congestion_kb_get(void *data, u64 *val) 386 { 387 struct ceph_fs_client *fsc = (struct ceph_fs_client *)data; 388 389 *val = (u64)fsc->mount_options->congestion_kb; 390 return 0; 391 } 392 393 DEFINE_SIMPLE_ATTRIBUTE(congestion_kb_fops, congestion_kb_get, 394 congestion_kb_set, "%llu\n"); 395 396 397 void ceph_fs_debugfs_cleanup(struct ceph_fs_client *fsc) 398 { 399 doutc(fsc->client, "begin\n"); 400 debugfs_remove(fsc->debugfs_bdi); 401 debugfs_remove(fsc->debugfs_congestion_kb); 402 debugfs_remove(fsc->debugfs_mdsmap); 403 debugfs_remove(fsc->debugfs_mds_sessions); 404 debugfs_remove(fsc->debugfs_caps); 405 debugfs_remove(fsc->debugfs_status); 406 debugfs_remove(fsc->debugfs_mdsc); 407 debugfs_remove_recursive(fsc->debugfs_metrics_dir); 408 doutc(fsc->client, "done\n"); 409 } 410 411 void ceph_fs_debugfs_init(struct ceph_fs_client *fsc) 412 { 413 char name[NAME_MAX]; 414 415 doutc(fsc->client, "begin\n"); 416 fsc->debugfs_congestion_kb = 417 debugfs_create_file("writeback_congestion_kb", 418 0600, 419 fsc->client->debugfs_dir, 420 fsc, 421 &congestion_kb_fops); 422 423 snprintf(name, sizeof(name), "../../bdi/%s", 424 bdi_dev_name(fsc->sb->s_bdi)); 425 fsc->debugfs_bdi = 426 debugfs_create_symlink("bdi", 427 fsc->client->debugfs_dir, 428 name); 429 430 fsc->debugfs_mdsmap = debugfs_create_file("mdsmap", 431 0400, 432 fsc->client->debugfs_dir, 433 fsc, 434 &mdsmap_fops); 435 436 fsc->debugfs_mds_sessions = debugfs_create_file("mds_sessions", 437 0400, 438 fsc->client->debugfs_dir, 439 fsc, 440 &mds_sessions_fops); 441 442 fsc->debugfs_mdsc = debugfs_create_file("mdsc", 443 0400, 444 fsc->client->debugfs_dir, 445 fsc, 446 &mdsc_fops); 447 448 fsc->debugfs_caps = debugfs_create_file("caps", 449 0400, 450 fsc->client->debugfs_dir, 451 fsc, 452 &caps_fops); 453 454 fsc->debugfs_status = debugfs_create_file("status", 455 0400, 456 fsc->client->debugfs_dir, 457 fsc, 458 &status_fops); 459 460 fsc->debugfs_metrics_dir = debugfs_create_dir("metrics", 461 fsc->client->debugfs_dir); 462 463 debugfs_create_file("file", 0400, fsc->debugfs_metrics_dir, fsc, 464 &metrics_file_fops); 465 debugfs_create_file("latency", 0400, fsc->debugfs_metrics_dir, fsc, 466 &metrics_latency_fops); 467 debugfs_create_file("size", 0400, fsc->debugfs_metrics_dir, fsc, 468 &metrics_size_fops); 469 debugfs_create_file("caps", 0400, fsc->debugfs_metrics_dir, fsc, 470 &metrics_caps_fops); 471 doutc(fsc->client, "done\n"); 472 } 473 474 475 #else /* CONFIG_DEBUG_FS */ 476 477 void ceph_fs_debugfs_init(struct ceph_fs_client *fsc) 478 { 479 } 480 481 void ceph_fs_debugfs_cleanup(struct ceph_fs_client *fsc) 482 { 483 } 484 485 #endif /* CONFIG_DEBUG_FS */ 486