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