Whamcloud - gitweb
b=17896
[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 int mdd_procfs_init(struct mdd_device *mdd, const char *name)
71 {
72         struct lprocfs_static_vars lvars;
73         struct lu_device    *ld = &mdd->mdd_md_dev.md_lu_dev;
74         struct obd_type     *type;
75         int                  rc;
76         ENTRY;
77
78         type = ld->ld_type->ldt_obd_type;
79
80         LASSERT(name != NULL);
81         LASSERT(type != NULL);
82
83         /* Find the type procroot and add the proc entry for this device */
84         lprocfs_mdd_init_vars(&lvars);
85         mdd->mdd_proc_entry = lprocfs_register(name, type->typ_procroot,
86                                                lvars.obd_vars, mdd);
87         if (IS_ERR(mdd->mdd_proc_entry)) {
88                 rc = PTR_ERR(mdd->mdd_proc_entry);
89                 CERROR("Error %d setting up lprocfs for %s\n",
90                        rc, name);
91                 mdd->mdd_proc_entry = NULL;
92                 GOTO(out, rc);
93         }
94
95         rc = lu_time_init(&mdd->mdd_stats,
96                           mdd->mdd_proc_entry,
97                           mdd_counter_names, ARRAY_SIZE(mdd_counter_names));
98
99         EXIT;
100 out:
101         if (rc)
102                mdd_procfs_fini(mdd);
103         return rc;
104 }
105
106 int mdd_procfs_fini(struct mdd_device *mdd)
107 {
108         if (mdd->mdd_stats)
109                 lu_time_fini(&mdd->mdd_stats);
110
111         if (mdd->mdd_proc_entry) {
112                  lprocfs_remove(&mdd->mdd_proc_entry);
113                  mdd->mdd_proc_entry = NULL;
114         }
115         RETURN(0);
116 }
117
118 void mdd_lprocfs_time_start(const struct lu_env *env)
119 {
120         lu_lprocfs_time_start(env);
121 }
122
123 void mdd_lprocfs_time_end(const struct lu_env *env, struct mdd_device *mdd,
124                           int idx)
125 {
126         lu_lprocfs_time_end(env, mdd->mdd_stats, idx);
127 }
128
129 static int lprocfs_wr_atime_diff(struct file *file, const char *buffer,
130                                  unsigned long count, void *data)
131 {
132         struct mdd_device *mdd = data;
133         char kernbuf[20], *end;
134         unsigned long diff = 0;
135
136         if (count > (sizeof(kernbuf) - 1))
137                 return -EINVAL;
138
139         if (copy_from_user(kernbuf, buffer, count))
140                 return -EFAULT;
141
142         kernbuf[count] = '\0';
143
144         diff = simple_strtoul(kernbuf, &end, 0);
145         if (kernbuf == end)
146                 return -EINVAL;
147
148         mdd->mdd_atime_diff = diff;
149         return count;
150 }
151
152 static int lprocfs_rd_atime_diff(char *page, char **start, off_t off,
153                                  int count, int *eof, void *data)
154 {
155         struct mdd_device *mdd = data;
156
157         *eof = 1;
158         return snprintf(page, count, "%lu\n", mdd->mdd_atime_diff);
159 }
160
161
162 /**** changelogs ****/
163 DECLARE_CHANGELOG_NAMES;
164
165 const char *changelog_bit2str(int bit)
166 {
167         if (bit < CL_LAST)
168                 return changelog_str[bit];
169         return NULL;
170 }
171
172 static int lprocfs_rd_changelog_mask(char *page, char **start, off_t off,
173                                      int count, int *eof, void *data)
174 {
175         struct mdd_device *mdd = data;
176         int i = 0, rc = 0;
177
178         *eof = 1;
179         while (i < CL_LAST) {
180                 if (mdd->mdd_cl.mc_mask & (1 << i))
181                         rc += snprintf(page + rc, count - rc, "%s ",
182                                        changelog_str[i]);
183                 i++;
184         }
185         return rc;
186 }
187
188 static int lprocfs_wr_changelog_mask(struct file *file, const char *buffer,
189                                      unsigned long count, void *data)
190 {
191         struct mdd_device *mdd = data;
192         char *kernbuf;
193         int rc;
194         ENTRY;
195
196         if (count >= CFS_PAGE_SIZE)
197                 RETURN(-EINVAL);
198         OBD_ALLOC(kernbuf, CFS_PAGE_SIZE);
199         if (kernbuf == NULL)
200                 RETURN(-ENOMEM);
201         if (copy_from_user(kernbuf, buffer, count))
202                 GOTO(out, rc = -EFAULT);
203         kernbuf[count] = 0;
204
205         rc = libcfs_str2mask(kernbuf, changelog_bit2str, &mdd->mdd_cl.mc_mask,
206                              CHANGELOG_MINMASK, CHANGELOG_ALLMASK);
207         if (rc == 0)
208                 rc = count;
209 out:
210         OBD_FREE(kernbuf, CFS_PAGE_SIZE);
211         return rc;
212 }
213
214 struct cucb_data {
215         char *page;
216         int count;
217         int idx;
218 };
219
220 static int lprocfs_changelog_users_cb(struct llog_handle *llh,
221                                       struct llog_rec_hdr *hdr, void *data)
222 {
223         struct llog_changelog_user_rec *rec;
224         struct cucb_data *cucb = (struct cucb_data *)data;
225
226         LASSERT(llh->lgh_hdr->llh_flags & LLOG_F_IS_PLAIN);
227
228         rec = (struct llog_changelog_user_rec *)hdr;
229
230         cucb->idx += snprintf(cucb->page + cucb->idx, cucb->count - cucb->idx,
231                               CHANGELOG_USER_PREFIX"%-3d "LPU64"\n",
232                               rec->cur_id, rec->cur_endrec);
233         if (cucb->idx >= cucb->count)
234                 return -ENOSPC;
235
236         return 0;
237 }
238
239 static int lprocfs_rd_changelog_users(char *page, char **start, off_t off,
240                                       int count, int *eof, void *data)
241 {
242         struct mdd_device *mdd = data;
243         struct llog_ctxt *ctxt;
244         struct cucb_data cucb;
245         __u64 cur;
246
247         *eof = 1;
248
249         ctxt = llog_get_context(mdd2obd_dev(mdd),LLOG_CHANGELOG_USER_ORIG_CTXT);
250         if (ctxt == NULL)
251                 return -ENXIO;
252         LASSERT(ctxt->loc_handle->lgh_hdr->llh_flags & LLOG_F_IS_CAT);
253
254         spin_lock(&mdd->mdd_cl.mc_lock);
255         cur = mdd->mdd_cl.mc_index;
256         spin_unlock(&mdd->mdd_cl.mc_lock);
257
258         cucb.count = count;
259         cucb.page = page;
260         cucb.idx = 0;
261
262         cucb.idx += snprintf(cucb.page + cucb.idx, cucb.count - cucb.idx,
263                               "current index: "LPU64"\n", cur);
264
265         cucb.idx += snprintf(cucb.page + cucb.idx, cucb.count - cucb.idx,
266                               "%-5s %s\n", "ID", "index");
267
268         llog_cat_process(ctxt->loc_handle, lprocfs_changelog_users_cb,
269                          &cucb, 0, 0);
270
271         llog_ctxt_put(ctxt);
272         return cucb.idx;
273 }
274
275 /* non-seq version for direct calling by class_process_proc_param */
276 static int mdd_changelog_write(struct file *file, const char *buffer,
277                                unsigned long count, void *data)
278 {
279         struct mdd_device *mdd = (struct mdd_device *)data;
280         char kernbuf[32];
281         char *end;
282         int rc;
283
284         if (count > (sizeof(kernbuf) - 1))
285                 goto out_usage;
286
287         count = min_t(unsigned long, count, sizeof(kernbuf));
288         if (copy_from_user(kernbuf, buffer, count))
289                 return -EFAULT;
290
291         kernbuf[count] = '\0';
292         /* strip trailing newline from "echo blah" */
293         if (kernbuf[count - 1] == '\n')
294                 kernbuf[count - 1] = '\0';
295
296         if (strcmp(kernbuf, "on") == 0) {
297                 LCONSOLE_INFO("changelog on\n");
298                 if (mdd->mdd_cl.mc_flags & CLM_ERR) {
299                         CERROR("Changelogs cannot be enabled due to error "
300                                "condition.\n");
301                 } else {
302                         spin_lock(&mdd->mdd_cl.mc_lock);
303                         mdd->mdd_cl.mc_flags |= CLM_ON;
304                         spin_unlock(&mdd->mdd_cl.mc_lock);
305                         rc = mdd_changelog_write_header(mdd, CLM_START);
306                         if (rc)
307                               return rc;
308                 }
309         } else if (strcmp(kernbuf, "off") == 0) {
310                 LCONSOLE_INFO("changelog off\n");
311                 rc = mdd_changelog_write_header(mdd, CLM_FINI);
312                 if (rc)
313                       return rc;
314                 spin_lock(&mdd->mdd_cl.mc_lock);
315                 mdd->mdd_cl.mc_flags &= ~CLM_ON;
316                 spin_unlock(&mdd->mdd_cl.mc_lock);
317         } else {
318                 /* purge to an index */
319                 long long unsigned endrec;
320
321                 endrec = (long long)simple_strtoull(kernbuf, &end, 0);
322                 if (end == kernbuf)
323                         goto out_usage;
324
325                 LCONSOLE_INFO("changelog purge to %llu\n", endrec);
326
327                 rc = mdd_changelog_llog_cancel(mdd, endrec);
328                 if (rc < 0)
329                         return rc;
330         }
331
332         return count;
333
334 out_usage:
335         CWARN("changelog write usage: [on|off] | <purge_idx (0=all)>\n");
336         return -EINVAL;
337 }
338
339 static ssize_t mdd_changelog_seq_write(struct file *file, const char *buffer,
340                                        size_t count, loff_t *off)
341 {
342         struct seq_file *seq = file->private_data;
343         struct changelog_seq_iter *csi = seq->private;
344         struct mdd_device *mdd = (struct mdd_device *)csi->csi_dev;
345
346         return mdd_changelog_write(file, buffer, count, mdd);
347 }
348
349 static int mdd_changelog_done(struct changelog_seq_iter *csi)
350 {
351         struct mdd_device *mdd = (struct mdd_device *)csi->csi_dev;
352         int done = 0;
353
354         spin_lock(&mdd->mdd_cl.mc_lock);
355         done = (csi->csi_endrec >= mdd->mdd_cl.mc_index);
356         spin_unlock(&mdd->mdd_cl.mc_lock);
357         return done;
358 }
359
360 /* handle nonblocking */
361 static ssize_t mdd_changelog_seq_read(struct file *file, char __user *buf,
362                                       size_t count, loff_t *ppos)
363 {
364         struct seq_file *seq = (struct seq_file *)file->private_data;
365         struct changelog_seq_iter *csi = seq->private;
366         int rc;
367         ENTRY;
368
369         if ((file->f_flags & O_NONBLOCK) && mdd_changelog_done(csi))
370                 RETURN(-EAGAIN);
371
372         csi->csi_done = 0;
373         rc = seq_read(file, buf, count, ppos);
374         RETURN(rc);
375 }
376
377 /* handle nonblocking */
378 static unsigned int mdd_changelog_seq_poll(struct file *file, poll_table *wait)
379 {
380         struct seq_file *seq = (struct seq_file *)file->private_data;
381         struct changelog_seq_iter *csi = seq->private;
382         struct mdd_device *mdd = (struct mdd_device *)csi->csi_dev;
383         ENTRY;
384
385         csi->csi_done = 0;
386         poll_wait(file, &mdd->mdd_cl.mc_waitq, wait);
387         if (!mdd_changelog_done(csi))
388                 RETURN(POLLIN | POLLRDNORM);
389
390         RETURN(0);
391 }
392
393 static int mdd_changelog_seq_open(struct inode *inode, struct file *file)
394 {
395         struct changelog_seq_iter *csi;
396         struct obd_device *obd;
397         int rc;
398         ENTRY;
399
400         rc = changelog_seq_open(inode, file, &csi);
401         if (rc)
402                 RETURN(rc);
403
404         /* The proc file is set up with mdd in data, not obd */
405         obd = mdd2obd_dev((struct mdd_device *)csi->csi_dev);
406         csi->csi_ctxt = llog_get_context(obd, LLOG_CHANGELOG_ORIG_CTXT);
407         if (csi->csi_ctxt == NULL) {
408                 changelog_seq_release(inode, file);
409                 RETURN(-ENOENT);
410         }
411         /* The handle is set up in llog_obd_origin_setup */
412         csi->csi_llh = csi->csi_ctxt->loc_handle;
413         RETURN(rc);
414 }
415
416 static int mdd_changelog_seq_release(struct inode *inode, struct file *file)
417 {
418         struct seq_file *seq = file->private_data;
419         struct changelog_seq_iter *csi = seq->private;
420
421         if (csi && csi->csi_ctxt)
422                 llog_ctxt_put(csi->csi_ctxt);
423
424         return (changelog_seq_release(inode, file));
425 }
426
427 /* mdd changelog proc can handle nonblocking ops and writing to purge recs */
428 struct file_operations mdd_changelog_fops = {
429         .owner   = THIS_MODULE,
430         .open    = mdd_changelog_seq_open,
431         .read    = mdd_changelog_seq_read,
432         .write   = mdd_changelog_seq_write,
433         .llseek  = changelog_seq_lseek,
434         .poll    = mdd_changelog_seq_poll,
435         .release = mdd_changelog_seq_release,
436 };
437
438 #ifdef HAVE_QUOTA_SUPPORT
439 static int mdd_lprocfs_quota_rd_type(char *page, char **start, off_t off,
440                                      int count, int *eof, void *data)
441 {
442         struct mdd_device *mdd = data;
443         return lprocfs_quota_rd_type(page, start, off, count, eof,
444                                      mdd->mdd_obd_dev);
445 }
446
447 static int mdd_lprocfs_quota_wr_type(struct file *file, const char *buffer,
448                                      unsigned long count, void *data)
449 {
450         struct mdd_device *mdd = data;
451         return lprocfs_quota_wr_type(file, buffer, count, mdd->mdd_obd_dev);
452 }
453 #endif
454
455 static struct lprocfs_vars lprocfs_mdd_obd_vars[] = {
456         { "atime_diff",      lprocfs_rd_atime_diff, lprocfs_wr_atime_diff, 0 },
457         { "changelog_mask",  lprocfs_rd_changelog_mask,
458                              lprocfs_wr_changelog_mask, 0 },
459         { "changelog_users", lprocfs_rd_changelog_users, 0, 0},
460         { "changelog", 0, mdd_changelog_write, 0, &mdd_changelog_fops, 0600 },
461 #ifdef HAVE_QUOTA_SUPPORT
462         { "quota_type",      mdd_lprocfs_quota_rd_type,
463                              mdd_lprocfs_quota_wr_type, 0 },
464 #endif
465         { 0 }
466 };
467
468 static struct lprocfs_vars lprocfs_mdd_module_vars[] = {
469         { "num_refs",   lprocfs_rd_numrefs, 0, 0 },
470         { 0 }
471 };
472
473 void lprocfs_mdd_init_vars(struct lprocfs_static_vars *lvars)
474 {
475         lvars->module_vars  = lprocfs_mdd_module_vars;
476         lvars->obd_vars     = lprocfs_mdd_obd_vars;
477 }
478