Whamcloud - gitweb
LU-1842 quota: add quota disk operations
[fs/lustre-release.git] / lustre / quota / qsd_lib.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 Whamcloud, Inc.
25  * Use is subject to license terms.
26  *
27  * Author: Johann Lombardi <johann@whamcloud.com>
28  * Author: Niu    Yawei    <niu@whamcloud.com>
29  */
30
31 /*
32  * Quota Slave Driver (QSD) management.
33  */
34
35 #ifndef EXPORT_SYMTAB
36 # define EXPORT_SYMTAB
37 #endif
38
39 #define DEBUG_SUBSYSTEM S_LQUOTA
40
41 #include "qsd_internal.h"
42
43 /* some procfs helpers */
44 static int lprocfs_qsd_rd_state(char *page, char **start, off_t off,
45                                 int count, int *eof, void *data)
46 {
47         struct qsd_instance     *qsd = (struct qsd_instance *)data;
48
49         LASSERT(qsd != NULL);
50
51         return snprintf(page, count,
52                         "target name:    %s\n"
53                         "pool ID:        %d\n"
54                         "type:           %s\n"
55                         "quota enabled:  none\n",
56                         qsd->qsd_svname, qsd->qsd_pool_id,
57                         qsd->qsd_is_md ? "md" : "dt");
58 }
59
60 static struct lprocfs_vars lprocfs_quota_qsd_vars[] = {
61         { "info", lprocfs_qsd_rd_state, 0, 0},
62         { NULL }
63 };
64
65 /*
66  * Release qsd_qtype_info structure which contains data associated with a
67  * given quota type. This releases the accounting objects.
68  * It's called on OSD cleanup when the qsd instance is released.
69  *
70  * \param env - is the environment passed by the caller
71  * \param qsd - is the qsd instance managing the qsd_qtype_info structure
72  *              to be released
73  * \param qtype - is the quota type to be shutdown
74  */
75 static void qsd_qtype_fini(const struct lu_env *env, struct qsd_instance *qsd,
76                            int qtype)
77 {
78         struct qsd_qtype_info   *qqi;
79         ENTRY;
80
81         if (qsd->qsd_type_array[qtype] == NULL)
82                 RETURN_EXIT;
83         qqi = qsd->qsd_type_array[qtype];
84         qsd->qsd_type_array[qtype] = NULL;
85
86         /* release accounting object */
87         if (qqi->qqi_acct_obj != NULL && !IS_ERR(qqi->qqi_acct_obj)) {
88                 lu_object_put(env, &qqi->qqi_acct_obj->do_lu);
89                 qqi->qqi_acct_obj = NULL;
90         }
91
92         /* release slv index */
93         if (qqi->qqi_slv_obj != NULL && !IS_ERR(qqi->qqi_slv_obj)) {
94                 lu_object_put(env, &qqi->qqi_slv_obj->do_lu);
95                 qqi->qqi_slv_obj = NULL;
96                 qqi->qqi_slv_ver = 0;
97         }
98
99         /* release global index */
100         if (qqi->qqi_glb_obj != NULL && !IS_ERR(qqi->qqi_glb_obj)) {
101                 lu_object_put(env, &qqi->qqi_glb_obj->do_lu);
102                 qqi->qqi_glb_obj = NULL;
103                 qqi->qqi_glb_ver = 0;
104         }
105
106         OBD_FREE_PTR(qqi);
107         EXIT;
108 }
109
110 /*
111  * Allocate and initialize a qsd_qtype_info structure for quota type \qtype.
112  * This opens the accounting object and initializes the proc file.
113  * It's called on OSD start when the qsd instance is created.
114  *
115  * \param env  - the environment passed by the caller
116  * \param qsd  - is the qsd instance which will be in charge of the new
117  *               qsd_qtype_info instance.
118  * \param qtype - is quota type to set up
119  *
120  * \retval - 0 on success and qsd->qsd_type_array[qtype] is allocated,
121  *           appropriate error on failure
122  */
123 static int qsd_qtype_init(const struct lu_env *env, struct qsd_instance *qsd,
124                           int qtype)
125 {
126         struct qsd_qtype_info   *qqi;
127         int                      rc;
128         struct obd_uuid          uuid;
129         ENTRY;
130
131         LASSERT(qsd->qsd_type_array[qtype] == NULL);
132
133         /* allocate structure for this quota type */
134         OBD_ALLOC_PTR(qqi);
135         if (qqi == NULL)
136                 RETURN(-ENOMEM);
137         qsd->qsd_type_array[qtype] = qqi;
138
139         /* set backpointer and other parameters */
140         qqi->qqi_qsd   = qsd;
141         qqi->qqi_qtype = qtype;
142         lquota_generate_fid(&qqi->qqi_fid, qsd->qsd_pool_id, QSD_RES_TYPE(qsd),
143                             qtype);
144
145         /* open accounting object */
146         LASSERT(qqi->qqi_acct_obj == NULL);
147         qqi->qqi_acct_obj = acct_obj_lookup(env, qsd->qsd_dev,
148                                             qtype == USRQUOTA ? ACCT_USER_OID
149                                                               : ACCT_GROUP_OID);
150         /* don't print any error message on failure in order not to confuse
151          * non-OFD user (e.g. 2.3 MDT stack) */
152         if (IS_ERR(qqi->qqi_acct_obj))
153                 qqi->qqi_acct_obj = NULL;
154
155         /* open global index copy */
156         LASSERT(qqi->qqi_glb_obj == NULL);
157         qqi->qqi_glb_obj = lquota_disk_glb_find_create(env, qsd->qsd_dev,
158                                                        qsd->qsd_root,
159                                                        &qqi->qqi_fid, true);
160         if (IS_ERR(qqi->qqi_glb_obj)) {
161                 CERROR("%s: can't open global index copy "DFID" %ld\n",
162                        qsd->qsd_svname, PFID(&qqi->qqi_fid),
163                        PTR_ERR(qqi->qqi_glb_obj));
164                 GOTO(out, rc = PTR_ERR(qqi->qqi_glb_obj));
165         }
166         qqi->qqi_glb_ver = dt_version_get(env, qqi->qqi_glb_obj);
167
168         /* open slave index copy */
169         LASSERT(qqi->qqi_slv_obj == NULL);
170         obd_str2uuid(&uuid, qsd->qsd_svname);
171         qqi->qqi_slv_obj = lquota_disk_slv_find_create(env, qsd->qsd_dev,
172                                                        qsd->qsd_root,
173                                                        &qqi->qqi_fid, &uuid,
174                                                        true);
175         if (IS_ERR(qqi->qqi_slv_obj)) {
176                 CERROR("%s: can't open slave index copy "DFID" %ld\n",
177                        qsd->qsd_svname, PFID(&qqi->qqi_fid),
178                        PTR_ERR(qqi->qqi_slv_obj));
179                 GOTO(out, rc = PTR_ERR(qqi->qqi_slv_obj));
180         }
181         qqi->qqi_slv_ver = dt_version_get(env, qqi->qqi_slv_obj);
182
183         /* register proc entry for accounting object */
184         rc = lprocfs_seq_create(qsd->qsd_proc,
185                                 qtype == USRQUOTA ? "acct_user" : "acct_group",
186                                 0444, &lprocfs_quota_seq_fops,
187                                 qqi->qqi_acct_obj);
188         if (rc) {
189                 CWARN("%s: can't add procfs entry for accounting file %d\n",
190                       qsd->qsd_svname, rc);
191                 GOTO(out, rc);
192         }
193
194         EXIT;
195 out:
196         if (rc)
197                 qsd_qtype_fini(env, qsd, qtype);
198         return rc;
199 }
200
201 /*
202  * Release a qsd_instance. Companion of qsd_init(). This releases all data
203  * structures associated with the quota slave.
204  * This function should be called when the OSD is shutting down.
205  *
206  * \param env - is the environment passed by the caller
207  * \param qsd - is the qsd instance to shutdown
208  */
209 void qsd_fini(const struct lu_env *env, struct qsd_instance *qsd)
210 {
211         int     qtype;
212         ENTRY;
213
214         CDEBUG(D_QUOTA, "%s: initiating QSD shutdown\n", qsd->qsd_svname);
215         qsd->qsd_stopping = true;
216
217         /* remove qsd proc entry */
218         if (qsd->qsd_proc != NULL && !IS_ERR(qsd->qsd_proc)) {
219                 lprocfs_remove(&qsd->qsd_proc);
220                 qsd->qsd_proc = NULL;
221         }
222
223         /* free per-quota type data */
224         for (qtype = USRQUOTA; qtype < MAXQUOTAS; qtype++)
225                 qsd_qtype_fini(env, qsd, qtype);
226
227         /* release quota root directory */
228         if (qsd->qsd_root != NULL && !IS_ERR(qsd->qsd_root)) {
229                 lu_object_put(env, &qsd->qsd_root->do_lu);
230                 qsd->qsd_root = NULL;
231         }
232
233         /* release reference on dt_device */
234         if (qsd->qsd_dev != NULL) {
235                 lu_ref_del(&qsd->qsd_dev->dd_lu_dev.ld_reference, "qsd", qsd);
236                 lu_device_put(&qsd->qsd_dev->dd_lu_dev);
237                 qsd->qsd_dev = NULL;
238         }
239
240         OBD_FREE_PTR(qsd);
241         EXIT;
242 }
243 EXPORT_SYMBOL(qsd_fini);
244
245 /*
246  * Create a new qsd_instance to be associated with backend osd device
247  * identified by \dev. For now, this function just create procfs files which
248  * dumps the accounting information
249  *
250  * \param env    - the environment passed by the caller
251  * \param svname - is the service name of the OSD device creating this instance
252  * \param dev    - is the dt_device where to store quota index files
253  * \param osd_proc - is the procfs parent directory where to create procfs file
254  *                   related to this new qsd instance
255  *
256  * \retval - pointer to new qsd_instance associated with dev \dev on success,
257  *           appropriate error on failure
258  */
259 struct qsd_instance *qsd_init(const struct lu_env *env, char *svname,
260                               struct dt_device *dev,
261                               cfs_proc_dir_entry_t *osd_proc)
262 {
263         struct qsd_instance     *qsd;
264         int                      rc, qtype;
265         ENTRY;
266
267         /* allocate qsd instance */
268         OBD_ALLOC_PTR(qsd);
269         if (qsd == NULL)
270                 RETURN(ERR_PTR(-ENOMEM));
271
272         /* copy service name */
273         strncpy(qsd->qsd_svname, svname, MAX_OBD_NAME);
274
275         /* grab reference on osd device */
276         lu_device_get(&dev->dd_lu_dev);
277         lu_ref_add(&dev->dd_lu_dev.ld_reference, "qsd", qsd);
278         qsd->qsd_dev = dev;
279
280         /* we only support pool ID 0 (default data or metadata pool) for the
281          * time being. A different pool ID could be assigned to this target via
282          * the configuration log in the future */
283         qsd->qsd_pool_id  = 0;
284
285         /* Record whether this qsd instance is managing quota enforcement for a
286          * MDT (i.e. inode quota) or OST (block quota) */
287         qsd->qsd_is_md = lu_device_is_md(dev->dd_lu_dev.ld_site->ls_top_dev);
288
289         /* look-up on-disk directory for the quota slave */
290         qsd->qsd_root = lquota_disk_dir_find_create(env, dev, NULL, QSD_DIR);
291         if (IS_ERR(qsd->qsd_root)) {
292                 rc = PTR_ERR(qsd->qsd_root);
293                 CERROR("%s: failed to create quota slave root dir (%d)\n",
294                        svname, rc);
295                 GOTO(out, rc);
296         }
297
298         /* register procfs directory */
299         qsd->qsd_proc = lprocfs_register(QSD_DIR, osd_proc,
300                                          lprocfs_quota_qsd_vars, qsd);
301         if (IS_ERR(qsd->qsd_proc)) {
302                 rc = PTR_ERR(qsd->qsd_proc);
303                 CERROR("%s: fail to create quota slave proc entry (%d)\n",
304                        svname, rc);
305                 GOTO(out, rc);
306         }
307
308         /* initialize per-quota type data */
309         for (qtype = USRQUOTA; qtype < MAXQUOTAS; qtype++) {
310                 rc = qsd_qtype_init(env, qsd, qtype);
311                 if (rc)
312                         GOTO(out, rc);
313         }
314 out:
315         if (rc) {
316                 qsd_fini(env, qsd);
317                 return ERR_PTR(rc);
318         }
319         RETURN(qsd);
320 }
321 EXPORT_SYMBOL(qsd_init);