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