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