Whamcloud - gitweb
9a4c416a9d0b83457adfa36290f2596eb6d033aa
[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         if (rc == 0 && bump_version)
295                 qmt_glb_lock_notify(env, lqe, ver);
296
297         return rc;
298 }
299
300 /*
301  * Handle quotactl request.
302  *
303  * \param env   - is the environment passed by the caller
304  * \param ld    - is the lu device associated with the qmt
305  * \param oqctl - is the quotactl request
306  */
307 static int qmt_quotactl(const struct lu_env *env, struct lu_device *ld,
308                         struct obd_quotactl *oqctl)
309 {
310         struct qmt_thread_info  *qti = qmt_info(env);
311         union lquota_id         *id  = &qti->qti_id;
312         struct qmt_device       *qmt = lu2qmt_dev(ld);
313         struct obd_dqblk        *dqb = &oqctl->qc_dqblk;
314         int                      rc = 0;
315         ENTRY;
316
317         LASSERT(qmt != NULL);
318
319         if (oqctl->qc_type >= MAXQUOTAS)
320                 /* invalid quota type */
321                 RETURN(-EINVAL);
322
323         switch (oqctl->qc_cmd) {
324
325         case Q_GETINFO:  /* read grace times */
326                 /* read inode grace time */
327                 rc = qmt_getinfo(env, qmt, 0, LQUOTA_RES_MD, oqctl->qc_type,
328                                  &oqctl->qc_dqinfo.dqi_igrace);
329                 if (rc)
330                         break;
331
332                 /* read block grace time */
333                 rc = qmt_getinfo(env, qmt, 0, LQUOTA_RES_DT, oqctl->qc_type,
334                                  &oqctl->qc_dqinfo.dqi_bgrace);
335                 break;
336
337         case Q_SETINFO:  /* modify grace times */
338                 /* setinfo should be using dqi->dqi_valid, but lfs incorrectly
339                  * sets the valid flags in dqb->dqb_valid instead, try to live
340                  * with that ... */
341                 if ((dqb->dqb_valid & QIF_ITIME) != 0) {
342                         /* set inode grace time */
343                         rc = qmt_setinfo(env, qmt, 0, LQUOTA_RES_MD,
344                                          oqctl->qc_type,
345                                          oqctl->qc_dqinfo.dqi_igrace);
346                         if (rc)
347                                 break;
348                 }
349
350                 if ((dqb->dqb_valid & QIF_BTIME) != 0)
351                         /* set block grace time */
352                         rc = qmt_setinfo(env, qmt, 0, LQUOTA_RES_DT,
353                                          oqctl->qc_type,
354                                          oqctl->qc_dqinfo.dqi_bgrace);
355                 break;
356
357         case Q_GETQUOTA: /* consult quota limit */
358                 /* There is no quota limit for root user & group */
359                 if (oqctl->qc_id == 0) {
360                         memset(dqb, 0, sizeof(*dqb));
361                         dqb->dqb_valid = QIF_LIMITS | QIF_TIMES;
362                         break;
363                 }
364                 /* extract quota ID from quotactl request */
365                 id->qid_uid = oqctl->qc_id;
366
367                 /* look-up inode quota settings */
368                 rc = qmt_getquota(env, qmt, 0, LQUOTA_RES_MD, oqctl->qc_type,
369                                   id, &dqb->dqb_ihardlimit,
370                                   &dqb->dqb_isoftlimit, &dqb->dqb_itime);
371                 if (rc)
372                         break;
373
374                 dqb->dqb_valid |= QIF_ILIMITS | QIF_ITIME;
375                 /* master isn't aware of actual inode usage */
376                 dqb->dqb_curinodes = 0;
377
378                 /* look-up block quota settings */
379                 rc = qmt_getquota(env, qmt, 0, LQUOTA_RES_DT, oqctl->qc_type,
380                                   id, &dqb->dqb_bhardlimit,
381                                   &dqb->dqb_bsoftlimit, &dqb->dqb_btime);
382                 if (rc)
383                         break;
384
385                 dqb->dqb_valid |= QIF_BLIMITS | QIF_BTIME;
386                 /* master doesn't know the actual block usage */
387                 dqb->dqb_curspace = 0;
388                 break;
389
390         case Q_SETQUOTA: /* change quota limits */
391                 if (oqctl->qc_id == 0)
392                         /* can't enforce a quota limit for root user & group */
393                         RETURN(-EPERM);
394                 /* extract quota ID from quotactl request */
395                 id->qid_uid = oqctl->qc_id;
396
397                 if ((dqb->dqb_valid & QIF_IFLAGS) != 0) {
398                         /* update inode quota settings */
399                         rc = qmt_setquota(env, qmt, 0, LQUOTA_RES_MD,
400                                           oqctl->qc_type, id,
401                                           dqb->dqb_ihardlimit,
402                                           dqb->dqb_isoftlimit, dqb->dqb_itime,
403                                           dqb->dqb_valid & QIF_IFLAGS);
404                         if (rc)
405                                 break;
406                 }
407
408                 if ((dqb->dqb_valid & QIF_BFLAGS) != 0)
409                         /* update block quota settings */
410                         rc = qmt_setquota(env, qmt, 0, LQUOTA_RES_DT,
411                                           oqctl->qc_type, id,
412                                           dqb->dqb_bhardlimit,
413                                           dqb->dqb_bsoftlimit, dqb->dqb_btime,
414                                           dqb->dqb_valid & QIF_BFLAGS);
415                 break;
416
417         case Q_QUOTAON:
418         case Q_QUOTAOFF:   /* quota is always turned on on the master */
419                 RETURN(0);
420
421         case LUSTRE_Q_INVALIDATE: /* not supported any more */
422                 RETURN(-ENOTSUPP);
423
424         default:
425                 CERROR("%s: unsupported quotactl command: %d\n",
426                        qmt->qmt_svname, oqctl->qc_cmd);
427                 RETURN(-ENOTSUPP);
428         }
429
430         RETURN(rc);
431 }
432
433 /*
434  * Handle quota request from slave.
435  *
436  * \param env  - is the environment passed by the caller
437  * \param ld   - is the lu device associated with the qmt
438  * \param req  - is the quota acquire request
439  */
440 static int qmt_dqacq(const struct lu_env *env, struct lu_device *ld,
441                      struct ptlrpc_request *req)
442 {
443         struct quota_body       *qbody, *repbody;
444         ENTRY;
445
446         qbody = req_capsule_client_get(&req->rq_pill, &RMF_QUOTA_BODY);
447         if (qbody == NULL)
448                 RETURN(err_serious(-EPROTO));
449
450         repbody = req_capsule_server_get(&req->rq_pill, &RMF_QUOTA_BODY);
451         if (repbody == NULL)
452                 RETURN(err_serious(-EFAULT));
453
454         /* XXX: to be implemented */
455
456         RETURN(0);
457 }
458
459 /* Vector of quota request handlers. This vector is used by the MDT to forward
460  * requests to the quota master. */
461 struct qmt_handlers qmt_hdls = {
462         /* quota request handlers */
463         .qmth_quotactl          = qmt_quotactl,
464         .qmth_dqacq             = qmt_dqacq,
465
466         /* ldlm handlers */
467         .qmth_intent_policy     = qmt_intent_policy,
468         .qmth_lvbo_init         = qmt_lvbo_init,
469         .qmth_lvbo_update       = qmt_lvbo_update,
470         .qmth_lvbo_size         = qmt_lvbo_size,
471         .qmth_lvbo_fill         = qmt_lvbo_fill,
472         .qmth_lvbo_free         = qmt_lvbo_free,
473 };
474 EXPORT_SYMBOL(qmt_hdls);