Whamcloud - gitweb
b=15699
[fs/lustre-release.git] / lustre / mdd / mdd_lproc.c
1 /* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
2  * vim:expandtab:shiftwidth=8:tabstop=8:
3  *
4  * GPL HEADER START
5  *
6  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
7  *
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.
11  *
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).
17  *
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
21  *
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
24  * have any questions.
25  *
26  * GPL HEADER END
27  */
28 /*
29  * Copyright  2008 Sun Microsystems, Inc. All rights reserved
30  * Use is subject to license terms.
31  */
32 /*
33  * This file is part of Lustre, http://www.lustre.org/
34  * Lustre is a trademark of Sun Microsystems, Inc.
35  *
36  * lustre/mdd/mdd_lproc.c
37  *
38  * Lustre Metadata Server (mdd) routines
39  *
40  * Author: Wang Di <wangdi@clusterfs.com>
41  */
42
43 #ifndef EXPORT_SYMTAB
44 # define EXPORT_SYMTAB
45 #endif
46 #define DEBUG_SUBSYSTEM S_MDS
47
48 #include <linux/module.h>
49 #include <obd.h>
50 #include <obd_class.h>
51 #include <lustre_ver.h>
52 #include <obd_support.h>
53 #include <lprocfs_status.h>
54 #include <lu_time.h>
55 #include <lustre_log.h>
56 #include <lustre/lustre_idl.h>
57 #include <libcfs/libcfs_string.h>
58
59 #include "mdd_internal.h"
60
61 #ifndef SEEK_CUR /* SLES10 needs this */
62 #define SEEK_CUR        1
63 #define SEEK_END        2
64 #endif
65
66 static const char *mdd_counter_names[LPROC_MDD_NR] = {
67 };
68
69 /* from LPROC_SEQ_FOPS(mdd_changelog) below */
70 extern struct file_operations mdd_changelog_fops;
71
72 int mdd_procfs_init(struct mdd_device *mdd, const char *name)
73 {
74         struct lprocfs_static_vars lvars;
75         struct lu_device    *ld = &mdd->mdd_md_dev.md_lu_dev;
76         struct obd_type     *type;
77         int                  rc;
78         ENTRY;
79
80         type = ld->ld_type->ldt_obd_type;
81
82         LASSERT(name != NULL);
83         LASSERT(type != NULL);
84
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,
88                                                lvars.obd_vars, mdd);
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",
92                        rc, name);
93                 mdd->mdd_proc_entry = NULL;
94                 GOTO(out, rc);
95         }
96
97         rc = lu_time_init(&mdd->mdd_stats,
98                           mdd->mdd_proc_entry,
99                           mdd_counter_names, ARRAY_SIZE(mdd_counter_names));
100
101         EXIT;
102 out:
103         if (rc)
104                mdd_procfs_fini(mdd);
105         return rc;
106 }
107
108 int mdd_procfs_fini(struct mdd_device *mdd)
109 {
110         if (mdd->mdd_stats)
111                 lu_time_fini(&mdd->mdd_stats);
112
113         if (mdd->mdd_proc_entry) {
114                  lprocfs_remove(&mdd->mdd_proc_entry);
115                  mdd->mdd_proc_entry = NULL;
116         }
117         RETURN(0);
118 }
119
120 void mdd_lprocfs_time_start(const struct lu_env *env)
121 {
122         lu_lprocfs_time_start(env);
123 }
124
125 void mdd_lprocfs_time_end(const struct lu_env *env, struct mdd_device *mdd,
126                           int idx)
127 {
128         lu_lprocfs_time_end(env, mdd->mdd_stats, idx);
129 }
130
131 static int lprocfs_wr_atime_diff(struct file *file, const char *buffer,
132                                  unsigned long count, void *data)
133 {
134         struct mdd_device *mdd = data;
135         char kernbuf[20], *end;
136         unsigned long diff = 0;
137
138         if (count > (sizeof(kernbuf) - 1))
139                 return -EINVAL;
140
141         if (copy_from_user(kernbuf, buffer, count))
142                 return -EFAULT;
143
144         kernbuf[count] = '\0';
145
146         diff = simple_strtoul(kernbuf, &end, 0);
147         if (kernbuf == end)
148                 return -EINVAL;
149
150         mdd->mdd_atime_diff = diff;
151         return count;
152 }
153
154 static int lprocfs_rd_atime_diff(char *page, char **start, off_t off,
155                                  int count, int *eof, void *data)
156 {
157         struct mdd_device *mdd = data;
158
159         *eof = 1;
160         return snprintf(page, count, "%lu\n", mdd->mdd_atime_diff);
161 }
162
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"};
167
168 const char *changelog_bit2str(int bit)
169 {
170         if (bit < CL_LAST)
171                 return changelog_str[bit];
172         return NULL;
173 }
174
175 static int lprocfs_rd_cl_mask(char *page, char **start, off_t off,
176                               int count, int *eof, void *data)
177 {
178         struct mdd_device *mdd = data;
179         int i = 0, rc = 0;
180
181         *eof = 1;
182         while (i < CL_LAST) {
183                 if (mdd->mdd_cl.mc_mask & (1 << i))
184                         rc += snprintf(page + rc, count - rc, "%s ",
185                                        changelog_str[i]);
186                 i++;
187         }
188         return rc;
189 }
190
191 static int lprocfs_wr_cl_mask(struct file *file, const char *buffer,
192                               unsigned long count, void *data)
193 {
194         struct mdd_device *mdd = data;
195         char *kernbuf;
196         int rc;
197         ENTRY;
198
199         if (count >= CFS_PAGE_SIZE)
200                 RETURN(-EINVAL);
201         OBD_ALLOC(kernbuf, CFS_PAGE_SIZE);
202         if (kernbuf == NULL)
203                 RETURN(-ENOMEM);
204         if (copy_from_user(kernbuf, buffer, count))
205                 GOTO(out, rc = -EFAULT);
206         kernbuf[count] = 0;
207
208         rc = libcfs_str2mask(kernbuf, changelog_bit2str,
209                              &mdd->mdd_cl.mc_mask, CL_MINMASK, CL_ALLMASK);
210         if (rc == 0)
211                 rc = count;
212 out:
213         OBD_FREE(kernbuf, CFS_PAGE_SIZE);
214         return rc;
215 }
216
217 /** struct for holding changelog data for seq_file processing */
218 struct cl_seq_iter {
219         struct mdd_device *csi_mdd;
220         __u64 csi_startrec;
221         __u64 csi_endrec;
222         loff_t csi_pos;
223         int csi_wrote;
224         int csi_startcat;
225         int csi_startidx;
226         int csi_fill:1;
227 };
228
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)
232 {
233         struct mdd_device *mdd = (struct mdd_device *)data;
234         char kernbuf[32];
235         char *end;
236         int rc;
237
238         if (count > (sizeof(kernbuf) - 1))
239                 goto out_usage;
240
241         count = min_t(unsigned long, count, sizeof(kernbuf));
242         if (copy_from_user(kernbuf, buffer, count))
243                 return -EFAULT;
244
245         kernbuf[count] = '\0';
246         /* strip trailing newline from "echo blah" */
247         if (kernbuf[count - 1] == '\n')
248                 kernbuf[count - 1] = '\0';
249
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 "
254                                "condition.\n");
255                 } else {
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);
260                         if (rc)
261                               return rc;
262                 }
263         } else if (strcmp(kernbuf, "off") == 0) {
264                 LCONSOLE_INFO("changelog off\n");
265                 rc = mdd_changelog_write_header(mdd, CLM_FINI);
266                 if (rc)
267                       return rc;
268                 spin_lock(&mdd->mdd_cl.mc_lock);
269                 mdd->mdd_cl.mc_flags &= ~CLM_ON;
270                 spin_unlock(&mdd->mdd_cl.mc_lock);
271         } else {
272                 /* purge to an index */
273                 long long unsigned endrec, cur;
274
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);
278
279                 if (strcmp(kernbuf, "0") == 0)
280                         /* purge to "0" is shorthand for everything */
281                         endrec = cur;
282                 else
283                         endrec = (long long)simple_strtoull(kernbuf, &end, 0);
284                 if ((kernbuf == end) || (endrec == 0))
285                         goto out_usage;
286                 if (endrec > cur)
287                         endrec = cur;
288
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
293                    allright. */
294                 if (endrec == cur) {
295                         rc = mdd_changelog_write_header(mdd, CLM_PURGE);
296                         if (rc)
297                               return rc;
298                 }
299
300                 LCONSOLE_INFO("changelog purge to %llu\n", endrec);
301
302                 rc = mdd_changelog_llog_cancel(mdd, endrec);
303                 if (rc < 0)
304                         return rc;
305         }
306
307         return count;
308
309 out_usage:
310         CWARN("changelog write usage: [on|off] | <purge_idx (0=all)>\n");
311         return -EINVAL;
312 }
313
314 static ssize_t mdd_cl_seq_write(struct file *file, const char *buffer,
315                                 size_t count, loff_t *off)
316 {
317         struct seq_file *seq = file->private_data;
318         struct cl_seq_iter *csi = seq->private;
319         struct mdd_device *mdd = csi->csi_mdd;
320
321         return lprocfs_wr_cl(file, buffer, count, mdd);
322 }
323
324 #define D_CL 0
325
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
329
330 static int changelog_show_cb(struct llog_handle *llh, struct llog_rec_hdr *hdr,
331                              void *data)
332 {
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;
336         int rc;
337         ENTRY;
338
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,
342                        rec->cr_type);
343                 RETURN(-EINVAL);
344         }
345
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);
350
351         if (rec->cr_index < csi->csi_startrec)
352                 RETURN(0);
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
357                  * first place... */
358                 csi->csi_startcat = llh->lgh_hdr->llh_cat_idx;
359                 csi->csi_startidx = rec->cr_hdr.lrh_index - 1;
360         }
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.
364                  */
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;
368                 csi->csi_wrote = 0;
369                 RETURN(LLOG_PROC_BREAK);
370         }
371
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));
376
377         if (rec->cr_namelen)
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);
381         else
382                 rc += seq_puts(seq, "\n");
383
384         if (rc < 0) {
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;
390                 csi->csi_wrote = 0;
391                 RETURN(LLOG_PROC_BREAK);
392         }
393
394         csi->csi_wrote++;
395         csi->csi_endrec = rec->cr_index;
396
397         RETURN(0);
398 }
399
400 static int mdd_cl_seq_show(struct seq_file *seq, void *v)
401 {
402         struct cl_seq_iter *csi = seq->private;
403         struct obd_device *obd = mdd2obd_dev(csi->csi_mdd);
404         struct llog_ctxt *ctxt;
405         int rc;
406
407         if (csi->csi_fill) {
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
410                    accept that. */
411                 while ((rc = seq_putc(seq, 0)) == 0);
412                 return 0;
413         }
414
415         ctxt = llog_get_context(obd, LLOG_CHANGELOG_ORIG_CTXT);
416         if (ctxt == NULL)
417                 return -ENOENT;
418
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);
423
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);
427
428         llog_ctxt_put(ctxt);
429
430         if (rc == LLOG_PROC_BREAK)
431                 rc = 0;
432
433         return rc;
434 }
435
436 static int mdd_cl_done(struct cl_seq_iter *csi)
437 {
438         int done = 0;
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);
442         return done;
443 }
444
445
446 static void *mdd_cl_seq_start(struct seq_file *seq, loff_t *pos)
447 {
448         struct cl_seq_iter *csi = seq->private;
449         LASSERT(csi);
450
451         CDEBUG(D_CL, "start "LPU64"-"LPU64" pos="LPU64"\n",
452                csi->csi_startrec, csi->csi_endrec, *pos);
453
454         csi->csi_fill = 0;
455
456         if (mdd_cl_done(csi))
457                 /* no more records, seq_read should return 0 if buffer
458                    is empty */
459                 return NULL;
460
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;
468                 csi->csi_pos = *pos;
469         }
470         /* else use old startrec/startidx */
471
472         return csi;
473 }
474
475 static void mdd_cl_seq_stop(struct seq_file *seq, void *v)
476 {
477         struct cl_seq_iter *csi = seq->private;
478
479         CDEBUG(D_CL, "stop "LPU64"-"LPU64"\n",
480                csi->csi_startrec, csi->csi_endrec);
481 }
482
483 static void *mdd_cl_seq_next(struct seq_file *seq, void *v, loff_t *pos)
484 {
485         struct cl_seq_iter *csi = seq->private;
486
487         CDEBUG(D_CL, "next "LPU64"-"LPU64" pos="LPU64"\n",
488                csi->csi_startrec, csi->csi_endrec, *pos);
489
490         csi->csi_fill = 1;
491
492         return csi;
493 }
494
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,
500 };
501
502 static int mdd_cl_seq_open(struct inode *inode, struct file *file)
503 {
504         struct cl_seq_iter *csi;
505         struct proc_dir_entry *dp = PDE(inode);
506         struct seq_file *seq;
507         int rc;
508
509         LPROCFS_ENTRY_AND_CHECK(dp);
510
511         rc = seq_open(file, &mdd_cl_sops);
512         if (rc)
513                 goto out;
514
515         OBD_ALLOC_PTR(csi);
516         if (csi == NULL) {
517                 rc = -ENOMEM;
518                 goto out;
519         }
520         csi->csi_mdd = dp->data;
521         seq = file->private_data;
522         seq->private = csi;
523
524 out:
525         if (rc)
526                 LPROCFS_EXIT();
527         return rc;
528 }
529
530 static int mdd_cl_seq_release(struct inode *inode, struct file *file)
531 {
532         struct seq_file *seq = file->private_data;
533         struct cl_seq_iter *csi = seq->private;
534
535         OBD_FREE_PTR(csi);
536
537         return lprocfs_seq_release(inode, file);
538 }
539
540 static loff_t mdd_cl_seq_lseek(struct file *file, loff_t offset, int origin)
541 {
542         struct seq_file *seq = (struct seq_file *)file->private_data;
543         struct cl_seq_iter *csi = seq->private;
544
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);
547
548         LL_SEQ_LOCK(seq);
549
550         switch (origin) {
551                 case SEEK_CUR:
552                         offset += csi->csi_endrec;
553                         break;
554                 case SEEK_END:
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);
558                         break;
559         }
560
561         /* SEEK_SET */
562
563         if (offset < 0) {
564                 LL_SEQ_UNLOCK(seq);
565                 return -EINVAL;
566         }
567
568         csi->csi_startrec = offset;
569         csi->csi_endrec = offset ? offset - 1 : 0;
570
571         /* drop whatever is left in sucky seq_read's buffer */
572         seq->count = 0;
573         seq->from = 0;
574         seq->index++;
575         LL_SEQ_UNLOCK(seq);
576         file->f_pos = csi->csi_startrec;
577         return csi->csi_startrec;
578 }
579
580 static ssize_t mdd_cl_seq_read(struct file *file, char __user *buf,
581                                size_t count, loff_t *ppos)
582 {
583         struct seq_file *seq = (struct seq_file *)file->private_data;
584         struct cl_seq_iter *csi = seq->private;
585
586         if ((file->f_flags & O_NONBLOCK) && mdd_cl_done(csi))
587                 return -EAGAIN;
588         return seq_read(file, buf, count, ppos);
589 }
590
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;
595
596         poll_wait(file, &csi->csi_mdd->mdd_cl.mc_waitq, wait);
597         if (!mdd_cl_done(csi))
598                 return POLLIN | POLLRDNORM;
599
600         return 0;
601 }
602
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,
611 };
612
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)
616 {
617         struct mdd_device *mdd = data;
618         return lprocfs_quota_rd_type(page, start, off, count, eof,
619                                      mdd->mdd_obd_dev);
620 }
621
622 static int mdd_lprocfs_quota_wr_type(struct file *file, const char *buffer,
623                                      unsigned long count, void *data)
624 {
625         struct mdd_device *mdd = data;
626         return lprocfs_quota_wr_type(file, buffer, count, mdd->mdd_obd_dev);
627 }
628 #endif
629
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 },
637 #endif
638         { 0 }
639 };
640
641 static struct lprocfs_vars lprocfs_mdd_module_vars[] = {
642         { "num_refs",   lprocfs_rd_numrefs, 0, 0 },
643         { 0 }
644 };
645
646 void lprocfs_mdd_init_vars(struct lprocfs_static_vars *lvars)
647 {
648         lvars->module_vars  = lprocfs_mdd_module_vars;
649         lvars->obd_vars     = lprocfs_mdd_obd_vars;
650 }
651