Whamcloud - gitweb
69a9306cd82b0913d0f89958b8d7990fe6bbf0fb
[fs/lustre-release.git] / lustre / osc / osc_quota.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, write to the
20  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21  * Boston, MA 021110-1307, USA
22  *
23  * GPL HEADER END
24  */
25 /*
26  * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
27  * Copyright (c) 2011 Whamcloud, Inc.
28  *
29  * Code originally extracted from quota directory
30  */
31 #ifndef __KERNEL__
32 # include <liblustre.h>
33 #endif
34
35 #include <obd_ost.h>
36 #include "osc_internal.h"
37
38 struct osc_quota_info {
39         cfs_list_t         oqi_hash; /* hash list */
40         struct client_obd *oqi_cli;  /* osc obd */
41         unsigned int       oqi_id;   /* uid/gid of a file */
42         short              oqi_type; /* quota type */
43 };
44
45 cfs_spinlock_t qinfo_list_lock = CFS_SPIN_LOCK_UNLOCKED;
46
47 static cfs_list_t qinfo_hash[NR_DQHASH];
48 /* SLAB cache for client quota context */
49 cfs_mem_cache_t *qinfo_cachep = NULL;
50
51 static inline int hashfn(struct client_obd *cli, unsigned long id, int type)
52                          __attribute__((__const__));
53
54 static inline int hashfn(struct client_obd *cli, unsigned long id, int type)
55 {
56         unsigned long tmp = ((unsigned long)cli>>6) ^ id;
57         tmp = (tmp * (MAXQUOTAS - type)) % NR_DQHASH;
58         return tmp;
59 }
60
61 /* caller must hold qinfo_list_lock */
62 static inline void insert_qinfo_hash(struct osc_quota_info *oqi)
63 {
64         cfs_list_t *head = qinfo_hash +
65                 hashfn(oqi->oqi_cli, oqi->oqi_id, oqi->oqi_type);
66
67         LASSERT_SPIN_LOCKED(&qinfo_list_lock);
68         cfs_list_add(&oqi->oqi_hash, head);
69 }
70
71 /* caller must hold qinfo_list_lock */
72 static inline void remove_qinfo_hash(struct osc_quota_info *oqi)
73 {
74         LASSERT_SPIN_LOCKED(&qinfo_list_lock);
75         cfs_list_del_init(&oqi->oqi_hash);
76 }
77
78 /* caller must hold qinfo_list_lock */
79 static inline struct osc_quota_info *find_qinfo(struct client_obd *cli,
80                                                 unsigned int id, int type)
81 {
82         struct osc_quota_info *oqi;
83         unsigned int           hashent = hashfn(cli, id, type);
84         ENTRY;
85
86         LASSERT_SPIN_LOCKED(&qinfo_list_lock);
87         cfs_list_for_each_entry(oqi, &qinfo_hash[hashent], oqi_hash) {
88                 if (oqi->oqi_cli == cli &&
89                     oqi->oqi_id == id && oqi->oqi_type == type)
90                         RETURN(oqi);
91         }
92         RETURN(NULL);
93 }
94
95 static struct osc_quota_info *alloc_qinfo(struct client_obd *cli,
96                                           unsigned int id, int type)
97 {
98         struct osc_quota_info *oqi;
99         ENTRY;
100
101         OBD_SLAB_ALLOC(oqi, qinfo_cachep, CFS_ALLOC_IO, sizeof(*oqi));
102         if(!oqi)
103                 RETURN(NULL);
104
105         CFS_INIT_LIST_HEAD(&oqi->oqi_hash);
106         oqi->oqi_cli = cli;
107         oqi->oqi_id = id;
108         oqi->oqi_type = type;
109
110         RETURN(oqi);
111 }
112
113 static void free_qinfo(struct osc_quota_info *oqi)
114 {
115         OBD_SLAB_FREE(oqi, qinfo_cachep, sizeof(*oqi));
116 }
117
118 int osc_quota_chkdq(struct client_obd *cli, const unsigned int qid[])
119 {
120         unsigned int id;
121         int          cnt, rc = QUOTA_OK;
122         ENTRY;
123
124         cfs_spin_lock(&qinfo_list_lock);
125         for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
126                 struct osc_quota_info *oqi = NULL;
127
128                 id = (cnt == USRQUOTA) ? qid[USRQUOTA] : qid[GRPQUOTA];
129                 oqi = find_qinfo(cli, id, cnt);
130                 if (oqi) {
131                         rc = NO_QUOTA;
132                         break;
133                 }
134         }
135         cfs_spin_unlock(&qinfo_list_lock);
136
137         if (rc == NO_QUOTA)
138                 CDEBUG(D_QUOTA, "chkdq found noquota for %s %d\n",
139                        cnt == USRQUOTA ? "user" : "group", id);
140         RETURN(rc);
141 }
142
143 int osc_quota_setdq(struct client_obd *cli, const unsigned int qid[],
144                     obd_flag valid, obd_flag flags)
145 {
146         unsigned int id;
147         obd_flag     noquota;
148         int          cnt, rc = 0;
149         ENTRY;
150
151         for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
152                 struct osc_quota_info *oqi = NULL, *old;
153
154                 if (!(valid & ((cnt == USRQUOTA) ?
155                     OBD_MD_FLUSRQUOTA : OBD_MD_FLGRPQUOTA)))
156                         continue;
157
158                 id = (cnt == USRQUOTA) ? qid[USRQUOTA] : qid[GRPQUOTA];
159                 noquota = (cnt == USRQUOTA) ?
160                     (flags & OBD_FL_NO_USRQUOTA) : (flags & OBD_FL_NO_GRPQUOTA);
161
162                 if (noquota) {
163                         oqi = alloc_qinfo(cli, id, cnt);
164                         if (!oqi) {
165                                 rc = -ENOMEM;
166                                 CDEBUG(D_QUOTA, "setdq for %s %d failed, "
167                                        "(rc = %d)\n",
168                                        cnt == USRQUOTA ? "user" : "group",
169                                        id, rc);
170                                 break;
171                         }
172                 }
173
174                 cfs_spin_lock(&qinfo_list_lock);
175                 old = find_qinfo(cli, id, cnt);
176                 if (old && !noquota)
177                         remove_qinfo_hash(old);
178                 else if (!old && noquota)
179                         insert_qinfo_hash(oqi);
180                 cfs_spin_unlock(&qinfo_list_lock);
181
182                 if (old && !noquota)
183                         CDEBUG(D_QUOTA, "setdq to remove for %s %d\n",
184                                cnt == USRQUOTA ? "user" : "group", id);
185                 else if (!old && noquota)
186                         CDEBUG(D_QUOTA, "setdq to insert for %s %d\n",
187                                cnt == USRQUOTA ? "user" : "group", id);
188
189                 if (old) {
190                         if (noquota)
191                                 free_qinfo(oqi);
192                         else
193                                 free_qinfo(old);
194                 }
195         }
196         RETURN(rc);
197 }
198
199 int osc_quota_cleanup(struct obd_device *obd)
200 {
201         struct client_obd     *cli = &obd->u.cli;
202         struct osc_quota_info *oqi, *n;
203         int i;
204         ENTRY;
205
206         cfs_spin_lock(&qinfo_list_lock);
207         for (i = 0; i < NR_DQHASH; i++) {
208                 cfs_list_for_each_entry_safe(oqi, n, &qinfo_hash[i], oqi_hash) {
209                         if (oqi->oqi_cli != cli)
210                                 continue;
211                         remove_qinfo_hash(oqi);
212                         free_qinfo(oqi);
213                 }
214         }
215         cfs_spin_unlock(&qinfo_list_lock);
216
217         RETURN(0);
218 }
219
220 int osc_quota_init()
221 {
222         int i;
223         ENTRY;
224
225         LASSERT(qinfo_cachep == NULL);
226         qinfo_cachep = cfs_mem_cache_create("osc_quota_info",
227                                             sizeof(struct osc_quota_info),
228                                             0, 0);
229         if (!qinfo_cachep)
230                 RETURN(-ENOMEM);
231
232         for (i = 0; i < NR_DQHASH; i++)
233                 CFS_INIT_LIST_HEAD(qinfo_hash + i);
234
235         RETURN(0);
236 }
237
238 int osc_quota_exit()
239 {
240         struct osc_quota_info *oqi, *n;
241         int                    i, rc;
242         ENTRY;
243
244         cfs_spin_lock(&qinfo_list_lock);
245         for (i = 0; i < NR_DQHASH; i++) {
246                 cfs_list_for_each_entry_safe(oqi, n, &qinfo_hash[i], oqi_hash) {
247                         remove_qinfo_hash(oqi);
248                         free_qinfo(oqi);
249                 }
250         }
251         cfs_spin_unlock(&qinfo_list_lock);
252
253         rc = cfs_mem_cache_destroy(qinfo_cachep);
254         LASSERTF(rc == 0, "couldn't destory qinfo_cachep slab\n");
255         qinfo_cachep = NULL;
256
257         RETURN(0);
258 }
259
260 int osc_quotactl(struct obd_device *unused, struct obd_export *exp,
261                  struct obd_quotactl *oqctl)
262 {
263         struct ptlrpc_request *req;
264         struct obd_quotactl   *oqc;
265         int                    rc;
266         ENTRY;
267
268         req = ptlrpc_request_alloc_pack(class_exp2cliimp(exp),
269                                         &RQF_OST_QUOTACTL, LUSTRE_OST_VERSION,
270                                         OST_QUOTACTL);
271         if (req == NULL)
272                 RETURN(-ENOMEM);
273
274         oqc = req_capsule_client_get(&req->rq_pill, &RMF_OBD_QUOTACTL);
275         *oqc = *oqctl;
276
277         ptlrpc_request_set_replen(req);
278         ptlrpc_at_set_req_timeout(req);
279         req->rq_no_resend = 1;
280
281         rc = ptlrpc_queue_wait(req);
282         if (rc)
283                 CERROR("ptlrpc_queue_wait failed, rc: %d\n", rc);
284
285         if (req->rq_repmsg &&
286             (oqc = req_capsule_server_get(&req->rq_pill, &RMF_OBD_QUOTACTL))) {
287                 *oqctl = *oqc;
288         } else if (!rc) {
289                 CERROR ("Can't unpack obd_quotactl\n");
290                 rc = -EPROTO;
291         }
292         ptlrpc_req_finished(req);
293
294         RETURN(rc);
295 }
296
297 int osc_quotacheck(struct obd_device *unused, struct obd_export *exp,
298                    struct obd_quotactl *oqctl)
299 {
300         struct client_obd       *cli = &exp->exp_obd->u.cli;
301         struct ptlrpc_request   *req;
302         struct obd_quotactl     *body;
303         int                      rc;
304         ENTRY;
305
306         req = ptlrpc_request_alloc_pack(class_exp2cliimp(exp),
307                                         &RQF_OST_QUOTACHECK, LUSTRE_OST_VERSION,
308                                         OST_QUOTACHECK);
309         if (req == NULL)
310                 RETURN(-ENOMEM);
311
312         body = req_capsule_client_get(&req->rq_pill, &RMF_OBD_QUOTACTL);
313         *body = *oqctl;
314
315         ptlrpc_request_set_replen(req);
316
317         /* the next poll will find -ENODATA, that means quotacheck is
318          * going on */
319         cli->cl_qchk_stat = -ENODATA;
320         rc = ptlrpc_queue_wait(req);
321         if (rc)
322                 cli->cl_qchk_stat = rc;
323         ptlrpc_req_finished(req);
324         RETURN(rc);
325 }
326
327 int osc_quota_poll_check(struct obd_export *exp, struct if_quotacheck *qchk)
328 {
329         struct client_obd *cli = &exp->exp_obd->u.cli;
330         int rc;
331         ENTRY;
332
333         qchk->obd_uuid = cli->cl_target_uuid;
334         memcpy(qchk->obd_type, LUSTRE_OST_NAME, strlen(LUSTRE_OST_NAME));
335
336         rc = cli->cl_qchk_stat;
337         /* the client is not the previous one */
338         if (rc == CL_NOT_QUOTACHECKED)
339                 rc = -EINTR;
340         RETURN(rc);
341 }
342
343 int osc_quota_adjust_qunit(struct obd_export *exp,
344                            struct quota_adjust_qunit *oqaq,
345                            struct lustre_quota_ctxt *qctxt,
346                            struct ptlrpc_request_set *rqset)
347 {
348         struct ptlrpc_request     *req;
349         struct quota_adjust_qunit *oqa;
350         int                        rc = 0;
351         ENTRY;
352
353         /* client don't support this kind of operation, abort it */
354         if (!(exp->exp_connect_flags & OBD_CONNECT_CHANGE_QS)) {
355                 CDEBUG(D_QUOTA, "osc: %s don't support change qunit size\n",
356                        exp->exp_obd->obd_name);
357                 RETURN(rc);
358         }
359         if (strcmp(exp->exp_obd->obd_type->typ_name, LUSTRE_OSC_NAME))
360                 RETURN(-EINVAL);
361
362         LASSERT(rqset);
363
364         req = ptlrpc_request_alloc_pack(class_exp2cliimp(exp),
365                                         &RQF_OST_QUOTA_ADJUST_QUNIT,
366                                         LUSTRE_OST_VERSION,
367                                         OST_QUOTA_ADJUST_QUNIT);
368         if (req == NULL)
369                 RETURN(-ENOMEM);
370
371         oqa = req_capsule_client_get(&req->rq_pill, &RMF_QUOTA_ADJUST_QUNIT);
372         *oqa = *oqaq;
373
374         ptlrpc_request_set_replen(req);
375
376         ptlrpc_set_add_req(rqset, req);
377         RETURN(rc);
378 }