Whamcloud - gitweb
b=16098
[fs/lustre-release.git] / lustre / quota / quota_ctl.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 #ifndef EXPORT_SYMTAB
37 # define EXPORT_SYMTAB
38 #endif
39 #define DEBUG_SUBSYSTEM S_MDS
40
41 #ifdef __KERNEL__
42 # include <linux/version.h>
43 # include <linux/module.h>
44 # include <linux/init.h>
45 # include <linux/fs.h>
46 # include <linux/jbd.h>
47 # include <linux/ext3_fs.h>
48 # include <linux/quota.h>
49 # include <linux/smp_lock.h>
50 # include <linux/buffer_head.h>
51 # include <linux/workqueue.h>
52 # include <linux/mount.h>
53 #else /* __KERNEL__ */
54 # include <liblustre.h>
55 #endif
56
57 #include <obd_class.h>
58 #include <lustre_mds.h>
59 #include <lustre_dlm.h>
60 #include <lustre_cfg.h>
61 #include <obd_ost.h>
62 #include <lustre_fsfilt.h>
63 #include <lustre_quota.h>
64 #include "quota_internal.h"
65
66 #ifdef __KERNEL__
67 int mds_quota_ctl(struct obd_export *exp, struct obd_quotactl *oqctl)
68 {
69         struct obd_device *obd = exp->exp_obd;
70         int rc = 0;
71         ENTRY;
72
73         switch (oqctl->qc_cmd) {
74         case Q_QUOTAON:
75                 rc = mds_quota_on(obd, oqctl);
76                 break;
77         case Q_QUOTAOFF:
78                 mds_quota_off(obd, oqctl);
79                 break;
80         case Q_SETINFO:
81                 rc = mds_set_dqinfo(obd, oqctl);
82                 break;
83         case Q_GETINFO:
84                 rc = mds_get_dqinfo(obd, oqctl);
85                 break;
86         case Q_SETQUOTA:
87                 rc = mds_set_dqblk(obd, oqctl);
88                 break;
89         case Q_GETQUOTA:
90                 rc = mds_get_dqblk(obd, oqctl);
91                 break;
92         case Q_GETOINFO:
93         case Q_GETOQUOTA:
94                 rc = mds_get_obd_quota(obd, oqctl);
95                 break;
96         default:
97                 CERROR("%s: unsupported mds_quotactl command: %d\n",
98                        obd->obd_name, oqctl->qc_cmd);
99                 RETURN(-EFAULT);
100         }
101
102         if (rc)
103                 CDEBUG(D_INFO, "mds_quotactl admin quota command %d, id %u, "
104                                "type %d, failed: rc = %d\n",
105                        oqctl->qc_cmd, oqctl->qc_id, oqctl->qc_type, rc);
106
107         RETURN(rc);
108 }
109
110 int filter_quota_ctl(struct obd_export *exp, struct obd_quotactl *oqctl)
111 {
112         struct obd_device *obd = exp->exp_obd;
113         struct obd_device_target *obt = &obd->u.obt;
114         struct lvfs_run_ctxt saved;
115         int rc = 0;
116         ENTRY;
117
118         switch (oqctl->qc_cmd) {
119         case Q_QUOTAON:
120         case Q_QUOTAOFF:
121                 if (!atomic_dec_and_test(&obt->obt_quotachecking)) {
122                         CDEBUG(D_INFO, "other people are doing quotacheck\n");
123                         atomic_inc(&obt->obt_quotachecking);
124                         rc = -EBUSY;
125                         break;
126                 }
127         case Q_GETOINFO:
128         case Q_GETOQUOTA:
129         case Q_GETQUOTA:
130                 /* In recovery scenario, this pending dqacq/dqrel might have
131                  * been processed by master successfully before it's dquot
132                  * on master enter recovery mode. We must wait for this 
133                  * dqacq/dqrel done then return the correct limits to master */
134                 if (oqctl->qc_stat == QUOTA_RECOVERING)
135                         qctxt_wait_pending_dqacq(&obd->u.obt.obt_qctxt,
136                                                  oqctl->qc_id, oqctl->qc_type, 
137                                                  1);
138
139                 push_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL);
140                 rc = fsfilt_quotactl(obd, obd->u.obt.obt_sb, oqctl);
141                 pop_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL);
142
143                 if (oqctl->qc_cmd == Q_QUOTAON || oqctl->qc_cmd == Q_QUOTAOFF) {
144                         if (!rc)
145                                 obt->obt_qctxt.lqc_status = 
146                                         (oqctl->qc_cmd == Q_QUOTAON) ? 1 : 0;
147                         atomic_inc(&obt->obt_quotachecking);
148                 }
149                 break;
150         case Q_SETQUOTA:
151                 qctxt_wait_pending_dqacq(&obd->u.obt.obt_qctxt, 
152                                          oqctl->qc_id, oqctl->qc_type, 1);
153
154                 push_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL);
155                 rc = fsfilt_quotactl(obd, obd->u.obt.obt_sb, oqctl);
156
157                 if (!rc) {
158                         oqctl->qc_cmd = Q_SYNC;
159                         fsfilt_quotactl(obd, obd->u.obt.obt_sb, oqctl);
160                         oqctl->qc_cmd = Q_SETQUOTA;
161                 }
162                 pop_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL);
163                 break;
164         case Q_INITQUOTA:
165                 {
166                 unsigned int uid = 0, gid = 0;
167
168                 /* Initialize quota limit to MIN_QLIMIT */
169                 LASSERT(oqctl->qc_dqblk.dqb_valid == QIF_BLIMITS);
170                 LASSERT(oqctl->qc_dqblk.dqb_bsoftlimit == 0);
171
172                 /* There might be a pending dqacq/dqrel (which is going to
173                  * clear stale limits on slave). we should wait for it's 
174                  * completion then initialize limits */
175                 qctxt_wait_pending_dqacq(&obd->u.obt.obt_qctxt, 
176                                          oqctl->qc_id, oqctl->qc_type, 1);
177
178                 if (!oqctl->qc_dqblk.dqb_bhardlimit)
179                         goto adjust;
180                 
181                 LASSERT(oqctl->qc_dqblk.dqb_bhardlimit == MIN_QLIMIT);
182                 push_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL);
183                 rc = fsfilt_quotactl(obd, obd->u.obt.obt_sb, oqctl);
184
185                 /* Update on-disk quota, in case of lose the changed limits
186                  * (MIN_QLIMIT) on crash, which cannot be recovered.*/
187                 if (!rc) {
188                         oqctl->qc_cmd = Q_SYNC;
189                         fsfilt_quotactl(obd, obd->u.obt.obt_sb, oqctl);
190                         oqctl->qc_cmd = Q_INITQUOTA;
191                 }
192                 pop_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL);
193
194                 if (rc)
195                         RETURN(rc);
196 adjust:
197                 /* Trigger qunit pre-acquire */
198                 if (oqctl->qc_type == USRQUOTA)
199                         uid = oqctl->qc_id;
200                 else
201                         gid = oqctl->qc_id;
202
203                 rc = qctxt_adjust_qunit(obd, &obd->u.obt.obt_qctxt, 
204                                         uid, gid, 1, 0);
205                 break;
206                 }
207         default:
208                 CERROR("%s: unsupported filter_quotactl command: %d\n",
209                        obd->obd_name, oqctl->qc_cmd);
210                 RETURN(-EFAULT);
211         }
212
213         RETURN(rc);
214 }
215 #endif /* __KERNEL__ */
216
217 int client_quota_ctl(struct obd_export *exp, struct obd_quotactl *oqctl)
218 {
219         struct ptlrpc_request *req;
220         struct obd_quotactl   *oqc;
221         int                    ver, opc, rc;
222         ENTRY;
223
224         if (!strcmp(exp->exp_obd->obd_type->typ_name, LUSTRE_MDC_NAME)) {
225                 ver = LUSTRE_MDS_VERSION,
226                 opc = MDS_QUOTACTL;
227         } else if (!strcmp(exp->exp_obd->obd_type->typ_name, LUSTRE_OSC_NAME)) {
228                 ver = LUSTRE_OST_VERSION,
229                 opc = OST_QUOTACTL;
230         } else {
231                 RETURN(-EINVAL);
232         }
233
234         req = ptlrpc_request_alloc_pack(class_exp2cliimp(exp),
235                                         &RQF_MDS_QUOTACTL, ver, opc);
236         if (req == NULL)
237                 RETURN(-ENOMEM);
238
239         oqc = req_capsule_client_get(&req->rq_pill, &RMF_OBD_QUOTACTL);
240         *oqc = *oqctl;
241
242         ptlrpc_request_set_replen(req);
243
244         rc = ptlrpc_queue_wait(req);
245         if (!rc) {
246                 oqc = req_capsule_server_get(&req->rq_pill, &RMF_OBD_QUOTACTL);
247                 if (oqc == NULL)
248                         GOTO(out, rc = -EPROTO);
249
250                 *oqctl = *oqc;
251         }
252 out:
253         ptlrpc_req_finished(req);
254         RETURN (rc);
255 }
256
257 int lov_quota_ctl(struct obd_export *exp, struct obd_quotactl *oqctl)
258 {
259         struct obd_device *obd = class_exp2obd(exp);
260         struct lov_obd *lov = &obd->u.lov;
261         __u64 curspace = 0;
262         __u32 bhardlimit = 0;
263         int i, rc = 0;
264         ENTRY;
265
266         if (oqctl->qc_cmd != Q_QUOTAON && oqctl->qc_cmd != Q_QUOTAOFF &&
267             oqctl->qc_cmd != Q_GETOQUOTA && oqctl->qc_cmd != Q_INITQUOTA &&
268             oqctl->qc_cmd != Q_SETQUOTA) {
269                 CERROR("bad quota opc %x for lov obd", oqctl->qc_cmd);
270                 RETURN(-EFAULT);
271         }
272
273         for (i = 0; i < lov->desc.ld_tgt_count; i++) {
274                 int err;
275
276                 if (!lov->lov_tgts[i] || !lov->lov_tgts[i]->ltd_active) {
277                         if (oqctl->qc_cmd == Q_GETOQUOTA) {
278                                 CERROR("ost %d is inactive\n", i);
279                                 rc = -EIO;
280                                 break;
281                         } else {
282                                 CDEBUG(D_HA, "ost %d is inactive\n", i);
283                                 continue;
284                         }
285                 }
286
287                 err = obd_quotactl(lov->lov_tgts[i]->ltd_exp, oqctl);
288                 if (err) {
289                         if (lov->lov_tgts[i]->ltd_active && !rc)
290                                 rc = err;
291                         continue;
292                 }
293
294                 if (oqctl->qc_cmd == Q_GETOQUOTA) {
295                         curspace += oqctl->qc_dqblk.dqb_curspace;
296                         bhardlimit += oqctl->qc_dqblk.dqb_bhardlimit;
297                 }
298         }
299
300         if (oqctl->qc_cmd == Q_GETOQUOTA) {
301                 oqctl->qc_dqblk.dqb_curspace = curspace;
302                 oqctl->qc_dqblk.dqb_bhardlimit = bhardlimit;
303         }
304         RETURN(rc);
305 }