Whamcloud - gitweb
Branch b1_4_mountconf
[fs/lustre-release.git] / lustre / mds / quota_master.c
1 /* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
2  * vim:expandtab:shiftwidth=8:tabstop=8:
3  *
4  *  lustre/mds/quota_master.c
5  *  Lustre Quota Master request handler
6  *
7  *  Copyright (c) 2001-2003 Cluster File Systems, Inc.
8  *   Author: Niu YaWei <niu@clusterfs.com>
9  *
10  *   This file is part of Lustre, http://www.lustre.org.
11  *
12  *   No redistribution or use is permitted outside of Cluster File Systems, Inc.
13  *
14  */
15 #ifndef EXPORT_SYMTAB
16 # define EXPORT_SYMTAB
17 #endif
18
19 #define DEBUG_SUBSYSTEM S_MDS
20
21 #include <linux/version.h>
22 #include <linux/fs.h>
23 #include <asm/unistd.h>
24 #include <linux/slab.h>
25 #include <linux/quotaops.h>
26 #include <linux/module.h>
27 #include <linux/init.h>
28 #include <linux/quota.h>
29
30 #include <linux/obd_class.h>
31 #include <linux/lustre_quota.h>
32 #include <linux/lustre_fsfilt.h>
33 #include <linux/lustre_mds.h>
34
35 #include "mds_internal.h"
36
37 static struct list_head lustre_dquot_hash[NR_DQHASH];
38 static spinlock_t dquot_hash_lock = SPIN_LOCK_UNLOCKED;
39
40 kmem_cache_t *lustre_dquot_cachep;
41
42 int lustre_dquot_init(void)
43 {
44         int i;
45         ENTRY;
46
47         LASSERT(lustre_dquot_cachep == NULL);
48         lustre_dquot_cachep = kmem_cache_create("lustre_dquot_cache",
49                                                 sizeof(struct lustre_dquot),
50                                                 0, 0, NULL, NULL);
51         if (!lustre_dquot_cachep)
52                 return (-ENOMEM);
53
54         for (i = 0; i < NR_DQHASH; i++) {
55                 INIT_LIST_HEAD(lustre_dquot_hash + i);
56         }
57         RETURN(0);
58 }
59
60 void lustre_dquot_exit(void)
61 {
62         int i;
63         ENTRY;
64         /* FIXME cleanup work ?? */
65
66         for (i = 0; i < NR_DQHASH; i++) {
67                 LASSERT(list_empty(lustre_dquot_hash + i));
68         }
69         if (lustre_dquot_cachep) {
70                 LASSERTF(kmem_cache_destroy(lustre_dquot_cachep) == 0,
71                          "Cannot destroy lustre_dquot_cache\n");
72                 lustre_dquot_cachep = NULL;
73         }
74         EXIT;
75 }
76
77 static inline int const dquot_hashfn(struct lustre_quota_info *info,
78                                      unsigned int id, int type)
79 {
80         unsigned long tmp = ((unsigned long)info >> L1_CACHE_SHIFT) ^ id;
81         tmp = (tmp * (MAXQUOTAS - type)) % NR_DQHASH;
82         return tmp;
83 }
84
85 static struct lustre_dquot *find_dquot(int hashent,
86                                        struct lustre_quota_info *lqi, qid_t id,
87                                        int type)
88 {
89         struct list_head *head;
90         struct lustre_dquot *dquot;
91         ENTRY;
92
93         for (head = lustre_dquot_hash[hashent].next;
94              head != lustre_dquot_hash + hashent; head = head->next) {
95                 dquot = list_entry(head, struct lustre_dquot, dq_hash);
96                 if (dquot->dq_info == lqi &&
97                     dquot->dq_id == id && dquot->dq_type == type)
98                         RETURN(dquot);
99         }
100         RETURN(NULL);
101 }
102
103 static struct lustre_dquot *alloc_dquot(struct lustre_quota_info *lqi,
104                                         qid_t id, int type)
105 {
106         struct lustre_dquot *dquot = NULL;
107         ENTRY;
108
109         OBD_SLAB_ALLOC(dquot, lustre_dquot_cachep, SLAB_NOFS, sizeof(*dquot));
110         if (dquot == NULL)
111                 RETURN(NULL);
112
113         INIT_LIST_HEAD(&dquot->dq_hash);
114         INIT_LIST_HEAD(&dquot->dq_unused);
115         sema_init(&dquot->dq_sem, 1);
116         atomic_set(&dquot->dq_refcnt, 1);
117         dquot->dq_info = lqi;
118         dquot->dq_id = id;
119         dquot->dq_type = type;
120
121         RETURN(dquot);
122 }
123
124 static void free_dquot(struct lustre_dquot *dquot)
125 {
126         OBD_SLAB_FREE(dquot, lustre_dquot_cachep, sizeof(*dquot));
127 }
128
129 static void insert_dquot_nolock(struct lustre_dquot *dquot)
130 {
131         struct list_head *head = lustre_dquot_hash +
132             dquot_hashfn(dquot->dq_info, dquot->dq_id, dquot->dq_type);
133         list_add(&dquot->dq_hash, head);
134 }
135
136 static void remove_dquot_nolock(struct lustre_dquot *dquot)
137 {
138         LASSERT(!list_empty(&dquot->dq_hash));
139         list_del_init(&dquot->dq_hash);
140 }
141
142 static void lustre_dqput(struct lustre_dquot *dquot)
143 {
144         ENTRY;
145         spin_lock(&dquot_hash_lock);
146         LASSERT(atomic_read(&dquot->dq_refcnt));
147         if (atomic_dec_and_test(&dquot->dq_refcnt)) {
148                 remove_dquot_nolock(dquot);
149                 free_dquot(dquot);
150         }
151         spin_unlock(&dquot_hash_lock);
152         EXIT;
153 }
154
155 #define DQUOT_DEBUG(dquot, fmt, arg...)                                       \
156         CDEBUG(D_QUOTA, "refcnt(%u) id(%u) type(%u) off(%llu) flags(%lu) " \
157                "bhardlimit(%u) curspace("LPX64") ihardlimit(%u) "             \
158                "curinodes(%u): " fmt, atomic_read(&dquot->dq_refcnt),         \
159                dquot->dq_id, dquot->dq_type, dquot->dq_off,  dquot->dq_flags, \
160                dquot->dq_dqb.dqb_bhardlimit, dquot->dq_dqb.dqb_curspace,      \
161                dquot->dq_dqb.dqb_ihardlimit, dquot->dq_dqb.dqb_curinodes,     \
162                ## arg);                                                       \
163
164 #define QINFO_DEBUG(qinfo, fmt, arg...)                                       \
165         CDEBUG(D_QUOTA, "files (%p/%p) flags(%lu/%lu) blocks(%u/%u) "         \
166                "free_blk(/%u/%u) free_entry(%u/%u): " fmt,                    \
167                qinfo->qi_files[0], qinfo->qi_files[1],                        \
168                qinfo->qi_info[0].dqi_flags, qinfo->qi_info[1].dqi_flags,      \
169                qinfo->qi_info[0].dqi_blocks, qinfo->qi_info[1].dqi_blocks,    \
170                qinfo->qi_info[0].dqi_free_blk, qinfo->qi_info[1].dqi_free_blk,\
171                qinfo->qi_info[0].dqi_free_entry,                              \
172                qinfo->qi_info[1].dqi_free_entry, ## arg);
173
174 static struct lustre_dquot *lustre_dqget(struct obd_device *obd,
175                                          struct lustre_quota_info *lqi,
176                                          qid_t id, int type)
177 {
178         unsigned int hashent = dquot_hashfn(lqi, id, type);
179         struct lustre_dquot *dquot = NULL;
180         int read = 0;
181         ENTRY;
182
183         spin_lock(&dquot_hash_lock);
184         if ((dquot = find_dquot(hashent, lqi, id, type)) != NULL) {
185                 atomic_inc(&dquot->dq_refcnt);
186         } else {
187                 dquot = alloc_dquot(lqi, id, type);
188                 if (dquot) {
189                         insert_dquot_nolock(dquot);
190                         read = 1;
191                 }
192         }
193         spin_unlock(&dquot_hash_lock);
194
195         if (dquot == NULL)
196                 RETURN(ERR_PTR(-ENOMEM));
197
198         if (read) {
199                 int rc = 0;
200
201                 down(&dquot->dq_info->qi_sem);
202                 down(&dquot->dq_sem);
203                 rc = fsfilt_dquot(obd, dquot, QFILE_RD_DQUOT);
204                 up(&dquot->dq_sem);
205                 up(&dquot->dq_info->qi_sem);
206                 if (rc) {
207                         CERROR("can't read dquot from admin qutoafile! "
208                                "(rc:%d)\n", rc);
209                         lustre_dqput(dquot);
210                         RETURN(ERR_PTR(rc));
211                 }
212         }
213         RETURN(dquot);
214 }
215
216 int dqacq_handler(struct obd_device *obd, struct qunit_data *qdata, int opc)
217 {
218         struct mds_obd *mds = &obd->u.mds;
219         struct lustre_quota_info *info = &mds->mds_quota_info;
220         struct lustre_dquot *dquot = NULL;
221         __u64 *usage = NULL;
222         __u32 hlimit = 0, slimit = 0;
223         time_t *time = NULL;
224         unsigned int grace = 0;
225         int rc = 0;
226         ENTRY;
227
228         /* slaves never acquires qunit for user root */
229         LASSERT(qdata->qd_id || qdata->qd_type == GRPQUOTA);
230
231         dquot = lustre_dqget(obd, info, qdata->qd_id, qdata->qd_type);
232         if (IS_ERR(dquot))
233                 RETURN(PTR_ERR(dquot));
234
235         DQUOT_DEBUG(dquot, "get dquot in dqacq_handler\n");
236         QINFO_DEBUG(dquot->dq_info, "get dquot in dqadq_handler\n");
237
238         down(&dquot->dq_info->qi_sem);
239         down(&dquot->dq_sem);
240
241         if (qdata->qd_isblk) {
242                 grace = info->qi_info[qdata->qd_type].dqi_bgrace;
243                 usage = &dquot->dq_dqb.dqb_curspace;
244                 hlimit = dquot->dq_dqb.dqb_bhardlimit;
245                 slimit = dquot->dq_dqb.dqb_bsoftlimit;
246                 time = &dquot->dq_dqb.dqb_btime;
247         } else {
248                 grace = info->qi_info[qdata->qd_type].dqi_igrace;
249                 usage = (__u64 *) & dquot->dq_dqb.dqb_curinodes;
250                 hlimit = dquot->dq_dqb.dqb_ihardlimit;
251                 slimit = dquot->dq_dqb.dqb_isoftlimit;
252                 time = &dquot->dq_dqb.dqb_itime;
253         } 
254
255         /* if the quota limit in admin quotafile is zero, we just inform
256          * slave to clear quota limit with zero qd_count */
257         if (hlimit == 0 && slimit == 0) {
258                 qdata->qd_count = 0;
259                 GOTO(out, rc);
260         }
261         
262         if (opc == QUOTA_DQACQ) {
263                 if (hlimit && 
264                     QUSG(*usage + qdata->qd_count, qdata->qd_isblk) > hlimit)
265                         GOTO(out, rc = -EDQUOT);
266
267                 if (slimit &&
268                     QUSG(*usage + qdata->qd_count, qdata->qd_isblk) > slimit) {
269                         if (*time && CURRENT_SECONDS >= *time)
270                                 GOTO(out, rc = -EDQUOT);
271                         else if (!*time)
272                                 *time = CURRENT_SECONDS + grace;
273                 }
274
275                 *usage += qdata->qd_count;
276                 
277         } else if (opc == QUOTA_DQREL) {
278                 LASSERT(*usage - qdata->qd_count >= 0);
279                 *usage -= qdata->qd_count;
280
281                 /* (usage <= soft limit) but not (usage < soft limit) */
282                 if (!slimit || QUSG(*usage, qdata->qd_isblk) <= slimit)
283                         *time = 0;
284         } else {
285                 LBUG();
286         }
287
288         rc = fsfilt_dquot(obd, dquot, QFILE_WR_DQUOT);
289 out:
290         up(&dquot->dq_sem);
291         up(&dquot->dq_info->qi_sem);
292         lustre_dqput(dquot);
293         RETURN(rc);
294 }
295
296 void mds_adjust_qunit(struct obd_device *obd, uid_t cuid, gid_t cgid,
297                       uid_t puid, gid_t pgid, int rc)
298 {
299         struct mds_obd *mds = &obd->u.mds;
300         struct lustre_quota_ctxt *qctxt = &mds->mds_quota_ctxt;
301         ENTRY;
302
303         if (rc && rc != -EDQUOT) {
304                 EXIT;
305                 return;
306         }
307         /* dqacq/dqrel file quota on owner of child */
308         rc = qctxt_adjust_qunit(obd, qctxt, cuid, cgid, 0);
309         if (rc)
310                 CERROR("error mds adjust child qunit! (rc:%d)\n", rc);
311         /* dqacq/dqrel block quota on owner of parent directory */
312         rc = qctxt_adjust_qunit(obd, qctxt, puid, pgid, 1);
313         if (rc)
314                 CERROR("error mds adjust parent qunit! (rc:%d)\n", rc);
315         EXIT;
316 }
317
318 int init_admin_quotafiles(struct obd_device *obd, struct obd_quotactl *oqctl)
319 {
320         struct mds_obd *mds = &obd->u.mds;
321         struct lustre_quota_info *qinfo = &mds->mds_quota_info;
322         const char *quotafiles[] = LUSTRE_ADMIN_QUOTAFILES;
323         struct lvfs_run_ctxt saved;
324         char name[64];
325         int i, rc = 0;
326         struct dentry *dparent = mds->mds_objects_dir;
327         struct inode *iparent = dparent->d_inode;
328         ENTRY;
329
330         LASSERT(iparent);
331         push_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL);
332
333         down(&qinfo->qi_sem);
334         for (i = 0; i < MAXQUOTAS; i++) {
335                 struct dentry *de = NULL;
336                 struct file *fp = NULL;
337
338                 if (!Q_TYPESET(oqctl, i))
339                         continue;
340
341                 /* quota file has been opened ? */
342                 if (qinfo->qi_files[i]) {
343                         CWARN("init %s admin quotafile while quota on.\n",
344                               i == USRQUOTA ? "user" : "group");
345                         continue;
346                 }
347
348                 /* lookup quota file */
349                 rc = 0;
350                 down(&iparent->i_sem);
351
352                 de = lookup_one_len(quotafiles[i], dparent,
353                                     strlen(quotafiles[i]));
354                 if (IS_ERR(de) || de->d_inode == NULL)
355                         rc = IS_ERR(de) ? PTR_ERR(de) : -ENOENT;
356                 if (!IS_ERR(de))
357                         dput(de);
358                 up(&iparent->i_sem);
359
360                 if (rc && rc != -ENOENT) {
361                         CERROR("error lookup quotafile %s! (rc:%d)\n",
362                                name, rc);
363                         break;
364                 } else if (!rc) {
365                         continue;
366                 }
367
368                 sprintf(name, "OBJECTS/%s", quotafiles[i]);
369
370                 LASSERT(rc == -ENOENT);
371                 /* create quota file */
372                 fp = filp_open(name, O_CREAT | O_EXCL, 0644);
373                 if (IS_ERR(fp)) {
374                         rc = PTR_ERR(fp);
375                         CERROR("error creating admin quotafile %s (rc:%d)\n",
376                                name, rc);
377                         break;
378                 }
379
380                 qinfo->qi_files[i] = fp;
381                 rc = fsfilt_quotainfo(obd, qinfo, i, QFILE_INIT_INFO);
382                 filp_close(fp, 0);
383                 qinfo->qi_files[i] = NULL;
384
385                 if (rc) {
386                         CERROR("error init %s admin quotafile! (rc:%d)\n",
387                                i == USRQUOTA ? "user" : "group", rc);
388                         break;
389                 }
390         }
391         up(&qinfo->qi_sem);
392
393         pop_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL);
394         RETURN(rc);
395 }
396
397 int mds_quota_on(struct obd_device *obd, struct obd_quotactl *oqctl)
398 {
399         struct mds_obd *mds = &obd->u.mds;
400         struct lustre_quota_info *qinfo = &mds->mds_quota_info;
401         const char *quotafiles[] = LUSTRE_ADMIN_QUOTAFILES;
402         struct lvfs_run_ctxt saved;
403         char name[64];
404         int i, rc = 0;
405         struct inode *iparent = mds->mds_objects_dir->d_inode;
406         ENTRY;
407
408         LASSERT(iparent);
409         push_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL);
410
411         down(&qinfo->qi_sem);
412         /* open admin quota files and read quotafile info */
413         for (i = 0; i < MAXQUOTAS; i++) {
414                 struct file *fp = NULL;
415
416                 if (!Q_TYPESET(oqctl, i))
417                         continue;
418
419                 sprintf(name, "OBJECTS/%s", quotafiles[i]);
420
421                 if (qinfo->qi_files[i] != NULL) {
422                         rc = -EBUSY;
423                         break;
424                 }
425
426                 fp = filp_open(name, O_RDWR | O_EXCL, 0644);
427                 if (IS_ERR(fp)) {
428                         rc = PTR_ERR(fp);
429                         CERROR("error open %s! (rc:%d)\n", name, rc);
430                         break;
431                 }
432                 qinfo->qi_files[i] = fp;
433
434                 rc = fsfilt_quotainfo(obd, qinfo, i, QFILE_RD_INFO);
435                 if (rc) {
436                         CERROR("error read quotainfo of %s! (rc:%d)\n",
437                                name, rc);
438                         break;
439                 }
440         }
441         up(&qinfo->qi_sem);
442
443         pop_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL);
444
445         if (rc && rc != -EBUSY) {
446                 down(&qinfo->qi_sem);
447                 for (i = 0; i < MAXQUOTAS; i++) {
448                         if (!Q_TYPESET(oqctl, i))
449                                 continue;
450                         if (qinfo->qi_files[i])
451                                 filp_close(qinfo->qi_files[i], 0);
452                         qinfo->qi_files[i] = NULL;
453                 }
454                 up(&qinfo->qi_sem);
455         }
456         RETURN(rc);
457 }
458
459 int mds_quota_off(struct obd_device *obd, struct obd_quotactl *oqctl)
460 {
461         struct mds_obd *mds = &obd->u.mds;
462         struct lustre_quota_info *qinfo = &mds->mds_quota_info;
463         int i, rc = 0;
464         ENTRY;
465
466         down(&qinfo->qi_sem);
467         /* close admin quota files */
468         for (i = 0; i < MAXQUOTAS; i++) {
469                 if (!Q_TYPESET(oqctl, i))
470                         continue;
471
472                 if (qinfo->qi_files[i] == NULL) {
473                         rc = -ESRCH;
474                         continue;
475                 }
476                 filp_close(qinfo->qi_files[i], 0);
477                 qinfo->qi_files[i] = NULL;
478         }
479         up(&qinfo->qi_sem);
480
481         RETURN(rc);
482 }
483
484 int mds_set_dqinfo(struct obd_device *obd, struct obd_quotactl *oqctl)
485 {
486         struct mds_obd *mds = &obd->u.mds;
487         struct lustre_quota_info *qinfo = &mds->mds_quota_info;
488         struct obd_dqinfo *dqinfo = &oqctl->qc_dqinfo;
489         int rc = 0;
490         ENTRY;
491
492         if (qinfo->qi_files[oqctl->qc_type] == NULL)
493                 RETURN(-ESRCH);
494
495         down(&qinfo->qi_sem);
496         qinfo->qi_info[oqctl->qc_type].dqi_bgrace = dqinfo->dqi_bgrace;
497         qinfo->qi_info[oqctl->qc_type].dqi_igrace = dqinfo->dqi_igrace;
498         qinfo->qi_info[oqctl->qc_type].dqi_flags = dqinfo->dqi_flags;
499
500         rc = fsfilt_quotainfo(obd, qinfo, oqctl->qc_type, QFILE_WR_INFO);
501         up(&qinfo->qi_sem);
502
503         RETURN(rc);
504 }
505
506 int mds_get_dqinfo(struct obd_device *obd, struct obd_quotactl *oqctl)
507 {
508         struct mds_obd *mds = &obd->u.mds;
509         struct lustre_quota_info *qinfo = &mds->mds_quota_info;
510         struct obd_dqinfo *dqinfo = &oqctl->qc_dqinfo;
511         ENTRY;
512
513         if (qinfo->qi_files[oqctl->qc_type] == NULL)
514                 RETURN(-ESRCH);
515
516         down(&qinfo->qi_sem);
517         dqinfo->dqi_bgrace = qinfo->qi_info[oqctl->qc_type].dqi_bgrace;
518         dqinfo->dqi_igrace = qinfo->qi_info[oqctl->qc_type].dqi_igrace;
519         dqinfo->dqi_flags = qinfo->qi_info[oqctl->qc_type].dqi_flags;
520         up(&qinfo->qi_sem);
521
522         RETURN(0);
523 }
524
525 static int mds_init_slave_ilimits(struct obd_device *obd,
526                                   struct obd_quotactl *oqctl)
527 {
528         /* XXX: for file limits only adjust local now */
529         struct mds_obd *mds = &obd->u.mds;
530         unsigned int uid = 0, gid = 0;
531         struct obd_quotactl *ioqc;
532         int rc;
533         ENTRY;
534
535         /* if we are going to set zero limit, needn't init slaves */
536         if (!oqctl->qc_dqblk.dqb_ihardlimit && !oqctl->qc_dqblk.dqb_isoftlimit)
537                 RETURN(0);
538
539         OBD_ALLOC(ioqc, sizeof(*ioqc));
540         if (!ioqc)
541                 RETURN(-ENOMEM);
542
543         ioqc->qc_cmd = Q_SETQUOTA;
544         ioqc->qc_id = oqctl->qc_id;
545         ioqc->qc_type = oqctl->qc_type;
546         ioqc->qc_dqblk.dqb_valid = QIF_ILIMITS;
547         ioqc->qc_dqblk.dqb_ihardlimit = MIN_QLIMIT;
548
549         /* set local limit to MIN_QLIMIT */
550         rc = fsfilt_quotactl(obd, mds->mds_sb, ioqc);
551         if (rc)
552                 GOTO(out, rc);
553
554         /* trigger local qunit pre-acquire */
555         if (oqctl->qc_type == USRQUOTA)
556                 uid = oqctl->qc_id;
557         else
558                 gid = oqctl->qc_id;
559
560         rc = qctxt_adjust_qunit(obd, &mds->mds_quota_ctxt, uid, gid, 0);
561         if (rc) {
562                 CERROR("error mds adjust local file quota! (rc:%d)\n", rc);
563                 GOTO(out, rc);
564         }
565         /* FIXME initialize all slaves in CMD */
566 out:
567         OBD_FREE(ioqc, sizeof(*ioqc));
568         RETURN(rc);
569 }
570
571 static int mds_init_slave_blimits(struct obd_device *obd,
572                                   struct obd_quotactl *oqctl)
573 {
574         struct mds_obd *mds = &obd->u.mds;
575         struct obd_quotactl *ioqc;
576         unsigned int uid = 0, gid = 0;
577         int rc;
578         ENTRY;
579
580         /* if we are going to set zero limit, needn't init slaves */
581         if (!oqctl->qc_dqblk.dqb_bhardlimit && !oqctl->qc_dqblk.dqb_bsoftlimit)
582                 RETURN(0);
583
584         OBD_ALLOC(ioqc, sizeof(*ioqc));
585         if (!ioqc)
586                 RETURN(-ENOMEM);
587
588         ioqc->qc_cmd = Q_SETQUOTA;
589         ioqc->qc_id = oqctl->qc_id;
590         ioqc->qc_type = oqctl->qc_type;
591         ioqc->qc_dqblk.dqb_valid = QIF_BLIMITS;
592         ioqc->qc_dqblk.dqb_bhardlimit = MIN_QLIMIT;
593
594         /* set local limit to MIN_QLIMIT */
595         rc = fsfilt_quotactl(obd, mds->mds_sb, ioqc);
596         if (rc)
597                 GOTO(out, rc);
598
599         /* trigger local qunit pre-acquire */
600         if (oqctl->qc_type == USRQUOTA)
601                 uid = oqctl->qc_id;
602         else
603                 gid = oqctl->qc_id;
604
605         rc = qctxt_adjust_qunit(obd, &mds->mds_quota_ctxt, uid, gid, 1);
606         if (rc) {
607                 CERROR("error mds adjust local block quota! (rc:%d)\n", rc);
608                 GOTO(out, rc);
609         }
610
611         /* initialize all slave's limit */
612         ioqc->qc_cmd = Q_INITQUOTA;
613         rc = obd_quotactl(mds->mds_osc_exp, ioqc);
614 out:
615         OBD_FREE(ioqc, sizeof(*ioqc));
616         RETURN(rc);
617 }
618
619 int mds_set_dqblk(struct obd_device *obd, struct obd_quotactl *oqctl)
620 {
621         struct mds_obd *mds = &obd->u.mds;
622         struct lustre_quota_info *qinfo = &mds->mds_quota_info;
623         __u32 ihardlimit, isoftlimit, bhardlimit, bsoftlimit;
624         time_t btime, itime;
625         struct lustre_dquot *dquot;
626         struct obd_dqblk *dqblk = &oqctl->qc_dqblk;
627         int rc = 0;
628         ENTRY;
629
630         if (qinfo->qi_files[oqctl->qc_type] == NULL)
631                 RETURN(-ESRCH);
632
633         dquot = lustre_dqget(obd, qinfo, oqctl->qc_id, oqctl->qc_type);
634         if (IS_ERR(dquot))
635                 RETURN(PTR_ERR(dquot));
636         DQUOT_DEBUG(dquot, "get dquot in mds_set_blk\n");
637         QINFO_DEBUG(dquot->dq_info, "get dquot in mds_set_blk\n");
638
639         down(&dquot->dq_info->qi_sem);
640         down(&dquot->dq_sem);
641
642         ihardlimit = dquot->dq_dqb.dqb_ihardlimit;
643         isoftlimit = dquot->dq_dqb.dqb_isoftlimit;
644         bhardlimit = dquot->dq_dqb.dqb_bhardlimit;
645         bsoftlimit = dquot->dq_dqb.dqb_bsoftlimit;
646         btime = dquot->dq_dqb.dqb_btime;
647         itime = dquot->dq_dqb.dqb_itime;
648
649         if (dqblk->dqb_valid & QIF_BLIMITS) {
650                 dquot->dq_dqb.dqb_bhardlimit = dqblk->dqb_bhardlimit;
651                 dquot->dq_dqb.dqb_bsoftlimit = dqblk->dqb_bsoftlimit;
652                 /* clear usage (limit pool) */
653                 if (!dquot->dq_dqb.dqb_bhardlimit && 
654                     !dquot->dq_dqb.dqb_bsoftlimit)
655                         dquot->dq_dqb.dqb_curspace = 0;
656         }
657
658         if (dqblk->dqb_valid & QIF_ILIMITS) {
659                 dquot->dq_dqb.dqb_ihardlimit = dqblk->dqb_ihardlimit;
660                 dquot->dq_dqb.dqb_isoftlimit = dqblk->dqb_isoftlimit;
661                 /* clear usage (limit pool) */
662                 if (!dquot->dq_dqb.dqb_ihardlimit &&
663                     !dquot->dq_dqb.dqb_isoftlimit)
664                         dquot->dq_dqb.dqb_curinodes = 0;
665         }
666
667         if (dqblk->dqb_valid & QIF_BTIME)
668                 dquot->dq_dqb.dqb_btime = dqblk->dqb_btime;
669
670         if (dqblk->dqb_valid & QIF_ITIME)
671                 dquot->dq_dqb.dqb_itime = dqblk->dqb_itime;
672
673         rc = fsfilt_dquot(obd, dquot, QFILE_WR_DQUOT);
674
675         up(&dquot->dq_sem);
676         up(&dquot->dq_info->qi_sem);
677
678         if (rc)
679                 GOTO(out, rc);
680
681         if (dqblk->dqb_valid & QIF_ILIMITS && !(ihardlimit || isoftlimit)) {
682                 rc = mds_init_slave_ilimits(obd, oqctl);
683                 if (rc) {
684                         CERROR("init slave ilimits failed! (rc:%d)\n", rc);
685                         GOTO(revoke_out, rc);
686                 }
687         }
688
689         if (dqblk->dqb_valid & QIF_BLIMITS && !(bhardlimit || bsoftlimit)) {
690                 rc = mds_init_slave_blimits(obd, oqctl);
691                 if (rc) {
692                         CERROR("init slave blimits failed! (rc:%d)\n", rc);
693                         GOTO(revoke_out, rc);
694                 }
695         }
696
697 revoke_out:
698         if (rc) {
699                 /* cancel previous setting */
700                 down(&dquot->dq_info->qi_sem);
701                 down(&dquot->dq_sem);
702                 dquot->dq_dqb.dqb_ihardlimit = ihardlimit;
703                 dquot->dq_dqb.dqb_isoftlimit = isoftlimit;
704                 dquot->dq_dqb.dqb_bhardlimit = bhardlimit;
705                 dquot->dq_dqb.dqb_bsoftlimit = bsoftlimit;
706                 dquot->dq_dqb.dqb_btime = btime;
707                 dquot->dq_dqb.dqb_itime = itime;
708                 fsfilt_dquot(obd, dquot, QFILE_WR_DQUOT);
709                 up(&dquot->dq_sem);
710                 up(&dquot->dq_info->qi_sem);
711         }
712 out:
713         lustre_dqput(dquot);
714         RETURN(rc);
715 }
716
717 static int mds_get_space(struct obd_device *obd, struct obd_quotactl *oqctl)
718 {
719         struct obd_quotactl *soqc;
720         int rc;
721
722         OBD_ALLOC(soqc, sizeof(*soqc));
723         if (!soqc)
724                 RETURN(-ENOMEM);
725
726         soqc->qc_cmd = oqctl->qc_cmd;
727         soqc->qc_id = oqctl->qc_id;
728         soqc->qc_type = oqctl->qc_type;
729
730         rc = obd_quotactl(obd->u.mds.mds_osc_exp, soqc);
731
732         oqctl->qc_dqblk.dqb_curspace = soqc->qc_dqblk.dqb_curspace;
733
734         OBD_FREE(soqc, sizeof(*soqc));
735         return rc;
736 }
737
738 int mds_get_dqblk(struct obd_device *obd, struct obd_quotactl *oqctl)
739 {
740         struct mds_obd *mds = &obd->u.mds;
741         struct lustre_quota_info *qinfo = &mds->mds_quota_info;
742         struct lustre_dquot *dquot;
743         struct obd_dqblk *dqblk = &oqctl->qc_dqblk;
744         int rc;
745         ENTRY;
746
747         if (qinfo->qi_files[oqctl->qc_type] == NULL)
748                 RETURN(-ESRCH);
749
750         dquot = lustre_dqget(obd, qinfo, oqctl->qc_id, oqctl->qc_type);
751         if (IS_ERR(dquot))
752                 RETURN(PTR_ERR(dquot));
753
754         down(&dquot->dq_sem);
755         dqblk->dqb_ihardlimit = dquot->dq_dqb.dqb_ihardlimit;
756         dqblk->dqb_isoftlimit = dquot->dq_dqb.dqb_isoftlimit;
757         dqblk->dqb_bhardlimit = dquot->dq_dqb.dqb_bhardlimit;
758         dqblk->dqb_bsoftlimit = dquot->dq_dqb.dqb_bsoftlimit;
759         dqblk->dqb_btime = dquot->dq_dqb.dqb_btime;
760         dqblk->dqb_itime = dquot->dq_dqb.dqb_itime;
761         up(&dquot->dq_sem);
762
763         /* the usages in admin quota file is inaccurate */
764         dqblk->dqb_curinodes = 0;
765         dqblk->dqb_curspace = 0;
766         rc = mds_get_space(obd, oqctl);
767
768         lustre_dqput(dquot);
769         RETURN(rc);
770 }