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