Whamcloud - gitweb
d8179537b2bcd5855525a07537c129455d9e6197
[fs/lustre-release.git] / lustre / osc / osc_quota.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) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
25  *
26  * Copyright (c) 2012, 2015, Intel Corporation.
27  *
28  * Code originally extracted from quota directory
29  */
30
31 #include <obd.h>
32 #include "osc_internal.h"
33
34 static inline struct osc_quota_info *osc_oqi_alloc(u32 id)
35 {
36         struct osc_quota_info *oqi;
37
38         OBD_SLAB_ALLOC_PTR(oqi, osc_quota_kmem);
39         if (oqi != NULL)
40                 oqi->oqi_id = id;
41
42         return oqi;
43 }
44
45 int osc_quota_chkdq(struct client_obd *cli, const unsigned int qid[])
46 {
47         int type;
48         ENTRY;
49
50         for (type = 0; type < LL_MAXQUOTAS; type++) {
51                 struct osc_quota_info *oqi;
52
53                 oqi = cfs_hash_lookup(cli->cl_quota_hash[type], &qid[type]);
54                 if (oqi) {
55                         /* do not try to access oqi here, it could have been
56                          * freed by osc_quota_setdq() */
57
58                         /* the slot is busy, the user is about to run out of
59                          * quota space on this OST */
60                         CDEBUG(D_QUOTA, "chkdq found noquota for %s %d\n",
61                                type == USRQUOTA ? "user" : "grout", qid[type]);
62                         RETURN(NO_QUOTA);
63                 }
64         }
65
66         RETURN(QUOTA_OK);
67 }
68
69 static inline u32 md_quota_flag(int qtype)
70 {
71         switch (qtype) {
72         case USRQUOTA:
73                 return OBD_MD_FLUSRQUOTA;
74         case GRPQUOTA:
75                 return OBD_MD_FLGRPQUOTA;
76         default:
77                 return 0;
78         }
79 }
80
81 static inline u32 fl_quota_flag(int qtype)
82 {
83         switch (qtype) {
84         case USRQUOTA:
85                 return OBD_FL_NO_USRQUOTA;
86         case GRPQUOTA:
87                 return OBD_FL_NO_GRPQUOTA;
88         default:
89                 return 0;
90         }
91 }
92
93 int osc_quota_setdq(struct client_obd *cli, const unsigned int qid[],
94                     u64 valid, u32 flags)
95 {
96         int type;
97         int rc = 0;
98
99         ENTRY;
100
101         if ((valid & (OBD_MD_FLUSRQUOTA | OBD_MD_FLGRPQUOTA)) == 0)
102                 RETURN(0);
103
104         for (type = 0; type < LL_MAXQUOTAS; type++) {
105                 struct osc_quota_info *oqi;
106
107                 if ((valid & md_quota_flag(type)) == 0)
108                         continue;
109
110                 /* lookup the ID in the per-type hash table */
111                 oqi = cfs_hash_lookup(cli->cl_quota_hash[type], &qid[type]);
112                 if ((flags & fl_quota_flag(type)) != 0) {
113                         /* This ID is getting close to its quota limit, let's
114                          * switch to sync I/O */
115                         if (oqi != NULL)
116                                 continue;
117
118                         oqi = osc_oqi_alloc(qid[type]);
119                         if (oqi == NULL) {
120                                 rc = -ENOMEM;
121                                 break;
122                         }
123
124                         rc = cfs_hash_add_unique(cli->cl_quota_hash[type],
125                                                  &qid[type], &oqi->oqi_hash);
126                         /* race with others? */
127                         if (rc == -EALREADY) {
128                                 rc = 0;
129                                 OBD_SLAB_FREE_PTR(oqi, osc_quota_kmem);
130                         }
131
132                         CDEBUG(D_QUOTA, "%s: setdq to insert for %s %d (%d)\n",
133                                cli_name(cli), qtype_name(type), qid[type], rc);
134                 } else {
135                         /* This ID is now off the hook, let's remove it from
136                          * the hash table */
137                         if (oqi == NULL)
138                                 continue;
139
140                         oqi = cfs_hash_del_key(cli->cl_quota_hash[type],
141                                                &qid[type]);
142                         if (oqi)
143                                 OBD_SLAB_FREE_PTR(oqi, osc_quota_kmem);
144
145                         CDEBUG(D_QUOTA, "%s: setdq to remove for %s %d (%p)\n",
146                                cli_name(cli), qtype_name(type), qid[type], oqi);
147                 }
148         }
149
150         RETURN(rc);
151 }
152
153 /*
154  * Hash operations for uid/gid <-> osc_quota_info
155  */
156 static unsigned
157 oqi_hashfn(struct cfs_hash *hs, const void *key, unsigned mask)
158 {
159         return cfs_hash_u32_hash(*((__u32*)key), mask);
160 }
161
162 static int
163 oqi_keycmp(const void *key, struct hlist_node *hnode)
164 {
165         struct osc_quota_info *oqi;
166         u32 uid;
167
168         LASSERT(key != NULL);
169         uid = *((u32 *)key);
170         oqi = hlist_entry(hnode, struct osc_quota_info, oqi_hash);
171
172         return uid == oqi->oqi_id;
173 }
174
175 static void *
176 oqi_key(struct hlist_node *hnode)
177 {
178         struct osc_quota_info *oqi;
179         oqi = hlist_entry(hnode, struct osc_quota_info, oqi_hash);
180         return &oqi->oqi_id;
181 }
182
183 static void *
184 oqi_object(struct hlist_node *hnode)
185 {
186         return hlist_entry(hnode, struct osc_quota_info, oqi_hash);
187 }
188
189 static void
190 oqi_get(struct cfs_hash *hs, struct hlist_node *hnode)
191 {
192 }
193
194 static void
195 oqi_put_locked(struct cfs_hash *hs, struct hlist_node *hnode)
196 {
197 }
198
199 static void
200 oqi_exit(struct cfs_hash *hs, struct hlist_node *hnode)
201 {
202         struct osc_quota_info *oqi;
203
204         oqi = hlist_entry(hnode, struct osc_quota_info, oqi_hash);
205
206         OBD_SLAB_FREE_PTR(oqi, osc_quota_kmem);
207 }
208
209 #define HASH_QUOTA_BKT_BITS 5
210 #define HASH_QUOTA_CUR_BITS 5
211 #define HASH_QUOTA_MAX_BITS 15
212
213 static struct cfs_hash_ops quota_hash_ops = {
214         .hs_hash        = oqi_hashfn,
215         .hs_keycmp      = oqi_keycmp,
216         .hs_key         = oqi_key,
217         .hs_object      = oqi_object,
218         .hs_get         = oqi_get,
219         .hs_put_locked  = oqi_put_locked,
220         .hs_exit        = oqi_exit,
221 };
222
223 int osc_quota_setup(struct obd_device *obd)
224 {
225         struct client_obd *cli = &obd->u.cli;
226         int i, type;
227         ENTRY;
228
229         for (type = 0; type < LL_MAXQUOTAS; type++) {
230                 cli->cl_quota_hash[type] = cfs_hash_create("QUOTA_HASH",
231                                                            HASH_QUOTA_CUR_BITS,
232                                                            HASH_QUOTA_MAX_BITS,
233                                                            HASH_QUOTA_BKT_BITS,
234                                                            0,
235                                                            CFS_HASH_MIN_THETA,
236                                                            CFS_HASH_MAX_THETA,
237                                                            &quota_hash_ops,
238                                                            CFS_HASH_DEFAULT);
239                 if (cli->cl_quota_hash[type] == NULL)
240                         break;
241         }
242
243         if (type == LL_MAXQUOTAS)
244                 RETURN(0);
245
246         for (i = 0; i < type; i++)
247                 cfs_hash_putref(cli->cl_quota_hash[i]);
248
249         RETURN(-ENOMEM);
250 }
251
252 int osc_quota_cleanup(struct obd_device *obd)
253 {
254         struct client_obd     *cli = &obd->u.cli;
255         int type;
256         ENTRY;
257
258         for (type = 0; type < LL_MAXQUOTAS; type++)
259                 cfs_hash_putref(cli->cl_quota_hash[type]);
260
261         RETURN(0);
262 }
263
264 int osc_quotactl(struct obd_device *unused, struct obd_export *exp,
265                  struct obd_quotactl *oqctl)
266 {
267         struct ptlrpc_request *req;
268         struct obd_quotactl   *oqc;
269         int                    rc;
270         ENTRY;
271
272         req = ptlrpc_request_alloc_pack(class_exp2cliimp(exp),
273                                         &RQF_OST_QUOTACTL, LUSTRE_OST_VERSION,
274                                         OST_QUOTACTL);
275         if (req == NULL)
276                 RETURN(-ENOMEM);
277
278         oqc = req_capsule_client_get(&req->rq_pill, &RMF_OBD_QUOTACTL);
279         *oqc = *oqctl;
280
281         ptlrpc_request_set_replen(req);
282         ptlrpc_at_set_req_timeout(req);
283         req->rq_no_resend = 1;
284
285         rc = ptlrpc_queue_wait(req);
286         if (rc)
287                 CERROR("ptlrpc_queue_wait failed, rc: %d\n", rc);
288
289         if (req->rq_repmsg &&
290             (oqc = req_capsule_server_get(&req->rq_pill, &RMF_OBD_QUOTACTL))) {
291                 *oqctl = *oqc;
292         } else if (!rc) {
293                 CERROR ("Can't unpack obd_quotactl\n");
294                 rc = -EPROTO;
295         }
296         ptlrpc_req_finished(req);
297
298         RETURN(rc);
299 }