Whamcloud - gitweb
b=17491
[fs/lustre-release.git] / lustre / lvfs / quotafmt_test.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/lvfs/quotafmt_test.c
37  *
38  * No redistribution or use is permitted outside of Sun Microsystems, Inc.
39  *
40  * Kernel module to test lustre administrative quotafile format APIs
41  * from the OBD setup function
42  */
43
44 #ifndef EXPORT_SYMTAB
45 # define EXPORT_SYMTAB
46 #endif
47
48 #include <linux/module.h>
49 #include <linux/init.h>
50 #include <linux/errno.h>
51 #include <linux/fs.h>
52 #include <linux/kernel.h>
53 #include <linux/random.h>
54
55 #include <lustre_quota.h>
56 #include <obd_class.h>
57
58 #include "lustre_quota_fmt.h"
59
60 #ifdef HAVE_QUOTA_SUPPORT
61
62 char *test_quotafile[2] = { "usrquota_test", "grpquota_test" };
63
64 static int quotfmt_initialize(struct lustre_quota_info *lqi,
65                               struct obd_device *tgt,
66                               struct lvfs_run_ctxt *saved)
67 {
68         struct lustre_disk_dqheader dqhead;
69         static const uint quota_magics[] = LUSTRE_INITQMAGICS;
70         static const uint quota_versions[] = LUSTRE_INITQVERSIONS_V2;
71         struct file *fp;
72         struct inode *parent_inode = tgt->obd_lvfs_ctxt.pwd->d_inode;
73         size_t size;
74         struct dentry *de;
75         int i, rc = 0;
76         ENTRY;
77
78         push_ctxt(saved, &tgt->obd_lvfs_ctxt, NULL);
79
80         for (i = 0; i < MAXQUOTAS; i++) {
81                 loff_t offset = 0;
82                 char *name = test_quotafile[i];
83                 int namelen = strlen(name);
84
85                 /* remove the stale test quotafile */
86                 LOCK_INODE_MUTEX_PARENT(parent_inode);
87                 de = lookup_one_len(name, tgt->obd_lvfs_ctxt.pwd, namelen);
88                 if (!IS_ERR(de) && de->d_inode)
89                         ll_vfs_unlink(parent_inode, de,
90                                       tgt->obd_lvfs_ctxt.pwdmnt);
91                 if (!IS_ERR(de))
92                         dput(de);
93                 UNLOCK_INODE_MUTEX(parent_inode);
94
95                 /* create quota file */
96                 fp = filp_open(name, O_CREAT | O_EXCL, 0644);
97                 if (IS_ERR(fp)) {
98                         rc = PTR_ERR(fp);
99                         CERROR("error creating test quotafile %s (rc = %d)\n",
100                                name, rc);
101                         break;
102                 }
103                 lqi->qi_files[i] = fp;
104
105                 /* write quotafile header */
106                 dqhead.dqh_magic = cpu_to_le32(quota_magics[i]);
107                 dqhead.dqh_version = cpu_to_le32(quota_versions[i]);
108                 size = fp->f_op->write(fp, (char *)&dqhead,
109                                        sizeof(struct lustre_disk_dqheader),
110                                        &offset);
111                 if (size != sizeof(struct lustre_disk_dqheader)) {
112                         CERROR("error writing quotafile header %s (rc = %d)\n",
113                                name, rc);
114                         rc = size;
115                         break;
116                 }
117         }
118
119         RETURN(rc);
120 }
121
122 static int quotfmt_finalize(struct lustre_quota_info *lqi,
123                             struct obd_device *tgt, struct lvfs_run_ctxt *saved)
124 {
125         struct dentry *de;
126         struct inode *parent_inode = tgt->obd_lvfs_ctxt.pwd->d_inode;
127         int i, rc = 0;
128         ENTRY;
129
130         for (i = 0; i < MAXQUOTAS; i++) {
131                 char *name = test_quotafile[i];
132                 int namelen = strlen(name);
133
134                 if (lqi->qi_files[i] == NULL)
135                         continue;
136
137                 /* close quota file */
138                 filp_close(lqi->qi_files[i], 0);
139
140                 /* unlink quota file */
141                 LOCK_INODE_MUTEX_PARENT(parent_inode);
142
143                 de = lookup_one_len(name, tgt->obd_lvfs_ctxt.pwd, namelen);
144                 if (IS_ERR(de) || de->d_inode == NULL) {
145                         rc = IS_ERR(de) ? PTR_ERR(de) : -ENOENT;
146                         CERROR("error lookup quotafile %s (rc = %d)\n",
147                                name, rc);
148                         goto dput;
149                 }
150
151                 rc = ll_vfs_unlink(parent_inode, de, tgt->obd_lvfs_ctxt.pwdmnt);
152                 if (rc)
153                         CERROR("error unlink quotafile %s (rc = %d)\n",
154                                name, rc);
155               dput:
156                 if (!IS_ERR(de))
157                         dput(de);
158                 UNLOCK_INODE_MUTEX(parent_inode);
159         }
160
161         pop_ctxt(saved, &tgt->obd_lvfs_ctxt, NULL);
162         RETURN(rc);
163 }
164
165 static int quotfmt_test_1(struct lustre_quota_info *lqi)
166 {
167         int i;
168         ENTRY;
169
170         for (i = 0; i < MAXQUOTAS; i++) {
171                 if (lustre_check_quota_file(lqi, i))
172                         RETURN(-EINVAL);
173         }
174         RETURN(0);
175 }
176
177 static void print_quota_info(struct lustre_quota_info *lqi)
178 {
179 #if 0
180         struct lustre_mem_dqinfo *dqinfo;
181         int i;
182
183         for (i = 0; i < MAXQUOTAS; i++) {
184                 dqinfo = &lqi->qi_info[i];
185                 CDEBUG(D_INFO, "%s quota info:\n", i == USRQUOTA ? "user " : "group");
186                 CDEBUG(D_INFO, "dqi_bgrace(%u) dqi_igrace(%u) dqi_flags(%lu) dqi_blocks(%u) "
187                        "dqi_free_blk(%u) dqi_free_entry(%u)\n",
188                        dqinfo->dqi_bgrace, dqinfo->dqi_igrace, dqinfo->dqi_flags,
189                        dqinfo->dqi_blocks, dqinfo->dqi_free_blk,
190                        dqinfo->dqi_free_entry);
191         }
192 #endif
193 }
194
195 static int quotfmt_test_2(struct lustre_quota_info *lqi)
196 {
197         int i, rc = 0;
198         ENTRY;
199
200         for (i = 0; i < MAXQUOTAS; i++) {
201                 struct lustre_mem_dqinfo dqinfo;
202
203                 rc = lustre_init_quota_info(lqi, i);
204                 if (rc) {
205                         CERROR("init quotainfo(%d) failed! (rc:%d)\n", i, rc);
206                         break;
207                 }
208                 memcpy(&dqinfo, &lqi->qi_info[i], sizeof(dqinfo));
209
210                 rc = lustre_read_quota_info(lqi, i);
211                 if (rc) {
212                         CERROR("read quotainfo(%d) failed! (rc:%d)\n", i, rc);
213                         break;
214                 }
215
216                 if (memcmp(&dqinfo, &lqi->qi_info[i], sizeof(dqinfo))) {
217                         rc = -EINVAL;
218                         break;
219                 }
220         }
221         RETURN(rc);
222 }
223
224 static struct lustre_dquot *get_rand_dquot(struct lustre_quota_info *lqi)
225 {
226         struct lustre_dquot *dquot;
227         unsigned int rand;
228
229         OBD_ALLOC(dquot, sizeof(*dquot));
230         if (dquot == NULL)
231                 return NULL;
232
233         get_random_bytes(&rand, sizeof(rand));
234         if (!rand)
235                 rand = 1000;
236
237         dquot->dq_info = lqi;
238         dquot->dq_id = rand % 1000 + 1;
239         dquot->dq_type = rand % MAXQUOTAS;
240
241         dquot->dq_dqb.dqb_bhardlimit = rand;
242         dquot->dq_dqb.dqb_bsoftlimit = rand / 2;
243         dquot->dq_dqb.dqb_curspace = rand / 3;
244         dquot->dq_dqb.dqb_ihardlimit = rand;
245         dquot->dq_dqb.dqb_isoftlimit = rand / 2;
246         dquot->dq_dqb.dqb_curinodes = rand / 3;
247         dquot->dq_dqb.dqb_btime = jiffies;
248         dquot->dq_dqb.dqb_itime = jiffies;
249
250         return dquot;
251 }
252
253 static void put_rand_dquot(struct lustre_dquot *dquot)
254 {
255         OBD_FREE(dquot, sizeof(*dquot));
256 }
257
258 static int write_check_dquot(struct lustre_quota_info *lqi)
259 {
260         struct lustre_dquot *dquot;
261         struct lustre_mem_dqblk dqblk;
262         int rc = 0;
263         ENTRY;
264
265         dquot = get_rand_dquot(lqi);
266         if (dquot == NULL)
267                 RETURN(-ENOMEM);
268
269         /* for already exists entry, we set the dq_off by read_dquot */
270         rc = lustre_read_dquot(dquot);
271         if (rc) {
272                 CERROR("read dquot failed! (rc:%d)\n", rc);
273                 GOTO(out, rc);
274         }
275
276         clear_bit(DQ_FAKE_B, &dquot->dq_flags);
277         /* for already exists entry, we rewrite it */
278         rc = lustre_commit_dquot(dquot);
279         if (rc) {
280                 CERROR("commit dquot failed! (rc:%d)\n", rc);
281                 GOTO(out, rc);
282         }
283         memcpy(&dqblk, &dquot->dq_dqb, sizeof(dqblk));
284         memset(&dquot->dq_dqb, 0, sizeof(dqblk));
285
286         rc = lustre_read_dquot(dquot);
287         if (rc) {
288                 CERROR("read dquot failed! (rc:%d)\n", rc);
289                 GOTO(out, rc);
290         }
291
292         if (memcmp(&dqblk, &dquot->dq_dqb, sizeof(dqblk))) {
293                 rc = -EINVAL;
294                 GOTO(out, rc);
295         }
296       out:
297         put_rand_dquot(dquot);
298         RETURN(rc);
299 }
300
301 static int quotfmt_test_3(struct lustre_quota_info *lqi)
302 {
303         struct lustre_dquot *dquot;
304         int i = 0, rc = 0;
305         ENTRY;
306
307         dquot = get_rand_dquot(lqi);
308         if (dquot == NULL)
309                 RETURN(-ENOMEM);
310       repeat:
311         clear_bit(DQ_FAKE_B, &dquot->dq_flags);
312         /* write a new dquot */
313         rc = lustre_commit_dquot(dquot);
314         if (rc) {
315                 CERROR("commit dquot failed! (rc:%d)\n", rc);
316                 GOTO(out, rc);
317         }
318         dquot->dq_off = 0;
319         memset(&dquot->dq_dqb, 0, sizeof(dquot->dq_dqb));
320
321         /* check if this dquot is on disk now */
322         rc = lustre_read_dquot(dquot);
323         if (rc) {
324                 CERROR("read dquot failed! (rc:%d)\n", rc);
325                 GOTO(out, rc);
326         }
327         if (!dquot->dq_off || test_bit(DQ_FAKE_B, &dquot->dq_flags)) {
328                 CERROR("the dquot isn't committed\n");
329                 GOTO(out, rc = -EINVAL);
330         }
331
332         /* remove this dquot */
333         set_bit(DQ_FAKE_B, &dquot->dq_flags);
334         dquot->dq_dqb.dqb_curspace = 0;
335         dquot->dq_dqb.dqb_curinodes = 0;
336         rc = lustre_commit_dquot(dquot);
337         if (rc) {
338                 CERROR("remove dquot failed! (rc:%d)\n", rc);
339                 GOTO(out, rc);
340         }
341
342         /* check if the dquot is really removed */
343         clear_bit(DQ_FAKE_B, &dquot->dq_flags);
344         dquot->dq_off = 0;
345         rc = lustre_read_dquot(dquot);
346         if (rc) {
347                 CERROR("read dquot failed! (rc:%d)\n", rc);
348                 GOTO(out, rc);
349         }
350         if (!test_bit(DQ_FAKE_B, &dquot->dq_flags) || dquot->dq_off) {
351                 CERROR("the dquot isn't removed!\n");
352                 GOTO(out, rc = -EINVAL);
353         }
354
355         /* check if this dquot can be write again */
356         if (++i < 2)
357                 goto repeat;
358
359         print_quota_info(lqi);
360
361       out:
362         put_rand_dquot(dquot);
363         RETURN(rc);
364 }
365
366 static int quotfmt_test_4(struct lustre_quota_info *lqi)
367 {
368         int i, rc = 0;
369         ENTRY;
370
371         for (i = 0; i < 30000; i++) {
372                 rc = write_check_dquot(lqi);
373                 if (rc) {
374                         CERROR("write/check dquot failed at %d! (rc:%d)\n",
375                                i, rc);
376                         break;
377                 }
378         }
379         print_quota_info(lqi);
380         RETURN(rc);
381 }
382
383 static int quotfmt_test_5(struct lustre_quota_info *lqi)
384 {
385 #ifndef KERNEL_SUPPORTS_QUOTA_READ
386         int i, rc = 0;
387
388         for (i = USRQUOTA; i < MAXQUOTAS && !rc; i++) {
389                 struct list_head list;
390                 struct dquot_id *dqid, *tmp;
391
392                 INIT_LIST_HEAD(&list);
393                 rc = lustre_get_qids(lqi->qi_files[i], NULL, i, &list);
394                 if (rc) {
395                         CERROR("%s get all %ss (rc:%d):\n",
396                                rc ? "error" : "success",
397                                i == USRQUOTA ? "uid" : "gid", rc);
398                 }
399                 list_for_each_entry_safe(dqid, tmp, &list, di_link) {
400                         list_del_init(&dqid->di_link);
401                         if (rc == 0)
402                                 CDEBUG(D_INFO, "%d ", dqid->di_id);
403                         kfree(dqid);
404                 }
405                 CDEBUG(D_INFO, "\n");
406         }
407         return rc;
408 #else
409         CWARN("kernel supports quota_read OR kernel version >= 2.6.12, test skipped\n");
410         return 0;
411 #endif
412 }
413
414 static int quotfmt_run_tests(struct obd_device *obd, struct obd_device *tgt)
415 {
416         struct lvfs_run_ctxt saved;
417         struct lustre_quota_info *lqi = NULL;
418         int rc = 0;
419         ENTRY;
420
421         OBD_ALLOC(lqi, sizeof(*lqi));
422         if (lqi == NULL) {
423                 CERROR("not enough memory\n");
424                 RETURN(-ENOMEM);
425         }
426
427         CWARN("=== Initialize quotafile test\n");
428         rc = quotfmt_initialize(lqi, tgt, &saved);
429         if (rc)
430                 GOTO(out, rc);
431
432         CWARN("=== test  1: check quota header\n");
433         rc = quotfmt_test_1(lqi);
434         if (rc) {
435                 CERROR("check quota header failed! (rc:%d)\n", rc);
436                 GOTO(out, rc);
437         }
438
439         CWARN("=== test  2: write/read quota info\n");
440         rc = quotfmt_test_2(lqi);
441         if (rc) {
442                 CERROR("write/read quota info failed! (rc:%d)\n", rc);
443                 GOTO(out, rc);
444         }
445
446         CWARN("=== test  3: write/remove dquot\n");
447         rc = quotfmt_test_3(lqi);
448         if (rc) {
449                 CERROR("write/remove dquot failed! (rc:%d)\n", rc);
450                 GOTO(out, rc);
451         }
452
453         CWARN("=== test  4: write/read 30000 dquot\n");
454         rc = quotfmt_test_4(lqi);
455         if (rc) {
456                 CERROR("write/read 30000 dquot failed\n");
457                 GOTO(out, rc);
458         }
459
460         CWARN("=== test 5: walk through quota file to get all ids\n");
461         rc = quotfmt_test_5(lqi);
462         if (rc) {
463                 CERROR("walk through quota file failed\n");
464                 GOTO(out, rc);
465         }
466
467       out:
468         CWARN("=== Finalize quotafile test\n");
469         rc = quotfmt_finalize(lqi, tgt, &saved);
470         OBD_FREE(lqi, sizeof(*lqi));
471         RETURN(rc);
472 }
473
474 static int quotfmt_test_cleanup(struct obd_device *obd)
475 {
476         ENTRY;
477         lprocfs_obd_cleanup(obd);
478         RETURN(0);
479 }
480
481 static int quotfmt_test_setup(struct obd_device *obd, struct lustre_cfg *lcfg)
482 {
483         struct lprocfs_static_vars lvars;
484         struct obd_device *tgt;
485         int rc;
486         ENTRY;
487
488         if (lcfg->lcfg_bufcount < 1) {
489                 CERROR("requires a mds OBD name\n");
490                 RETURN(-EINVAL);
491         }
492
493         tgt = class_name2obd(lustre_cfg_string(lcfg, 1));
494         if (!tgt || !tgt->obd_attached || !tgt->obd_set_up) {
495                 CERROR("target device not attached or not set up (%s)\n",
496                        lustre_cfg_string(lcfg, 1));
497                 RETURN(-EINVAL);
498         }
499
500         rc = quotfmt_run_tests(obd, tgt);
501         if (rc)
502                 quotfmt_test_cleanup(obd);
503
504         lprocfs_quotfmt_test_init_vars(&lvars);
505         lprocfs_obd_setup(obd, lvars.obd_vars);
506
507         RETURN(rc);
508 }
509
510 static struct obd_ops quotfmt_obd_ops = {
511         .o_owner = THIS_MODULE,
512         .o_setup = quotfmt_test_setup,
513         .o_cleanup = quotfmt_test_cleanup,
514 };
515
516 #ifdef LPROCFS
517 static struct lprocfs_vars lprocfs_quotfmt_test_obd_vars[] = { {0} };
518 static struct lprocfs_vars lprocfs_quotfmt_test_module_vars[] = { {0} };
519
520 void lprocfs_quotfmt_test_init_vars(struct lprocfs_static_vars *lvars)
521 {
522     lvars->module_vars  = lprocfs_quotfmt_test_module_vars;
523     lvars->obd_vars     = lprocfs_quotfmt_test_obd_vars;
524 }
525 #endif
526 static int __init quotfmt_test_init(void)
527 {
528         struct lprocfs_static_vars lvars;
529
530         lprocfs_quotfmt_test_init_vars(&lvars);
531         return class_register_type(&quotfmt_obd_ops, NULL, lvars.module_vars,
532                                    "quotfmt_test", NULL);
533 }
534
535 static void __exit quotfmt_test_exit(void)
536 {
537         class_unregister_type("quotfmt_test");
538 }
539
540 MODULE_AUTHOR("Sun Microsystems, Inc. <http://www.lustre.org/>");
541 MODULE_DESCRIPTION("administrative quotafile test module");
542 MODULE_LICENSE("GPL");
543
544 module_init(quotfmt_test_init);
545 module_exit(quotfmt_test_exit);
546
547 #endif /* HAVE_QUOTA_SUPPORT */