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>
49 #include <linux/poll.h>
51 #include <obd_class.h>
52 #include <lustre_ver.h>
53 #include <obd_support.h>
54 #include <lprocfs_status.h>
56 #include <lustre_log.h>
57 #include <lustre/lustre_idl.h>
58 #include <libcfs/libcfs_string.h>
60 #include "mdd_internal.h"
62 #ifndef SEEK_CUR /* SLES10 needs this */
67 static const char *mdd_counter_names[LPROC_MDD_NR] = {
70 /* from LPROC_SEQ_FOPS(mdd_changelog) below */
71 extern struct file_operations mdd_changelog_fops;
73 int mdd_procfs_init(struct mdd_device *mdd, const char *name)
75 struct lprocfs_static_vars lvars;
76 struct lu_device *ld = &mdd->mdd_md_dev.md_lu_dev;
77 struct obd_type *type;
81 type = ld->ld_type->ldt_obd_type;
83 LASSERT(name != NULL);
84 LASSERT(type != NULL);
86 /* Find the type procroot and add the proc entry for this device */
87 lprocfs_mdd_init_vars(&lvars);
88 mdd->mdd_proc_entry = lprocfs_register(name, type->typ_procroot,
90 if (IS_ERR(mdd->mdd_proc_entry)) {
91 rc = PTR_ERR(mdd->mdd_proc_entry);
92 CERROR("Error %d setting up lprocfs for %s\n",
94 mdd->mdd_proc_entry = NULL;
98 rc = lu_time_init(&mdd->mdd_stats,
100 mdd_counter_names, ARRAY_SIZE(mdd_counter_names));
105 mdd_procfs_fini(mdd);
109 int mdd_procfs_fini(struct mdd_device *mdd)
112 lu_time_fini(&mdd->mdd_stats);
114 if (mdd->mdd_proc_entry) {
115 lprocfs_remove(&mdd->mdd_proc_entry);
116 mdd->mdd_proc_entry = NULL;
121 void mdd_lprocfs_time_start(const struct lu_env *env)
123 lu_lprocfs_time_start(env);
126 void mdd_lprocfs_time_end(const struct lu_env *env, struct mdd_device *mdd,
129 lu_lprocfs_time_end(env, mdd->mdd_stats, idx);
132 static int lprocfs_wr_atime_diff(struct file *file, const char *buffer,
133 unsigned long count, void *data)
135 struct mdd_device *mdd = data;
136 char kernbuf[20], *end;
137 unsigned long diff = 0;
139 if (count > (sizeof(kernbuf) - 1))
142 if (copy_from_user(kernbuf, buffer, count))
145 kernbuf[count] = '\0';
147 diff = simple_strtoul(kernbuf, &end, 0);
151 mdd->mdd_atime_diff = diff;
155 static int lprocfs_rd_atime_diff(char *page, char **start, off_t off,
156 int count, int *eof, void *data)
158 struct mdd_device *mdd = data;
161 return snprintf(page, count, "%lu\n", mdd->mdd_atime_diff);
164 /* match enum changelog_rec_type */
165 static const char *changelog_str[] = {"MARK","CREAT","MKDIR","HLINK","SLINK",
166 "MKNOD","UNLNK","RMDIR","RNMFM","RNMTO","OPEN","CLOSE","IOCTL",
167 "TRUNC","SATTR","XATTR"};
169 const char *changelog_bit2str(int bit)
172 return changelog_str[bit];
176 static int lprocfs_rd_cl_mask(char *page, char **start, off_t off,
177 int count, int *eof, void *data)
179 struct mdd_device *mdd = data;
183 while (i < CL_LAST) {
184 if (mdd->mdd_cl.mc_mask & (1 << i))
185 rc += snprintf(page + rc, count - rc, "%s ",
192 static int lprocfs_wr_cl_mask(struct file *file, const char *buffer,
193 unsigned long count, void *data)
195 struct mdd_device *mdd = data;
200 if (count >= CFS_PAGE_SIZE)
202 OBD_ALLOC(kernbuf, CFS_PAGE_SIZE);
205 if (copy_from_user(kernbuf, buffer, count))
206 GOTO(out, rc = -EFAULT);
209 rc = libcfs_str2mask(kernbuf, changelog_bit2str,
210 &mdd->mdd_cl.mc_mask, CL_MINMASK, CL_ALLMASK);
214 OBD_FREE(kernbuf, CFS_PAGE_SIZE);
218 /** struct for holding changelog data for seq_file processing */
220 struct mdd_device *csi_mdd;
230 /* non-seq version for direct calling by class_process_proc_param */
231 static int lprocfs_wr_cl(struct file *file, const char *buffer,
232 unsigned long count, void *data)
234 struct mdd_device *mdd = (struct mdd_device *)data;
239 if (count > (sizeof(kernbuf) - 1))
242 count = min_t(unsigned long, count, sizeof(kernbuf));
243 if (copy_from_user(kernbuf, buffer, count))
246 kernbuf[count] = '\0';
247 /* strip trailing newline from "echo blah" */
248 if (kernbuf[count - 1] == '\n')
249 kernbuf[count - 1] = '\0';
251 if (strcmp(kernbuf, "on") == 0) {
252 LCONSOLE_INFO("changelog on\n");
253 if (mdd->mdd_cl.mc_flags & CLM_ERR) {
254 CERROR("Changelogs cannot be enabled due to error "
257 spin_lock(&mdd->mdd_cl.mc_lock);
258 mdd->mdd_cl.mc_flags |= CLM_ON;
259 spin_unlock(&mdd->mdd_cl.mc_lock);
260 rc = mdd_changelog_write_header(mdd, CLM_START);
264 } else if (strcmp(kernbuf, "off") == 0) {
265 LCONSOLE_INFO("changelog off\n");
266 rc = mdd_changelog_write_header(mdd, CLM_FINI);
269 spin_lock(&mdd->mdd_cl.mc_lock);
270 mdd->mdd_cl.mc_flags &= ~CLM_ON;
271 spin_unlock(&mdd->mdd_cl.mc_lock);
273 /* purge to an index */
274 long long unsigned endrec, cur;
276 spin_lock(&mdd->mdd_cl.mc_lock);
277 cur = (long long)mdd->mdd_cl.mc_index;
278 spin_unlock(&mdd->mdd_cl.mc_lock);
280 if (strcmp(kernbuf, "0") == 0)
281 /* purge to "0" is shorthand for everything */
284 endrec = (long long)simple_strtoull(kernbuf, &end, 0);
285 if ((kernbuf == end) || (endrec == 0))
290 /* If purging all records, write a header entry so we
291 don't have an empty catalog and
292 we're sure to have a valid starting index next time. In
293 case of crash, we just restart with old log so we're
296 rc = mdd_changelog_write_header(mdd, CLM_PURGE);
301 LCONSOLE_INFO("changelog purge to %llu\n", endrec);
303 rc = mdd_changelog_llog_cancel(mdd, endrec);
311 CWARN("changelog write usage: [on|off] | <purge_idx (0=all)>\n");
315 static ssize_t mdd_cl_seq_write(struct file *file, const char *buffer,
316 size_t count, loff_t *off)
318 struct seq_file *seq = file->private_data;
319 struct cl_seq_iter *csi = seq->private;
320 struct mdd_device *mdd = csi->csi_mdd;
322 return lprocfs_wr_cl(file, buffer, count, mdd);
327 /* How many records per seq_show. Too small, we spawn llog_process threads
328 too often; too large, we run out of buffer space */
329 #define CL_CHUNK_SIZE 100
331 static int changelog_show_cb(struct llog_handle *llh, struct llog_rec_hdr *hdr,
334 struct seq_file *seq = (struct seq_file *)data;
335 struct cl_seq_iter *csi = seq->private;
336 struct llog_changelog_rec *rec = (struct llog_changelog_rec *)hdr;
340 if ((rec->cr_hdr.lrh_type != CHANGELOG_REC) ||
341 (rec->cr_type >= CL_LAST)) {
342 CERROR("Not a changelog rec? %d/%d\n", rec->cr_hdr.lrh_type,
347 CDEBUG(D_CL, "rec="LPU64" start="LPU64" cat=%d:%d start=%d:%d\n",
348 rec->cr_index, csi->csi_startrec,
349 llh->lgh_hdr->llh_cat_idx, llh->lgh_cur_idx,
350 csi->csi_startcat, csi->csi_startidx);
352 if (rec->cr_index < csi->csi_startrec)
354 if (rec->cr_index == csi->csi_startrec) {
355 /* Remember where we started, since seq_read will re-read
356 * the data when it reallocs space. Sigh, if only there was
357 * a way to tell seq_file how big the buf should be in the
359 csi->csi_startcat = llh->lgh_hdr->llh_cat_idx;
360 csi->csi_startidx = rec->cr_hdr.lrh_index - 1;
362 if (csi->csi_wrote > CL_CHUNK_SIZE) {
363 /* Stop at some point with a reasonable seq_file buffer size.
364 * Start from here the next time.
366 csi->csi_endrec = rec->cr_index - 1;
367 csi->csi_startcat = llh->lgh_hdr->llh_cat_idx;
368 csi->csi_startidx = rec->cr_hdr.lrh_index - 1;
370 RETURN(LLOG_PROC_BREAK);
373 rc = seq_printf(seq, LPU64" %02d%-5s "LPU64" 0x%x t="DFID,
374 rec->cr_index, rec->cr_type,
375 changelog_str[rec->cr_type], rec->cr_time,
376 rec->cr_flags & CLF_FLAGMASK, PFID(&rec->cr_tfid));
379 /* namespace rec includes parent and filename */
380 rc += seq_printf(seq, " p="DFID" %.*s\n", PFID(&rec->cr_pfid),
381 rec->cr_namelen, rec->cr_name);
383 rc += seq_puts(seq, "\n");
386 /* seq_read will dump the whole buffer and re-seq_start with a
387 larger one; no point in continuing the llog_process */
388 CDEBUG(D_CL, "rec="LPU64" overflow "LPU64"<-"LPU64"\n",
389 rec->cr_index, csi->csi_startrec, csi->csi_endrec);
390 csi->csi_endrec = csi->csi_startrec - 1;
392 RETURN(LLOG_PROC_BREAK);
396 csi->csi_endrec = rec->cr_index;
401 static int mdd_cl_seq_show(struct seq_file *seq, void *v)
403 struct cl_seq_iter *csi = seq->private;
404 struct obd_device *obd = mdd2obd_dev(csi->csi_mdd);
405 struct llog_ctxt *ctxt;
409 /* seq_read wants more data to fill his buffer. But we already
410 filled the buf as much as we cared to; force seq_read to
412 while ((rc = seq_putc(seq, 0)) == 0);
416 ctxt = llog_get_context(obd, LLOG_CHANGELOG_ORIG_CTXT);
420 /* Since we have to restart the llog_cat_process for each chunk of the
421 seq_ functions, start from where we left off. */
422 rc = llog_cat_process(ctxt->loc_handle, changelog_show_cb, seq,
423 csi->csi_startcat, csi->csi_startidx);
425 CDEBUG(D_CL, "seq_show "LPU64"-"LPU64" cat=%d:%d wrote=%d rc=%d\n",
426 csi->csi_startrec, csi->csi_endrec, csi->csi_startcat,
427 csi->csi_startidx, csi->csi_wrote, rc);
431 if (rc == LLOG_PROC_BREAK)
437 static int mdd_cl_done(struct cl_seq_iter *csi)
440 spin_lock(&csi->csi_mdd->mdd_cl.mc_lock);
441 done = (csi->csi_endrec >= csi->csi_mdd->mdd_cl.mc_index);
442 spin_unlock(&csi->csi_mdd->mdd_cl.mc_lock);
447 static void *mdd_cl_seq_start(struct seq_file *seq, loff_t *pos)
449 struct cl_seq_iter *csi = seq->private;
452 CDEBUG(D_CL, "start "LPU64"-"LPU64" pos="LPU64"\n",
453 csi->csi_startrec, csi->csi_endrec, *pos);
457 if (mdd_cl_done(csi))
458 /* no more records, seq_read should return 0 if buffer
462 if (*pos > csi->csi_pos) {
463 /* The seq_read implementation sucks. It may call start
464 multiple times, using pos to indicate advances, if any,
465 by arbitrarily increasing it by 1. So ignore the actual
466 value of pos, and just register any increase as
467 "seq_read wants the next values". */
468 csi->csi_startrec = csi->csi_endrec + 1;
471 /* else use old startrec/startidx */
476 static void mdd_cl_seq_stop(struct seq_file *seq, void *v)
478 struct cl_seq_iter *csi = seq->private;
480 CDEBUG(D_CL, "stop "LPU64"-"LPU64"\n",
481 csi->csi_startrec, csi->csi_endrec);
484 static void *mdd_cl_seq_next(struct seq_file *seq, void *v, loff_t *pos)
486 struct cl_seq_iter *csi = seq->private;
488 CDEBUG(D_CL, "next "LPU64"-"LPU64" pos="LPU64"\n",
489 csi->csi_startrec, csi->csi_endrec, *pos);
496 struct seq_operations mdd_cl_sops = {
497 .start = mdd_cl_seq_start,
498 .stop = mdd_cl_seq_stop,
499 .next = mdd_cl_seq_next,
500 .show = mdd_cl_seq_show,
503 static int mdd_cl_seq_open(struct inode *inode, struct file *file)
505 struct cl_seq_iter *csi;
506 struct proc_dir_entry *dp = PDE(inode);
507 struct seq_file *seq;
510 LPROCFS_ENTRY_AND_CHECK(dp);
512 rc = seq_open(file, &mdd_cl_sops);
521 csi->csi_mdd = dp->data;
522 seq = file->private_data;
531 static int mdd_cl_seq_release(struct inode *inode, struct file *file)
533 struct seq_file *seq = file->private_data;
534 struct cl_seq_iter *csi = seq->private;
538 return lprocfs_seq_release(inode, file);
541 static loff_t mdd_cl_seq_lseek(struct file *file, loff_t offset, int origin)
543 struct seq_file *seq = (struct seq_file *)file->private_data;
544 struct cl_seq_iter *csi = seq->private;
546 CDEBUG(D_CL, "seek "LPU64"-"LPU64" off="LPU64":%d fpos="LPU64"\n",
547 csi->csi_startrec, csi->csi_endrec, offset, origin, file->f_pos);
553 offset += csi->csi_endrec;
556 spin_lock(&csi->csi_mdd->mdd_cl.mc_lock);
557 offset += csi->csi_mdd->mdd_cl.mc_index;
558 spin_unlock(&csi->csi_mdd->mdd_cl.mc_lock);
569 csi->csi_startrec = offset;
570 csi->csi_endrec = offset ? offset - 1 : 0;
572 /* drop whatever is left in sucky seq_read's buffer */
577 file->f_pos = csi->csi_startrec;
578 return csi->csi_startrec;
581 static ssize_t mdd_cl_seq_read(struct file *file, char __user *buf,
582 size_t count, loff_t *ppos)
584 struct seq_file *seq = (struct seq_file *)file->private_data;
585 struct cl_seq_iter *csi = seq->private;
587 if ((file->f_flags & O_NONBLOCK) && mdd_cl_done(csi))
589 return seq_read(file, buf, count, ppos);
592 static unsigned int mdd_cl_seq_poll(struct file *file, poll_table *wait)
593 { /* based on kmsg_poll */
594 struct seq_file *seq = (struct seq_file *)file->private_data;
595 struct cl_seq_iter *csi = seq->private;
597 poll_wait(file, &csi->csi_mdd->mdd_cl.mc_waitq, wait);
598 if (!mdd_cl_done(csi))
599 return POLLIN | POLLRDNORM;
604 struct file_operations mdd_changelog_fops = {
605 .owner = THIS_MODULE,
606 .open = mdd_cl_seq_open,
607 .read = mdd_cl_seq_read,
608 .write = mdd_cl_seq_write,
609 .llseek = mdd_cl_seq_lseek,
610 .poll = mdd_cl_seq_poll,
611 .release = mdd_cl_seq_release,
614 #ifdef HAVE_QUOTA_SUPPORT
615 static int mdd_lprocfs_quota_rd_type(char *page, char **start, off_t off,
616 int count, int *eof, void *data)
618 struct mdd_device *mdd = data;
619 return lprocfs_quota_rd_type(page, start, off, count, eof,
623 static int mdd_lprocfs_quota_wr_type(struct file *file, const char *buffer,
624 unsigned long count, void *data)
626 struct mdd_device *mdd = data;
627 return lprocfs_quota_wr_type(file, buffer, count, mdd->mdd_obd_dev);
631 static struct lprocfs_vars lprocfs_mdd_obd_vars[] = {
632 { "atime_diff", lprocfs_rd_atime_diff, lprocfs_wr_atime_diff, 0 },
633 { "changelog_mask", lprocfs_rd_cl_mask, lprocfs_wr_cl_mask, 0 },
634 { "changelog", 0, lprocfs_wr_cl, 0, &mdd_changelog_fops, 0600 },
635 #ifdef HAVE_QUOTA_SUPPORT
636 { "quota_type", mdd_lprocfs_quota_rd_type,
637 mdd_lprocfs_quota_wr_type, 0 },
642 static struct lprocfs_vars lprocfs_mdd_module_vars[] = {
643 { "num_refs", lprocfs_rd_numrefs, 0, 0 },
647 void lprocfs_mdd_init_vars(struct lprocfs_static_vars *lvars)
649 lvars->module_vars = lprocfs_mdd_module_vars;
650 lvars->obd_vars = lprocfs_mdd_obd_vars;