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