Whamcloud - gitweb
LU-4017 quota: enable project quota limits
[fs/lustre-release.git] / lustre / osd-ldiskfs / osd_quota_fmt.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, 2015, Intel Corporation.
25  * Use is subject to license terms.
26  *
27  * Lustre administrative quota format.
28  * from linux/fs/quota_v2.c
29  */
30
31 #include "osd_internal.h"
32 #include "osd_quota_fmt.h"
33
34 typedef char *dqbuf_t;
35
36 static const union
37 {
38         struct lustre_disk_dqblk_v2 r1;
39 } emptydquot = { .r1 = { 0 } };
40
41 static inline dqbuf_t getdqbuf(void)
42 {
43         dqbuf_t buf = kmalloc(LUSTRE_DQBLKSIZE, GFP_NOFS);
44         if (!buf)
45                 CWARN("Not enough memory for quota buffers.\n");
46         return buf;
47 }
48
49 static inline void freedqbuf(dqbuf_t buf)
50 {
51         kfree(buf);
52 }
53
54 /**
55  * Read the \a blk into \a buf.
56  */
57 static ssize_t quota_read_blk(const struct lu_env *env,
58                               struct osd_object *obj,
59                               int type, uint blk, dqbuf_t buf)
60 {
61         ssize_t ret;
62         struct super_block *sb = obj->oo_inode->i_sb;
63
64         ENTRY;
65
66         memset(buf, 0, LUSTRE_DQBLKSIZE);
67         LASSERTF((type == USRQUOTA || type == GRPQUOTA || type == PRJQUOTA),
68                  "type=%d\n", type);
69
70         ret = sb->s_op->quota_read(sb, type, buf, LUSTRE_DQBLKSIZE,
71                                    blk << LUSTRE_DQBLKSIZE_BITS);
72
73         /* Reading past EOF just returns a block of zeros */
74         if (ret == -EBADR)
75                 ret = 0;
76
77         RETURN(ret);
78 }
79
80 /**
81  * Find entry in block by given \a dqid in the leaf block \a blk
82  *
83  * \retval +ve, the offset of the entry in file
84  * \retval   0, entry not found
85  * \retval -ve, unexpected failure
86  */
87 static loff_t find_block_dqentry(const struct lu_env *env,
88                                  struct osd_object *obj, int type,
89                                  qid_t dqid, uint blk,
90                                  struct osd_it_quota *it)
91 {
92         dqbuf_t                          buf = getdqbuf();
93         loff_t                           ret;
94         int                              i;
95         struct lustre_disk_dqblk_v2     *ddquot;
96         int                              dqblk_sz;
97
98         ENTRY;
99
100         ddquot = (struct lustre_disk_dqblk_v2 *)GETENTRIES(buf);
101         dqblk_sz = sizeof(struct lustre_disk_dqblk_v2);
102         if (!buf)
103                 RETURN(-ENOMEM);
104         ret = quota_read_blk(env, obj, type, blk, buf);
105         if (ret < 0) {
106                 CERROR("Can't read quota tree block %u.\n", blk);
107                 GOTO(out_buf, ret);
108         }
109
110         if (dqid) {
111                 for (i = 0; i < LUSTRE_DQSTRINBLK &&
112                             le32_to_cpu(ddquot[i].dqb_id) != dqid; i++)
113                         continue;
114         } else { /* ID 0 as a bit more complicated searching... */
115                 for (i = 0; i < LUSTRE_DQSTRINBLK; i++)
116                         if (!le32_to_cpu(ddquot[i].dqb_id) &&
117                             memcmp((char *)&emptydquot, (char *)&ddquot[i],
118                                    dqblk_sz))
119                                 break;
120         }
121         if (i == LUSTRE_DQSTRINBLK) {
122                 CDEBUG(D_QUOTA, "Quota for id %u not found.\n", dqid);
123                 ret = 0;
124                 GOTO(out_buf, ret);
125         } else {
126                 ret = (blk << LUSTRE_DQBLKSIZE_BITS) +
127                       sizeof(struct lustre_disk_dqdbheader) + i * dqblk_sz;
128
129                 if (it) {
130                         it->oiq_blk[LUSTRE_DQTREEDEPTH] = blk;
131                         it->oiq_offset = ret;
132                         it->oiq_id = dqid;
133                         it->oiq_index[LUSTRE_DQTREEDEPTH] = i;
134                 } else {
135                         ret = 0;
136                 }
137         }
138 out_buf:
139         freedqbuf(buf);
140         RETURN(ret);
141 }
142
143 /**
144  * Find entry for given \a dqid in the tree block \a blk
145  *
146  * \retval +ve, the offset of the entry in file
147  * \retval   0, entry not found
148  * \retval -ve, unexpected failure
149  */
150 loff_t find_tree_dqentry(const struct lu_env *env,
151                          struct osd_object *obj, int type,
152                          qid_t dqid, uint blk, int depth,
153                          struct osd_it_quota *it)
154 {
155         dqbuf_t  buf = getdqbuf();
156         loff_t   ret;
157         u32     *ref = (u32 *) buf;
158
159         ENTRY;
160
161         if (!buf)
162                 RETURN(-ENOMEM);
163         ret = quota_read_blk(env, obj, type, blk, buf);
164         if (ret < 0) {
165                 CERROR("Can't read quota tree block %u.\n", blk);
166                 GOTO(out_buf, ret);
167         }
168         ret = 0;
169         blk = le32_to_cpu(ref[GETIDINDEX(dqid, depth)]);
170         if (!blk)               /* No reference? */
171                 GOTO(out_buf, ret);
172
173         if (depth < LUSTRE_DQTREEDEPTH - 1)
174                 ret = find_tree_dqentry(env, obj, type, dqid, blk,
175                                         depth + 1, it);
176         else
177                 ret = find_block_dqentry(env, obj, type, dqid, blk, it);
178
179         if (it && ret > 0) {
180                 it->oiq_blk[depth + 1] = blk;
181                 it->oiq_index[depth] = GETIDINDEX(dqid, depth);
182         }
183
184 out_buf:
185         freedqbuf(buf);
186         RETURN(ret);
187 }
188
189 /**
190  * Search from \a index within the leaf block \a blk, and fill the \a it with
191  * the first valid entry.
192  *
193  * \retval +ve, no valid entry found
194  * \retval   0, entry found
195  * \retval -ve, unexpected failure
196  */
197 int walk_block_dqentry(const struct lu_env *env, struct osd_object *obj,
198                        int type, uint blk, uint index,
199                        struct osd_it_quota *it)
200 {
201         dqbuf_t                          buf;
202         loff_t                           ret = 0;
203         struct lustre_disk_dqdbheader   *dqhead;
204         int                              i, dqblk_sz;
205         struct lustre_disk_dqblk_v2     *ddquot;
206         struct osd_quota_leaf           *leaf;
207         ENTRY;
208
209         /* check if the leaf block has been processed before */
210         list_for_each_entry(leaf, &it->oiq_list, oql_link) {
211                 if (leaf->oql_blk == blk)
212                         RETURN(1);
213         }
214
215         buf = getdqbuf();
216         dqhead = (struct lustre_disk_dqdbheader *)buf;
217         dqblk_sz = sizeof(struct lustre_disk_dqblk_v2);
218         if (!buf)
219                 RETURN(-ENOMEM);
220         ret = quota_read_blk(env, obj, type, blk, buf);
221         if (ret < 0) {
222                 CERROR("Can't read quota tree block %u.\n", blk);
223                 GOTO(out_buf, ret);
224         }
225         ret = 1;
226
227         if (!le16_to_cpu(dqhead->dqdh_entries))
228                 GOTO(out_buf, ret);
229
230         ddquot = (struct lustre_disk_dqblk_v2 *)GETENTRIES(buf);
231         LASSERT(index < LUSTRE_DQSTRINBLK);
232         for (i = index; i < LUSTRE_DQSTRINBLK; i++) {
233                 /* skip empty entry */
234                 if (!memcmp((char *)&emptydquot,
235                             (char *)&ddquot[i], dqblk_sz))
236                         continue;
237
238                 it->oiq_blk[LUSTRE_DQTREEDEPTH] = blk;
239                 it->oiq_id = le32_to_cpu(ddquot[i].dqb_id);
240                 it->oiq_offset = (blk << LUSTRE_DQBLKSIZE_BITS) +
241                                   sizeof(struct lustre_disk_dqdbheader) +
242                                   i * dqblk_sz;
243                 it->oiq_index[LUSTRE_DQTREEDEPTH] = i;
244                 ret = 0;
245                 break;
246         }
247
248 out_buf:
249         freedqbuf(buf);
250         RETURN(ret);
251 }
252
253 /**
254  * Search from \a index within the tree block \a blk, and fill the \a it
255  * with the first valid entry.
256  *
257  * \retval +ve, no valid entry found
258  * \retval   0, entry found
259  * \retval -ve, unexpected failure
260  */
261 int walk_tree_dqentry(const struct lu_env *env, struct osd_object *obj,
262                       int type, uint blk, int depth, uint index,
263                       struct osd_it_quota *it)
264 {
265         dqbuf_t  buf = getdqbuf();
266         loff_t   ret;
267         u32     *ref = (u32 *) buf;
268
269         ENTRY;
270
271         if (!buf)
272                 RETURN(-ENOMEM);
273         ret = quota_read_blk(env, obj, type, blk, buf);
274         if (ret < 0) {
275                 CERROR("Can't read quota tree block %u.\n", blk);
276                 goto out_buf;
277         }
278         ret = 1;
279
280         for (; index <= 0xff && ret > 0; index++) {
281                 blk = le32_to_cpu(ref[index]);
282                 if (!blk)       /* No reference */
283                         continue;
284
285                 if (depth < LUSTRE_DQTREEDEPTH - 1)
286                         ret = walk_tree_dqentry(env, obj, type, blk,
287                                                 depth + 1, 0, it);
288                 else
289                         ret = walk_block_dqentry(env, obj, type, blk, 0, it);
290
291         }
292         it->oiq_blk[depth + 1] = blk;
293         it->oiq_index[depth] = index;
294
295 out_buf:
296         freedqbuf(buf);
297         RETURN(ret);
298 }