Whamcloud - gitweb
LU-1842 quota: add quotactl support on qmt
[fs/lustre-release.git] / lustre / quota / qmt_handler.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, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 021110-1307, USA
20  *
21  * GPL HEADER END
22  */
23 /*
24  * Copyright (c) 2012 Intel, Inc.
25  * Use is subject to license terms.
26  *
27  * Author: Johann Lombardi <johann.lombardi@intel.com>
28  * Author: Niu    Yawei    <yawei.niu@intel.com>
29  */
30
31 #ifndef EXPORT_SYMTAB
32 # define EXPORT_SYMTAB
33 #endif
34
35 #define DEBUG_SUBSYSTEM S_LQUOTA
36
37 #include <obd_class.h>
38 #include "qmt_internal.h"
39
40 /*
41  * Fetch grace time for either inode or block.
42  *
43  * \param env     - is the environment passed by the caller
44  * \param qmt     - is the quota master target
45  * \param pool_id - is the 16-bit pool identifier
46  * \param restype - is the pool type, either block (i.e. LQUOTA_RES_DT) or inode
47  *                  (i.e. LQUOTA_RES_MD)
48  * \param qtype   - is the quota type
49  * \param time    - is the output variable where to copy the grace time
50  */
51 static int qmt_getinfo(const struct lu_env *env, struct qmt_device *qmt,
52                        __u16 pool_id, __u8 restype, __u8 qtype, __u64 *time)
53 {
54         struct qmt_thread_info  *qti = qmt_info(env);
55         union lquota_id         *id  = &qti->qti_id_bis;
56         struct lquota_entry     *lqe;
57         ENTRY;
58
59         /* Global grace time is stored in quota settings of ID 0. */
60         id->qid_uid = 0;
61
62         /* look-up quota entry storing grace time */
63         lqe = qmt_pool_lqe_lookup(env, qmt, pool_id, restype, qtype, id);
64         if (IS_ERR(lqe))
65                 RETURN(PTR_ERR(lqe));
66
67         lqe_read_lock(lqe);
68         LQUOTA_DEBUG(lqe, "getinfo");
69         /* copy grace time */
70         *time = lqe->lqe_gracetime;
71         lqe_read_unlock(lqe);
72
73         lqe_putref(lqe);
74         RETURN(0);
75 }
76
77 /*
78  * Update grace time for either inode or block.
79  * Global grace time is stored in quota settings of ID 0.
80  *
81  * \param env     - is the environment passed by the caller
82  * \param qmt     - is the quota master target
83  * \param pool_id - is the 16-bit pool identifier
84  * \param restype - is the pool type, either block (i.e. LQUOTA_RES_DT) or inode
85  *                  (i.e. LQUOTA_RES_MD)
86  * \param qtype   - is the quota type
87  * \param time    - is the new grace time
88  */
89 static int qmt_setinfo(const struct lu_env *env, struct qmt_device *qmt,
90                        __u16 pool_id, __u8 restype, __u8 qtype, __u64 time)
91 {
92         struct qmt_thread_info  *qti = qmt_info(env);
93         union lquota_id         *id  = &qti->qti_id_bis;
94         struct lquota_entry     *lqe;
95         struct thandle          *th = NULL;
96         int                      rc;
97         ENTRY;
98
99         /* Global grace time is stored in quota settings of ID 0. */
100         id->qid_uid = 0;
101
102         /* look-up quota entry storing the global grace time */
103         lqe = qmt_pool_lqe_lookup(env, qmt, pool_id, restype, qtype, id);
104         if (IS_ERR(lqe))
105                 RETURN(PTR_ERR(lqe));
106
107         /* allocate & start transaction with enough credits to update grace
108          * time in the global index file */
109         th = qmt_trans_start(env, lqe, &qti->qti_restore);
110         if (IS_ERR(th))
111                 GOTO(out_nolock, rc = PTR_ERR(th));
112
113         /* write lock quota entry storing the grace time */
114         lqe_write_lock(lqe);
115         if (lqe->lqe_gracetime == time)
116                 /* grace time is the same */
117                 GOTO(out, rc = 0);
118
119         LQUOTA_DEBUG(lqe, "setinfo time:"LPU64, time);
120
121         /* set new grace time */
122         lqe->lqe_gracetime = time;
123         /* always set enforced bit for ID 0 to make sure it does not go away */
124         lqe->lqe_enforced  = true;
125
126         /* write new grace time to disk, no need for version bump */
127         rc = qmt_glb_write(env, th, lqe, 0, NULL);
128         if (rc) {
129                 /* restore initial grace time */
130                 qmt_restore(lqe, &qti->qti_restore);
131                 GOTO(out, rc);
132         }
133         EXIT;
134 out:
135         lqe_write_unlock(lqe);
136 out_nolock:
137         lqe_putref(lqe);
138         if (th != NULL && !IS_ERR(th))
139                 dt_trans_stop(env, qmt->qmt_child, th);
140         return rc;
141 }
142
143 /*
144  * Retrieve quota settings for a given identifier.
145  *
146  * \param env     - is the environment passed by the caller
147  * \param qmt     - is the quota master target
148  * \param pool_id - is the 16-bit pool identifier
149  * \param restype - is the pool type, either block (i.e. LQUOTA_RES_DT) or inode
150  *                  (i.e. LQUOTA_RES_MD)
151  * \param qtype   - is the quota type
152  * \param id      - is the quota indentifier for which we want to acces quota
153  *                  settings.
154  * \param hard    - is the output variable where to copy the hard limit
155  * \param soft    - is the output variable where to copy the soft limit
156  * \param time    - is the output variable where to copy the grace time
157  */
158 static int qmt_getquota(const struct lu_env *env, struct qmt_device *qmt,
159                         __u16 pool_id, __u8 restype, __u8 qtype,
160                         union lquota_id *id, __u64 *hard, __u64 *soft,
161                         __u64 *time)
162 {
163         struct lquota_entry     *lqe;
164         ENTRY;
165
166         /* look-up lqe structure containing quota settings */
167         lqe = qmt_pool_lqe_lookup(env, qmt, pool_id, restype, qtype, id);
168         if (IS_ERR(lqe))
169                 RETURN(PTR_ERR(lqe));
170
171         /* copy quota settings */
172         lqe_read_lock(lqe);
173         LQUOTA_DEBUG(lqe, "getquota");
174         *hard = lqe->lqe_hardlimit;
175         *soft = lqe->lqe_softlimit;
176         *time = lqe->lqe_gracetime;
177         lqe_read_unlock(lqe);
178
179         lqe_putref(lqe);
180         RETURN(0);
181 }
182
183 /*
184  * Update quota settings for a given identifier.
185  *
186  * \param env     - is the environment passed by the caller
187  * \param qmt     - is the quota master target
188  * \param pool_id - is the 16-bit pool identifier
189  * \param restype - is the pool type, either block (i.e. LQUOTA_RES_DT) or inode
190  *                  (i.e. LQUOTA_RES_MD)
191  * \param qtype   - is the quota type
192  * \param id      - is the quota indentifier for which we want to modify quota
193  *                  settings.
194  * \param hard    - is the new hard limit
195  * \param soft    - is the new soft limit
196  * \param time    - is the new grace time
197  * \param valid   - is the list of settings to change
198  */
199 static int qmt_setquota(const struct lu_env *env, struct qmt_device *qmt,
200                         __u16 pool_id, __u8 restype, __u8 qtype,
201                         union lquota_id *id, __u64 hard, __u64 soft, __u64 time,
202                         __u32 valid)
203 {
204         struct qmt_thread_info  *qti = qmt_info(env);
205         struct lquota_entry     *lqe;
206         struct thandle          *th = NULL;
207         __u64                    grace, ver;
208         bool                     dirtied = false, bump_version = false;
209         int                      rc = 0;
210         ENTRY;
211
212         /* fetch global grace time */
213         rc = qmt_getinfo(env, qmt, pool_id, restype, qtype, &grace);
214         if (rc)
215                 RETURN(rc);
216
217         /* look-up quota entry associated with this ID */
218         lqe = qmt_pool_lqe_lookup(env, qmt, pool_id, restype, qtype, id);
219         if (IS_ERR(lqe))
220                 RETURN(PTR_ERR(lqe));
221
222         /* allocate & start transaction with enough credits to update quota
223          * settings in the global index file */
224         th = qmt_trans_start(env, lqe, &qti->qti_restore);
225         if (IS_ERR(th))
226                 GOTO(out_nolock, rc = PTR_ERR(th));
227
228         lqe_write_lock(lqe);
229         LQUOTA_DEBUG(lqe, "setquota valid:%x hard:"LPU64" soft:"LPU64
230                      " time:"LPU64, valid, hard, soft, time);
231
232         if ((valid & QIF_TIMES) != 0 && lqe->lqe_gracetime != time) {
233                 /* change time settings */
234                 lqe->lqe_gracetime = time;
235                 dirtied            = true;
236         }
237
238         if ((valid & QIF_LIMITS) != 0 &&
239             (lqe->lqe_hardlimit != hard || lqe->lqe_softlimit != soft)) {
240                 bool enforced = lqe->lqe_enforced;
241
242                 rc = qmt_validate_limits(lqe, hard, soft);
243                 if (rc)
244                         GOTO(out, rc);
245
246                 /* change quota limits */
247                 lqe->lqe_hardlimit = hard;
248                 lqe->lqe_softlimit = soft;
249
250                 /* clear grace time */
251                 if (lqe->lqe_softlimit == 0 ||
252                     lqe->lqe_granted <= lqe->lqe_softlimit)
253                         /* no soft limit or below soft limit, let's clear grace
254                          * time */
255                         lqe->lqe_gracetime = 0;
256                 else if ((valid & QIF_TIMES) == 0)
257                         /* set grace only if user hasn't provided his own */
258                          lqe->lqe_gracetime = cfs_time_current_sec() + grace;
259
260                 /* change enforced status based on new parameters */
261                 if (lqe->lqe_hardlimit == 0 && lqe->lqe_softlimit == 0)
262                         lqe->lqe_enforced = false;
263                 else
264                         lqe->lqe_enforced = true;
265
266                 if ((enforced && !lqe->lqe_enforced) ||
267                     (!enforced && lqe->lqe_enforced))
268                         /* if enforced status has changed, we need to inform
269                          * slave, therefore we need to bump the version */
270                          bump_version = true;
271
272                 dirtied = true;
273         }
274
275         if (dirtied) {
276                 /* write new quota settings to disk */
277                 rc = qmt_glb_write(env, th, lqe,
278                                    bump_version ? LQUOTA_BUMP_VER : 0, &ver);
279                 if (rc) {
280                         /* restore initial quota settings */
281                         qmt_restore(lqe, &qti->qti_restore);
282                         GOTO(out, rc);
283                 }
284         }
285         EXIT;
286 out:
287         lqe_write_unlock(lqe);
288 out_nolock:
289         lqe_putref(lqe);
290
291         if (th != NULL && !IS_ERR(th))
292                 dt_trans_stop(env, qmt->qmt_child, th);
293
294         return rc;
295 }
296
297 /*
298  * Handle quotactl request.
299  *
300  * \param env   - is the environment passed by the caller
301  * \param ld    - is the lu device associated with the qmt
302  * \param oqctl - is the quotactl request
303  */
304 static int qmt_quotactl(const struct lu_env *env, struct lu_device *ld,
305                         struct obd_quotactl *oqctl)
306 {
307         struct qmt_thread_info  *qti = qmt_info(env);
308         union lquota_id         *id  = &qti->qti_id;
309         struct qmt_device       *qmt = lu2qmt_dev(ld);
310         struct obd_dqblk        *dqb = &oqctl->qc_dqblk;
311         int                      rc = 0;
312         ENTRY;
313
314         LASSERT(qmt != NULL);
315
316         if (oqctl->qc_type >= MAXQUOTAS)
317                 /* invalid quota type */
318                 RETURN(-EINVAL);
319
320         switch (oqctl->qc_cmd) {
321
322         case Q_GETINFO:  /* read grace times */
323                 /* read inode grace time */
324                 rc = qmt_getinfo(env, qmt, 0, LQUOTA_RES_MD, oqctl->qc_type,
325                                  &oqctl->qc_dqinfo.dqi_igrace);
326                 if (rc)
327                         break;
328
329                 /* read block grace time */
330                 rc = qmt_getinfo(env, qmt, 0, LQUOTA_RES_DT, oqctl->qc_type,
331                                  &oqctl->qc_dqinfo.dqi_bgrace);
332                 break;
333
334         case Q_SETINFO:  /* modify grace times */
335                 /* setinfo should be using dqi->dqi_valid, but lfs incorrectly
336                  * sets the valid flags in dqb->dqb_valid instead, try to live
337                  * with that ... */
338                 if ((dqb->dqb_valid & QIF_ITIME) != 0) {
339                         /* set inode grace time */
340                         rc = qmt_setinfo(env, qmt, 0, LQUOTA_RES_MD,
341                                          oqctl->qc_type,
342                                          oqctl->qc_dqinfo.dqi_igrace);
343                         if (rc)
344                                 break;
345                 }
346
347                 if ((dqb->dqb_valid & QIF_BTIME) != 0)
348                         /* set block grace time */
349                         rc = qmt_setinfo(env, qmt, 0, LQUOTA_RES_DT,
350                                          oqctl->qc_type,
351                                          oqctl->qc_dqinfo.dqi_bgrace);
352                 break;
353
354         case Q_GETQUOTA: /* consult quota limit */
355                 /* There is no quota limit for root user & group */
356                 if (oqctl->qc_id == 0) {
357                         memset(dqb, 0, sizeof(*dqb));
358                         dqb->dqb_valid = QIF_LIMITS | QIF_TIMES;
359                         break;
360                 }
361                 /* extract quota ID from quotactl request */
362                 id->qid_uid = oqctl->qc_id;
363
364                 /* look-up inode quota settings */
365                 rc = qmt_getquota(env, qmt, 0, LQUOTA_RES_MD, oqctl->qc_type,
366                                   id, &dqb->dqb_ihardlimit,
367                                   &dqb->dqb_isoftlimit, &dqb->dqb_itime);
368                 if (rc)
369                         break;
370
371                 dqb->dqb_valid |= QIF_ILIMITS | QIF_ITIME;
372                 /* master isn't aware of actual inode usage */
373                 dqb->dqb_curinodes = 0;
374
375                 /* look-up block quota settings */
376                 rc = qmt_getquota(env, qmt, 0, LQUOTA_RES_DT, oqctl->qc_type,
377                                   id, &dqb->dqb_bhardlimit,
378                                   &dqb->dqb_bsoftlimit, &dqb->dqb_btime);
379                 if (rc)
380                         break;
381
382                 dqb->dqb_valid |= QIF_BLIMITS | QIF_BTIME;
383                 /* master doesn't know the actual block usage */
384                 dqb->dqb_curspace = 0;
385                 break;
386
387         case Q_SETQUOTA: /* change quota limits */
388                 if (oqctl->qc_id == 0)
389                         /* can't enforce a quota limit for root user & group */
390                         RETURN(-EPERM);
391                 /* extract quota ID from quotactl request */
392                 id->qid_uid = oqctl->qc_id;
393
394                 if ((dqb->dqb_valid & QIF_IFLAGS) != 0) {
395                         /* update inode quota settings */
396                         rc = qmt_setquota(env, qmt, 0, LQUOTA_RES_MD,
397                                           oqctl->qc_type, id,
398                                           dqb->dqb_ihardlimit,
399                                           dqb->dqb_isoftlimit, dqb->dqb_itime,
400                                           dqb->dqb_valid & QIF_IFLAGS);
401                         if (rc)
402                                 break;
403                 }
404
405                 if ((dqb->dqb_valid & QIF_BFLAGS) != 0)
406                         /* update block quota settings */
407                         rc = qmt_setquota(env, qmt, 0, LQUOTA_RES_DT,
408                                           oqctl->qc_type, id,
409                                           dqb->dqb_bhardlimit,
410                                           dqb->dqb_bsoftlimit, dqb->dqb_btime,
411                                           dqb->dqb_valid & QIF_BFLAGS);
412                 break;
413
414         case Q_QUOTAON:
415         case Q_QUOTAOFF:   /* quota is always turned on on the master */
416                 RETURN(0);
417
418         case LUSTRE_Q_INVALIDATE: /* not supported any more */
419                 RETURN(-ENOTSUPP);
420
421         default:
422                 CERROR("%s: unsupported quotactl command: %d\n",
423                        qmt->qmt_svname, oqctl->qc_cmd);
424                 RETURN(-ENOTSUPP);
425         }
426
427         RETURN(rc);
428 }
429
430 /*
431  * Handle quota request from slave.
432  *
433  * \param env  - is the environment passed by the caller
434  * \param ld   - is the lu device associated with the qmt
435  * \param req  - is the quota acquire request
436  */
437 static int qmt_dqacq(const struct lu_env *env, struct lu_device *ld,
438                      struct ptlrpc_request *req)
439 {
440         struct quota_body       *qbody, *repbody;
441         ENTRY;
442
443         qbody = req_capsule_client_get(&req->rq_pill, &RMF_QUOTA_BODY);
444         if (qbody == NULL)
445                 RETURN(err_serious(-EPROTO));
446
447         repbody = req_capsule_server_get(&req->rq_pill, &RMF_QUOTA_BODY);
448         if (repbody == NULL)
449                 RETURN(err_serious(-EFAULT));
450
451         /* XXX: to be implemented */
452
453         RETURN(0);
454 }
455
456 /* Vector of quota request handlers. This vector is used by the MDT to forward
457  * requests to the quota master. */
458 struct qmt_handlers qmt_hdls = {
459         /* quota request handlers */
460         .qmth_quotactl          = qmt_quotactl,
461         .qmth_dqacq             = qmt_dqacq,
462
463         /* ldlm handlers */
464         .qmth_intent_policy     = qmt_intent_policy,
465         .qmth_lvbo_init         = qmt_lvbo_init,
466         .qmth_lvbo_update       = qmt_lvbo_update,
467         .qmth_lvbo_size         = qmt_lvbo_size,
468         .qmth_lvbo_fill         = qmt_lvbo_fill,
469         .qmth_lvbo_free         = qmt_lvbo_free,
470 };
471 EXPORT_SYMBOL(qmt_hdls);