1 /* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
2 * vim:expandtab:shiftwidth=8:tabstop=8:
6 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 only,
10 * as published by the Free Software Foundation.
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License version 2 for more details (a copy is included
16 * in the LICENSE file that accompanied this code).
18 * You should have received a copy of the GNU General Public License
19 * version 2 along with this program; If not, see
20 * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
22 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
23 * CA 95054 USA or visit www.sun.com if you need additional information or
29 * Copyright 2008 Sun Microsystems, Inc. All rights reserved
30 * Use is subject to license terms.
33 * This file is part of Lustre, http://www.lustre.org/
34 * Lustre is a trademark of Sun Microsystems, Inc.
36 * lustre/mdd/mdd_lproc.c
38 * Lustre Metadata Server (mdd) routines
40 * Author: Wang Di <wangdi@clusterfs.com>
44 # define EXPORT_SYMTAB
46 #define DEBUG_SUBSYSTEM S_MDS
48 #include <linux/module.h>
50 #include <obd_class.h>
51 #include <lustre_ver.h>
52 #include <obd_support.h>
53 #include <lprocfs_status.h>
55 #include <lustre_log.h>
56 #include <lustre/lustre_idl.h>
57 #include <libcfs/libcfs_string.h>
59 #include "mdd_internal.h"
61 #ifndef SEEK_CUR /* SLES10 needs this */
66 static const char *mdd_counter_names[LPROC_MDD_NR] = {
69 /* from LPROC_SEQ_FOPS(mdd_changelog) below */
70 extern struct file_operations mdd_changelog_fops;
72 int mdd_procfs_init(struct mdd_device *mdd, const char *name)
74 struct lprocfs_static_vars lvars;
75 struct lu_device *ld = &mdd->mdd_md_dev.md_lu_dev;
76 struct obd_type *type;
80 type = ld->ld_type->ldt_obd_type;
82 LASSERT(name != NULL);
83 LASSERT(type != NULL);
85 /* Find the type procroot and add the proc entry for this device */
86 lprocfs_mdd_init_vars(&lvars);
87 mdd->mdd_proc_entry = lprocfs_register(name, type->typ_procroot,
89 if (IS_ERR(mdd->mdd_proc_entry)) {
90 rc = PTR_ERR(mdd->mdd_proc_entry);
91 CERROR("Error %d setting up lprocfs for %s\n",
93 mdd->mdd_proc_entry = NULL;
97 rc = lu_time_init(&mdd->mdd_stats,
99 mdd_counter_names, ARRAY_SIZE(mdd_counter_names));
104 mdd_procfs_fini(mdd);
108 int mdd_procfs_fini(struct mdd_device *mdd)
111 lu_time_fini(&mdd->mdd_stats);
113 if (mdd->mdd_proc_entry) {
114 lprocfs_remove(&mdd->mdd_proc_entry);
115 mdd->mdd_proc_entry = NULL;
120 void mdd_lprocfs_time_start(const struct lu_env *env)
122 lu_lprocfs_time_start(env);
125 void mdd_lprocfs_time_end(const struct lu_env *env, struct mdd_device *mdd,
128 lu_lprocfs_time_end(env, mdd->mdd_stats, idx);
131 static int lprocfs_wr_atime_diff(struct file *file, const char *buffer,
132 unsigned long count, void *data)
134 struct mdd_device *mdd = data;
135 char kernbuf[20], *end;
136 unsigned long diff = 0;
138 if (count > (sizeof(kernbuf) - 1))
141 if (copy_from_user(kernbuf, buffer, count))
144 kernbuf[count] = '\0';
146 diff = simple_strtoul(kernbuf, &end, 0);
150 mdd->mdd_atime_diff = diff;
154 static int lprocfs_rd_atime_diff(char *page, char **start, off_t off,
155 int count, int *eof, void *data)
157 struct mdd_device *mdd = data;
160 return snprintf(page, count, "%lu\n", mdd->mdd_atime_diff);
163 /* match enum changelog_rec_type */
164 static const char *changelog_str[] = {"MARK","CREAT","MKDIR","HLINK","SLINK",
165 "MKNOD","UNLNK","RMDIR","RNMFM","RNMTO","OPEN","CLOSE","IOCTL",
166 "TRUNC","SATTR","XATTR"};
168 const char *changelog_bit2str(int bit)
171 return changelog_str[bit];
175 static int lprocfs_rd_cl_mask(char *page, char **start, off_t off,
176 int count, int *eof, void *data)
178 struct mdd_device *mdd = data;
182 while (i < CL_LAST) {
183 if (mdd->mdd_cl.mc_mask & (1 << i))
184 rc += snprintf(page + rc, count - rc, "%s ",
191 static int lprocfs_wr_cl_mask(struct file *file, const char *buffer,
192 unsigned long count, void *data)
194 struct mdd_device *mdd = data;
199 if (count >= CFS_PAGE_SIZE)
201 OBD_ALLOC(kernbuf, CFS_PAGE_SIZE);
204 if (copy_from_user(kernbuf, buffer, count))
205 GOTO(out, rc = -EFAULT);
208 rc = libcfs_str2mask(kernbuf, changelog_bit2str,
209 &mdd->mdd_cl.mc_mask, CL_MINMASK, CL_ALLMASK);
213 OBD_FREE(kernbuf, CFS_PAGE_SIZE);
217 /** struct for holding changelog data for seq_file processing */
219 struct mdd_device *csi_mdd;
229 /* non-seq version for direct calling by class_process_proc_param */
230 static int lprocfs_wr_cl(struct file *file, const char *buffer,
231 unsigned long count, void *data)
233 struct mdd_device *mdd = (struct mdd_device *)data;
238 if (count > (sizeof(kernbuf) - 1))
241 count = min_t(unsigned long, count, sizeof(kernbuf));
242 if (copy_from_user(kernbuf, buffer, count))
245 kernbuf[count] = '\0';
246 /* strip trailing newline from "echo blah" */
247 if (kernbuf[count - 1] == '\n')
248 kernbuf[count - 1] = '\0';
250 if (strcmp(kernbuf, "on") == 0) {
251 LCONSOLE_INFO("changelog on\n");
252 if (mdd->mdd_cl.mc_flags & CLM_ERR) {
253 CERROR("Changelogs cannot be enabled due to error "
256 spin_lock(&mdd->mdd_cl.mc_lock);
257 mdd->mdd_cl.mc_flags |= CLM_ON;
258 spin_unlock(&mdd->mdd_cl.mc_lock);
259 rc = mdd_changelog_write_header(mdd, CLM_START);
263 } else if (strcmp(kernbuf, "off") == 0) {
264 LCONSOLE_INFO("changelog off\n");
265 rc = mdd_changelog_write_header(mdd, CLM_FINI);
268 spin_lock(&mdd->mdd_cl.mc_lock);
269 mdd->mdd_cl.mc_flags &= ~CLM_ON;
270 spin_unlock(&mdd->mdd_cl.mc_lock);
272 /* purge to an index */
273 long long unsigned endrec, cur;
275 spin_lock(&mdd->mdd_cl.mc_lock);
276 cur = (long long)mdd->mdd_cl.mc_index;
277 spin_unlock(&mdd->mdd_cl.mc_lock);
279 if (strcmp(kernbuf, "0") == 0)
280 /* purge to "0" is shorthand for everything */
283 endrec = (long long)simple_strtoull(kernbuf, &end, 0);
284 if ((kernbuf == end) || (endrec == 0))
289 /* If purging all records, write a header entry so we
290 don't have an empty catalog and
291 we're sure to have a valid starting index next time. In
292 case of crash, we just restart with old log so we're
295 rc = mdd_changelog_write_header(mdd, CLM_PURGE);
300 LCONSOLE_INFO("changelog purge to %llu\n", endrec);
302 rc = mdd_changelog_llog_cancel(mdd, endrec);
310 CWARN("changelog write usage: [on|off] | <purge_idx (0=all)>\n");
314 static ssize_t mdd_cl_seq_write(struct file *file, const char *buffer,
315 size_t count, loff_t *off)
317 struct seq_file *seq = file->private_data;
318 struct cl_seq_iter *csi = seq->private;
319 struct mdd_device *mdd = csi->csi_mdd;
321 return lprocfs_wr_cl(file, buffer, count, mdd);
326 /* How many records per seq_show. Too small, we spawn llog_process threads
327 too often; too large, we run out of buffer space */
328 #define CL_CHUNK_SIZE 100
330 static int changelog_show_cb(struct llog_handle *llh, struct llog_rec_hdr *hdr,
333 struct seq_file *seq = (struct seq_file *)data;
334 struct cl_seq_iter *csi = seq->private;
335 struct llog_changelog_rec *rec = (struct llog_changelog_rec *)hdr;
339 if ((rec->cr_hdr.lrh_type != CHANGELOG_REC) ||
340 (rec->cr_type >= CL_LAST)) {
341 CERROR("Not a changelog rec? %d/%d\n", rec->cr_hdr.lrh_type,
346 CDEBUG(D_CL, "rec="LPU64" start="LPU64" cat=%d:%d start=%d:%d\n",
347 rec->cr_index, csi->csi_startrec,
348 llh->lgh_hdr->llh_cat_idx, llh->lgh_cur_idx,
349 csi->csi_startcat, csi->csi_startidx);
351 if (rec->cr_index < csi->csi_startrec)
353 if (rec->cr_index == csi->csi_startrec) {
354 /* Remember where we started, since seq_read will re-read
355 * the data when it reallocs space. Sigh, if only there was
356 * a way to tell seq_file how big the buf should be in the
358 csi->csi_startcat = llh->lgh_hdr->llh_cat_idx;
359 csi->csi_startidx = rec->cr_hdr.lrh_index - 1;
361 if (csi->csi_wrote > CL_CHUNK_SIZE) {
362 /* Stop at some point with a reasonable seq_file buffer size.
363 * Start from here the next time.
365 csi->csi_endrec = rec->cr_index - 1;
366 csi->csi_startcat = llh->lgh_hdr->llh_cat_idx;
367 csi->csi_startidx = rec->cr_hdr.lrh_index - 1;
369 RETURN(LLOG_PROC_BREAK);
372 rc = seq_printf(seq, LPU64" %02d%-5s "LPU64" 0x%x t="DFID,
373 rec->cr_index, rec->cr_type,
374 changelog_str[rec->cr_type], rec->cr_time,
375 rec->cr_flags & CLF_FLAGMASK, PFID(&rec->cr_tfid));
378 /* namespace rec includes parent and filename */
379 rc += seq_printf(seq, " p="DFID" %.*s\n", PFID(&rec->cr_pfid),
380 rec->cr_namelen, rec->cr_name);
382 rc += seq_puts(seq, "\n");
385 /* seq_read will dump the whole buffer and re-seq_start with a
386 larger one; no point in continuing the llog_process */
387 CDEBUG(D_CL, "rec="LPU64" overflow "LPU64"<-"LPU64"\n",
388 rec->cr_index, csi->csi_startrec, csi->csi_endrec);
389 csi->csi_endrec = csi->csi_startrec - 1;
391 RETURN(LLOG_PROC_BREAK);
395 csi->csi_endrec = rec->cr_index;
400 static int mdd_cl_seq_show(struct seq_file *seq, void *v)
402 struct cl_seq_iter *csi = seq->private;
403 struct obd_device *obd = mdd2obd_dev(csi->csi_mdd);
404 struct llog_ctxt *ctxt;
408 /* seq_read wants more data to fill his buffer. But we already
409 filled the buf as much as we cared to; force seq_read to
411 while ((rc = seq_putc(seq, 0)) == 0);
415 ctxt = llog_get_context(obd, LLOG_CHANGELOG_ORIG_CTXT);
419 /* Since we have to restart the llog_cat_process for each chunk of the
420 seq_ functions, start from where we left off. */
421 rc = llog_cat_process(ctxt->loc_handle, changelog_show_cb, seq,
422 csi->csi_startcat, csi->csi_startidx);
424 CDEBUG(D_CL, "seq_show "LPU64"-"LPU64" cat=%d:%d wrote=%d rc=%d\n",
425 csi->csi_startrec, csi->csi_endrec, csi->csi_startcat,
426 csi->csi_startidx, csi->csi_wrote, rc);
430 if (rc == LLOG_PROC_BREAK)
436 static int mdd_cl_done(struct cl_seq_iter *csi)
439 spin_lock(&csi->csi_mdd->mdd_cl.mc_lock);
440 done = (csi->csi_endrec >= csi->csi_mdd->mdd_cl.mc_index);
441 spin_unlock(&csi->csi_mdd->mdd_cl.mc_lock);
446 static void *mdd_cl_seq_start(struct seq_file *seq, loff_t *pos)
448 struct cl_seq_iter *csi = seq->private;
451 CDEBUG(D_CL, "start "LPU64"-"LPU64" pos="LPU64"\n",
452 csi->csi_startrec, csi->csi_endrec, *pos);
456 if (mdd_cl_done(csi))
457 /* no more records, seq_read should return 0 if buffer
461 if (*pos > csi->csi_pos) {
462 /* The seq_read implementation sucks. It may call start
463 multiple times, using pos to indicate advances, if any,
464 by arbitrarily increasing it by 1. So ignore the actual
465 value of pos, and just register any increase as
466 "seq_read wants the next values". */
467 csi->csi_startrec = csi->csi_endrec + 1;
470 /* else use old startrec/startidx */
475 static void mdd_cl_seq_stop(struct seq_file *seq, void *v)
477 struct cl_seq_iter *csi = seq->private;
479 CDEBUG(D_CL, "stop "LPU64"-"LPU64"\n",
480 csi->csi_startrec, csi->csi_endrec);
483 static void *mdd_cl_seq_next(struct seq_file *seq, void *v, loff_t *pos)
485 struct cl_seq_iter *csi = seq->private;
487 CDEBUG(D_CL, "next "LPU64"-"LPU64" pos="LPU64"\n",
488 csi->csi_startrec, csi->csi_endrec, *pos);
495 struct seq_operations mdd_cl_sops = {
496 .start = mdd_cl_seq_start,
497 .stop = mdd_cl_seq_stop,
498 .next = mdd_cl_seq_next,
499 .show = mdd_cl_seq_show,
502 static int mdd_cl_seq_open(struct inode *inode, struct file *file)
504 struct cl_seq_iter *csi;
505 struct proc_dir_entry *dp = PDE(inode);
506 struct seq_file *seq;
509 LPROCFS_ENTRY_AND_CHECK(dp);
511 rc = seq_open(file, &mdd_cl_sops);
520 csi->csi_mdd = dp->data;
521 seq = file->private_data;
530 static int mdd_cl_seq_release(struct inode *inode, struct file *file)
532 struct seq_file *seq = file->private_data;
533 struct cl_seq_iter *csi = seq->private;
537 return lprocfs_seq_release(inode, file);
540 static loff_t mdd_cl_seq_lseek(struct file *file, loff_t offset, int origin)
542 struct seq_file *seq = (struct seq_file *)file->private_data;
543 struct cl_seq_iter *csi = seq->private;
545 CDEBUG(D_CL, "seek "LPU64"-"LPU64" off="LPU64":%d fpos="LPU64"\n",
546 csi->csi_startrec, csi->csi_endrec, offset, origin, file->f_pos);
552 offset += csi->csi_endrec;
555 spin_lock(&csi->csi_mdd->mdd_cl.mc_lock);
556 offset += csi->csi_mdd->mdd_cl.mc_index;
557 spin_unlock(&csi->csi_mdd->mdd_cl.mc_lock);
568 csi->csi_startrec = offset;
569 csi->csi_endrec = offset ? offset - 1 : 0;
571 /* drop whatever is left in sucky seq_read's buffer */
576 file->f_pos = csi->csi_startrec;
577 return csi->csi_startrec;
580 static ssize_t mdd_cl_seq_read(struct file *file, char __user *buf,
581 size_t count, loff_t *ppos)
583 struct seq_file *seq = (struct seq_file *)file->private_data;
584 struct cl_seq_iter *csi = seq->private;
586 if ((file->f_flags & O_NONBLOCK) && mdd_cl_done(csi))
588 return seq_read(file, buf, count, ppos);
591 static unsigned int mdd_cl_seq_poll(struct file *file, poll_table *wait)
592 { /* based on kmsg_poll */
593 struct seq_file *seq = (struct seq_file *)file->private_data;
594 struct cl_seq_iter *csi = seq->private;
596 poll_wait(file, &csi->csi_mdd->mdd_cl.mc_waitq, wait);
597 if (!mdd_cl_done(csi))
598 return POLLIN | POLLRDNORM;
603 struct file_operations mdd_changelog_fops = {
604 .owner = THIS_MODULE,
605 .open = mdd_cl_seq_open,
606 .read = mdd_cl_seq_read,
607 .write = mdd_cl_seq_write,
608 .llseek = mdd_cl_seq_lseek,
609 .poll = mdd_cl_seq_poll,
610 .release = mdd_cl_seq_release,
613 #ifdef HAVE_QUOTA_SUPPORT
614 static int mdd_lprocfs_quota_rd_type(char *page, char **start, off_t off,
615 int count, int *eof, void *data)
617 struct mdd_device *mdd = data;
618 return lprocfs_quota_rd_type(page, start, off, count, eof,
622 static int mdd_lprocfs_quota_wr_type(struct file *file, const char *buffer,
623 unsigned long count, void *data)
625 struct mdd_device *mdd = data;
626 return lprocfs_quota_wr_type(file, buffer, count, mdd->mdd_obd_dev);
630 static struct lprocfs_vars lprocfs_mdd_obd_vars[] = {
631 { "atime_diff", lprocfs_rd_atime_diff, lprocfs_wr_atime_diff, 0 },
632 { "changelog_mask", lprocfs_rd_cl_mask, lprocfs_wr_cl_mask, 0 },
633 { "changelog", 0, lprocfs_wr_cl, 0, &mdd_changelog_fops, 0600 },
634 #ifdef HAVE_QUOTA_SUPPORT
635 { "quota_type", mdd_lprocfs_quota_rd_type,
636 mdd_lprocfs_quota_wr_type, 0 },
641 static struct lprocfs_vars lprocfs_mdd_module_vars[] = {
642 { "num_refs", lprocfs_rd_numrefs, 0, 0 },
646 void lprocfs_mdd_init_vars(struct lprocfs_static_vars *lvars)
648 lvars->module_vars = lprocfs_mdd_module_vars;
649 lvars->obd_vars = lprocfs_mdd_obd_vars;