Whamcloud - gitweb
LU-8801 quota: fix ldiskfs accounting iterator
[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=%d\n", type);
68
69         ret = sb->s_op->quota_read(sb, type, buf, LUSTRE_DQBLKSIZE,
70                                    blk << LUSTRE_DQBLKSIZE_BITS);
71
72         /* Reading past EOF just returns a block of zeros */
73         if (ret == -EBADR)
74                 ret = 0;
75
76         RETURN(ret);
77 }
78
79 /**
80  * Find entry in block by given \a dqid in the leaf block \a blk
81  *
82  * \retval +ve, the offset of the entry in file
83  * \retval   0, entry not found
84  * \retval -ve, unexpected failure
85  */
86 static loff_t find_block_dqentry(const struct lu_env *env,
87                                  struct osd_object *obj, int type,
88                                  qid_t dqid, uint blk,
89                                  struct osd_it_quota *it)
90 {
91         dqbuf_t                          buf = getdqbuf();
92         loff_t                           ret;
93         int                              i;
94         struct lustre_disk_dqblk_v2     *ddquot;
95         int                              dqblk_sz;
96
97         ENTRY;
98
99         ddquot = (struct lustre_disk_dqblk_v2 *)GETENTRIES(buf);
100         dqblk_sz = sizeof(struct lustre_disk_dqblk_v2);
101         if (!buf)
102                 RETURN(-ENOMEM);
103         ret = quota_read_blk(env, obj, type, blk, buf);
104         if (ret < 0) {
105                 CERROR("Can't read quota tree block %u.\n", blk);
106                 GOTO(out_buf, ret);
107         }
108
109         if (dqid) {
110                 for (i = 0; i < LUSTRE_DQSTRINBLK &&
111                             le32_to_cpu(ddquot[i].dqb_id) != dqid; i++)
112                         continue;
113         } else { /* ID 0 as a bit more complicated searching... */
114                 for (i = 0; i < LUSTRE_DQSTRINBLK; i++)
115                         if (!le32_to_cpu(ddquot[i].dqb_id) &&
116                             memcmp((char *)&emptydquot, (char *)&ddquot[i],
117                                    dqblk_sz))
118                                 break;
119         }
120         if (i == LUSTRE_DQSTRINBLK) {
121                 CDEBUG(D_QUOTA, "Quota for id %u not found.\n", dqid);
122                 ret = 0;
123                 GOTO(out_buf, ret);
124         } else {
125                 ret = (blk << LUSTRE_DQBLKSIZE_BITS) +
126                       sizeof(struct lustre_disk_dqdbheader) + i * dqblk_sz;
127
128                 if (it) {
129                         it->oiq_blk[LUSTRE_DQTREEDEPTH] = blk;
130                         it->oiq_offset = ret;
131                         it->oiq_id = dqid;
132                         it->oiq_index[LUSTRE_DQTREEDEPTH] = i;
133                 } else {
134                         ret = 0;
135                 }
136         }
137 out_buf:
138         freedqbuf(buf);
139         RETURN(ret);
140 }
141
142 /**
143  * Find entry for given \a dqid in the tree block \a blk
144  *
145  * \retval +ve, the offset of the entry in file
146  * \retval   0, entry not found
147  * \retval -ve, unexpected failure
148  */
149 loff_t find_tree_dqentry(const struct lu_env *env,
150                          struct osd_object *obj, int type,
151                          qid_t dqid, uint blk, int depth,
152                          struct osd_it_quota *it)
153 {
154         dqbuf_t  buf = getdqbuf();
155         loff_t   ret;
156         u32     *ref = (u32 *) buf;
157
158         ENTRY;
159
160         if (!buf)
161                 RETURN(-ENOMEM);
162         ret = quota_read_blk(env, obj, type, blk, buf);
163         if (ret < 0) {
164                 CERROR("Can't read quota tree block %u.\n", blk);
165                 GOTO(out_buf, ret);
166         }
167         ret = 0;
168         blk = le32_to_cpu(ref[GETIDINDEX(dqid, depth)]);
169         if (!blk)               /* No reference? */
170                 GOTO(out_buf, ret);
171
172         if (depth < LUSTRE_DQTREEDEPTH - 1)
173                 ret = find_tree_dqentry(env, obj, type, dqid, blk,
174                                         depth + 1, it);
175         else
176                 ret = find_block_dqentry(env, obj, type, dqid, blk, it);
177
178         if (it && ret > 0) {
179                 it->oiq_blk[depth + 1] = blk;
180                 it->oiq_index[depth] = GETIDINDEX(dqid, depth);
181         }
182
183 out_buf:
184         freedqbuf(buf);
185         RETURN(ret);
186 }
187
188 /**
189  * Search from \a index within the leaf block \a blk, and fill the \a it with
190  * the first valid entry.
191  *
192  * \retval +ve, no valid entry found
193  * \retval   0, entry found
194  * \retval -ve, unexpected failure
195  */
196 int walk_block_dqentry(const struct lu_env *env, struct osd_object *obj,
197                        int type, uint blk, uint index,
198                        struct osd_it_quota *it)
199 {
200         dqbuf_t                          buf;
201         loff_t                           ret = 0;
202         struct lustre_disk_dqdbheader   *dqhead;
203         int                              i, dqblk_sz;
204         struct lustre_disk_dqblk_v2     *ddquot;
205         struct osd_quota_leaf           *leaf;
206         ENTRY;
207
208         /* check if the leaf block has been processed before */
209         list_for_each_entry(leaf, &it->oiq_list, oql_link) {
210                 if (leaf->oql_blk == blk)
211                         RETURN(1);
212         }
213
214         buf = getdqbuf();
215         dqhead = (struct lustre_disk_dqdbheader *)buf;
216         dqblk_sz = sizeof(struct lustre_disk_dqblk_v2);
217         if (!buf)
218                 RETURN(-ENOMEM);
219         ret = quota_read_blk(env, obj, type, blk, buf);
220         if (ret < 0) {
221                 CERROR("Can't read quota tree block %u.\n", blk);
222                 GOTO(out_buf, ret);
223         }
224         ret = 1;
225
226         if (!le16_to_cpu(dqhead->dqdh_entries))
227                 GOTO(out_buf, ret);
228
229         ddquot = (struct lustre_disk_dqblk_v2 *)GETENTRIES(buf);
230         LASSERT(index < LUSTRE_DQSTRINBLK);
231         for (i = index; i < LUSTRE_DQSTRINBLK; i++) {
232                 /* skip empty entry */
233                 if (!memcmp((char *)&emptydquot,
234                             (char *)&ddquot[i], dqblk_sz))
235                         continue;
236
237                 it->oiq_blk[LUSTRE_DQTREEDEPTH] = blk;
238                 it->oiq_id = le32_to_cpu(ddquot[i].dqb_id);
239                 it->oiq_offset = (blk << LUSTRE_DQBLKSIZE_BITS) +
240                                   sizeof(struct lustre_disk_dqdbheader) +
241                                   i * dqblk_sz;
242                 it->oiq_index[LUSTRE_DQTREEDEPTH] = i;
243                 ret = 0;
244                 break;
245         }
246
247 out_buf:
248         freedqbuf(buf);
249         RETURN(ret);
250 }
251
252 /**
253  * Search from \a index within the tree block \a blk, and fill the \a it
254  * with the first valid entry.
255  *
256  * \retval +ve, no valid entry found
257  * \retval   0, entry found
258  * \retval -ve, unexpected failure
259  */
260 int walk_tree_dqentry(const struct lu_env *env, struct osd_object *obj,
261                       int type, uint blk, int depth, uint index,
262                       struct osd_it_quota *it)
263 {
264         dqbuf_t  buf = getdqbuf();
265         loff_t   ret;
266         u32     *ref = (u32 *) buf;
267
268         ENTRY;
269
270         if (!buf)
271                 RETURN(-ENOMEM);
272         ret = quota_read_blk(env, obj, type, blk, buf);
273         if (ret < 0) {
274                 CERROR("Can't read quota tree block %u.\n", blk);
275                 goto out_buf;
276         }
277         ret = 1;
278
279         for (; index <= 0xff && ret > 0; index++) {
280                 blk = le32_to_cpu(ref[index]);
281                 if (!blk)       /* No reference */
282                         continue;
283
284                 if (depth < LUSTRE_DQTREEDEPTH - 1)
285                         ret = walk_tree_dqentry(env, obj, type, blk,
286                                                 depth + 1, 0, it);
287                 else
288                         ret = walk_block_dqentry(env, obj, type, blk, 0, it);
289
290         }
291         it->oiq_blk[depth + 1] = blk;
292         it->oiq_index[depth] = index;
293
294 out_buf:
295         freedqbuf(buf);
296         RETURN(ret);
297 }