Whamcloud - gitweb
LU-1866 osd: ancillary work for initial OI scrub
[fs/lustre-release.git] / lustre / ofd / ofd_fmd.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, see
18  * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
19  *
20  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
21  * CA 95054 USA or visit www.sun.com if you need additional information or
22  * have any questions.
23  *
24  * GPL HEADER END
25  */
26 /*
27  * Copyright  2008 Sun Microsystems, Inc. All rights reserved
28  * Use is subject to license terms.
29  *
30  * Copyright (c) 2012, Intel Corporation.
31  */
32 /*
33  * This file is part of Lustre, http://www.lustre.org/
34  * Lustre is a trademark of Sun Microsystems, Inc.
35  *
36  *  lustre/ofd/filter_fmd.c
37  */
38
39 #define DEBUG_SUBSYSTEM S_FILTER
40
41 #include "ofd_internal.h"
42
43 static cfs_mem_cache_t *ll_fmd_cachep;
44
45 /* drop fmd reference, free it if last ref. must be called with fed_lock held.*/
46 static inline void ofd_fmd_put_nolock(struct obd_export *exp,
47                                       struct ofd_mod_data *fmd)
48 {
49         struct filter_export_data *fed = &exp->exp_filter_data;
50
51         LASSERT_SPIN_LOCKED(&fed->fed_lock);
52         if (--fmd->fmd_refcount == 0) {
53                 /* XXX when we have persistent reservations and the handle
54                  * is stored herein we need to drop it here. */
55                 fed->fed_mod_count--;
56                 cfs_list_del(&fmd->fmd_list);
57                 OBD_SLAB_FREE(fmd, ll_fmd_cachep, sizeof(*fmd));
58         }
59 }
60
61 /* drop fmd reference, free it if last ref */
62 void ofd_fmd_put(struct obd_export *exp, struct ofd_mod_data *fmd)
63 {
64         struct filter_export_data *fed = &exp->exp_filter_data;
65
66         if (fmd == NULL)
67                 return;
68
69         spin_lock(&fed->fed_lock);
70         ofd_fmd_put_nolock(exp, fmd); /* caller reference */
71         spin_unlock(&fed->fed_lock);
72 }
73
74 /* expire entries from the end of the list if there are too many
75  * or they are too old */
76 static void ofd_fmd_expire_nolock(struct obd_export *exp,
77                                   struct ofd_mod_data *keep)
78 {
79         struct filter_export_data       *fed = &exp->exp_filter_data;
80         struct ofd_device               *ofd = ofd_exp(exp);
81         struct ofd_mod_data             *fmd, *tmp;
82
83         cfs_time_t now = cfs_time_current();
84
85         cfs_list_for_each_entry_safe(fmd, tmp, &fed->fed_mod_list, fmd_list) {
86                 if (fmd == keep)
87                         break;
88
89                 if (cfs_time_before(now, fmd->fmd_expire) &&
90                     fed->fed_mod_count < ofd->ofd_fmd_max_num)
91                         break;
92
93                 cfs_list_del_init(&fmd->fmd_list);
94                 ofd_fmd_put_nolock(exp, fmd); /* list reference */
95         }
96 }
97
98 void ofd_fmd_expire(struct obd_export *exp)
99 {
100         struct filter_export_data *fed = &exp->exp_filter_data;
101
102         spin_lock(&fed->fed_lock);
103         ofd_fmd_expire_nolock(exp, NULL);
104         spin_unlock(&fed->fed_lock);
105 }
106
107 /* find specified fid in fed_fmd_list.
108  * caller must hold fed_lock and take fmd reference itself */
109 static struct ofd_mod_data *ofd_fmd_find_nolock(struct obd_export *exp,
110                                                 const struct lu_fid *fid)
111 {
112         struct filter_export_data       *fed = &exp->exp_filter_data;
113         struct ofd_mod_data             *found = NULL, *fmd;
114         struct ofd_device               *ofd = ofd_exp(exp);
115
116         cfs_time_t now = cfs_time_current();
117
118         LASSERT_SPIN_LOCKED(&fed->fed_lock);
119
120         cfs_list_for_each_entry_reverse(fmd, &fed->fed_mod_list, fmd_list) {
121                 if (lu_fid_eq(&fmd->fmd_fid, fid)) {
122                         found = fmd;
123                         cfs_list_del(&fmd->fmd_list);
124                         cfs_list_add_tail(&fmd->fmd_list, &fed->fed_mod_list);
125                         fmd->fmd_expire = cfs_time_add(now, ofd->ofd_fmd_max_age);
126                         break;
127                 }
128         }
129
130         ofd_fmd_expire_nolock(exp, found);
131
132         return found;
133 }
134
135 /* Find fmd based on fid or return NULL if not found. */
136 struct ofd_mod_data *ofd_fmd_find(struct obd_export *exp,
137                                   struct lu_fid *fid)
138 {
139         struct filter_export_data       *fed = &exp->exp_filter_data;
140         struct ofd_mod_data             *fmd;
141
142         spin_lock(&fed->fed_lock);
143         fmd = ofd_fmd_find_nolock(exp, fid);
144         if (fmd)
145                 fmd->fmd_refcount++;    /* caller reference */
146         spin_unlock(&fed->fed_lock);
147
148         return fmd;
149 }
150
151 /* Find fmd based on FID, or create a new one if none is found.
152  * It is possible for this function to return NULL under memory pressure,
153  * or if fid = 0 is passed (which will only cause old entries to expire).
154  * Currently this is not fatal because any fmd state is transient and
155  * may also be freed when it gets sufficiently old. */
156 struct ofd_mod_data *ofd_fmd_get(struct obd_export *exp, struct lu_fid *fid)
157 {
158         struct filter_export_data       *fed = &exp->exp_filter_data;
159         struct ofd_device               *ofd = ofd_exp(exp);
160         struct ofd_mod_data             *found = NULL, *fmd_new = NULL;
161
162         cfs_time_t now = cfs_time_current();
163
164         OBD_SLAB_ALLOC_PTR(fmd_new, ll_fmd_cachep);
165
166         spin_lock(&fed->fed_lock);
167         found = ofd_fmd_find_nolock(exp, fid);
168         if (fmd_new) {
169                 if (found == NULL) {
170                         cfs_list_add_tail(&fmd_new->fmd_list,
171                                           &fed->fed_mod_list);
172                         fmd_new->fmd_fid = *fid;
173                         fmd_new->fmd_refcount++;   /* list reference */
174                         found = fmd_new;
175                         fed->fed_mod_count++;
176                 } else {
177                         OBD_SLAB_FREE_PTR(fmd_new, ll_fmd_cachep);
178                 }
179         }
180         if (found) {
181                 found->fmd_refcount++; /* caller reference */
182                 found->fmd_expire = cfs_time_add(now, ofd->ofd_fmd_max_age);
183         }
184
185         spin_unlock(&fed->fed_lock);
186
187         return found;
188 }
189
190 #ifdef DO_FMD_DROP
191 /* drop fmd list reference so it will disappear when last reference is put.
192  * This isn't so critical because it would in fact only affect the one client
193  * that is doing the unlink and at worst we have an stale entry referencing
194  * an object that should never be used again. */
195 void ofd_fmd_drop(struct obd_export *exp, struct lu_fid *fid)
196 {
197         struct filter_export_data       *fed = &exp->exp_filter_data;
198         struct ofd_mod_data             *found = NULL;
199
200         spin_lock(&fed->fed_lock);
201         found = ofd_fmd_find_nolock(exp, fid);
202         if (found) {
203                 cfs_list_del_init(&found->fmd_list);
204                 ofd_fmd_put_nolock(exp, found);
205         }
206         spin_unlock(&fed->fed_lock);
207 }
208 #endif
209
210 /* remove all entries from fmd list */
211 void ofd_fmd_cleanup(struct obd_export *exp)
212 {
213         struct filter_export_data       *fed = &exp->exp_filter_data;
214         struct ofd_mod_data             *fmd = NULL, *tmp;
215
216         spin_lock(&fed->fed_lock);
217         cfs_list_for_each_entry_safe(fmd, tmp, &fed->fed_mod_list, fmd_list) {
218                 cfs_list_del_init(&fmd->fmd_list);
219                 if (fmd->fmd_refcount > 1) {
220                         CDEBUG(D_INFO, "fmd %p still referenced (refcount = %d)\n",
221                                fmd, fmd->fmd_refcount);
222                 }
223                 ofd_fmd_put_nolock(exp, fmd);
224         }
225         spin_unlock(&fed->fed_lock);
226 }
227
228 int ofd_fmd_init(void)
229 {
230         ll_fmd_cachep = cfs_mem_cache_create("ll_fmd_cache",
231                                              sizeof(struct ofd_mod_data),
232                                              0, 0);
233         if (!ll_fmd_cachep)
234                 return -ENOMEM;
235         else
236                 return 0;
237 }
238
239 void ofd_fmd_exit(void)
240 {
241         if (ll_fmd_cachep) {
242                 int rc = cfs_mem_cache_destroy(ll_fmd_cachep);
243
244                 LASSERTF(rc == 0, "Cannot destroy ll_fmd_cachep: rc %d\n", rc);
245                 ll_fmd_cachep = NULL;
246         }
247 }