Whamcloud - gitweb
LU-8726 osd-ldiskfs: bypass read for benchmarking
[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.gnu.org/licenses/gpl-2.0.html
19  *
20  * GPL HEADER END
21  */
22 /*
23  * Copyright  2008 Sun Microsystems, Inc. All rights reserved
24  * Use is subject to license terms.
25  *
26  * Copyright (c) 2012, 2015, Intel Corporation.
27  */
28 /*
29  * This file is part of Lustre, http://www.lustre.org/
30  * Lustre is a trademark of Sun Microsystems, Inc.
31  *
32  * lustre/ofd/ofd_fmd.c
33  *
34  * This file provides functions to handle Filter Modification Data (FMD).
35  * The FMD is responsible for file attributes to be applied in
36  * Transaction ID (XID) order, so older requests can't re-write newer
37  * attributes.
38  *
39  * FMD is organized as per-client list and identified by FID of object. Each
40  * FMD stores FID of object and the highest received XID of modification
41  * request for this object.
42  *
43  * FMD can expire if there are no updates for a long time to keep the list
44  * reasonably small.
45  *
46  * Author: Andreas Dilger <andreas.dilger@intel.com>
47  */
48
49 #define DEBUG_SUBSYSTEM S_FILTER
50
51 #include "ofd_internal.h"
52
53 static struct kmem_cache *ll_fmd_cachep;
54
55 /**
56  * Drop FMD reference and free it if reference drops to zero.
57  *
58  * Must be called with fed_lock held.
59  *
60  * \param[in] exp       OBD export
61  * \param[in] fmd       FMD to put
62  */
63 static inline void ofd_fmd_put_nolock(struct obd_export *exp,
64                                       struct ofd_mod_data *fmd)
65 {
66         struct filter_export_data *fed = &exp->exp_filter_data;
67
68         assert_spin_locked(&fed->fed_lock);
69         if (--fmd->fmd_refcount == 0) {
70                 /* XXX when we have persistent reservations and the handle
71                  * is stored herein we need to drop it here. */
72                 fed->fed_mod_count--;
73                 list_del(&fmd->fmd_list);
74                 OBD_SLAB_FREE(fmd, ll_fmd_cachep, sizeof(*fmd));
75         }
76 }
77
78 /**
79  * Wrapper to drop FMD reference with fed_lock held.
80  *
81  * \param[in] exp       OBD export
82  * \param[in] fmd       FMD to put
83  */
84 void ofd_fmd_put(struct obd_export *exp, struct ofd_mod_data *fmd)
85 {
86         struct filter_export_data *fed = &exp->exp_filter_data;
87
88         if (fmd == NULL)
89                 return;
90
91         spin_lock(&fed->fed_lock);
92         ofd_fmd_put_nolock(exp, fmd); /* caller reference */
93         spin_unlock(&fed->fed_lock);
94 }
95
96 /**
97  * Expire FMD entries.
98  *
99  * Expire entries from the FMD list if there are too many
100  * of them or they are too old.
101  *
102  * This function must be called with fed_lock held.
103  *
104  * The \a keep FMD is not to be expired in any case. This parameter is used
105  * by ofd_fmd_find_nolock() to prohibit a FMD that was just found from
106  * expiring.
107  *
108  * \param[in] exp       OBD export
109  * \param[in] keep      FMD to keep always
110  */
111 static void ofd_fmd_expire_nolock(struct obd_export *exp,
112                                   struct ofd_mod_data *keep)
113 {
114         struct filter_export_data       *fed = &exp->exp_filter_data;
115         struct ofd_device               *ofd = ofd_exp(exp);
116         struct ofd_mod_data             *fmd, *tmp;
117
118         cfs_time_t now = cfs_time_current();
119
120         list_for_each_entry_safe(fmd, tmp, &fed->fed_mod_list, fmd_list) {
121                 if (fmd == keep)
122                         break;
123
124                 if (cfs_time_before(now, fmd->fmd_expire) &&
125                     fed->fed_mod_count < ofd->ofd_fmd_max_num)
126                         break;
127
128                 list_del_init(&fmd->fmd_list);
129                 ofd_fmd_put_nolock(exp, fmd); /* list reference */
130         }
131 }
132
133 /**
134  * Expire FMD entries.
135  *
136  * This is a wrapper to call ofd_fmd_expire_nolock() with the required lock.
137  *
138  * \param[in] exp       OBD export
139  */
140 void ofd_fmd_expire(struct obd_export *exp)
141 {
142         struct filter_export_data *fed = &exp->exp_filter_data;
143
144         spin_lock(&fed->fed_lock);
145         ofd_fmd_expire_nolock(exp, NULL);
146         spin_unlock(&fed->fed_lock);
147 }
148
149 /**
150  * Find FMD by specified FID.
151  *
152  * Function finds FMD entry by FID in the filter_export_data::fed_fmd_list.
153  *
154  * Caller must hold filter_export_data::fed_lock and take FMD reference.
155  *
156  * \param[in] exp       OBD export
157  * \param[in] fid       FID of FMD to find
158  *
159  * \retval              struct ofd_mod_data found by FID
160  * \retval              NULL is FMD is not found
161  */
162 static struct ofd_mod_data *ofd_fmd_find_nolock(struct obd_export *exp,
163                                                 const struct lu_fid *fid)
164 {
165         struct filter_export_data       *fed = &exp->exp_filter_data;
166         struct ofd_mod_data             *found = NULL, *fmd;
167         struct ofd_device               *ofd = ofd_exp(exp);
168
169         cfs_time_t now = cfs_time_current();
170
171         assert_spin_locked(&fed->fed_lock);
172
173         list_for_each_entry_reverse(fmd, &fed->fed_mod_list, fmd_list) {
174                 if (lu_fid_eq(&fmd->fmd_fid, fid)) {
175                         found = fmd;
176                         list_del(&fmd->fmd_list);
177                         list_add_tail(&fmd->fmd_list, &fed->fed_mod_list);
178                         fmd->fmd_expire = cfs_time_add(now, ofd->ofd_fmd_max_age);
179                         break;
180                 }
181         }
182
183         ofd_fmd_expire_nolock(exp, found);
184
185         return found;
186 }
187
188 /**
189  * Find FMD by specified FID with locking.
190  *
191  * Wrapper to the ofd_fmd_find_nolock() with correct locks.
192  *
193  * \param[in] exp       OBD export
194  * \param[in] fid       FID of FMD to find
195  *
196  * \retval              struct ofd_mod_data found by FID
197  * \retval              NULL indicates FMD is not found
198  */
199 struct ofd_mod_data *ofd_fmd_find(struct obd_export *exp,
200                                   const struct lu_fid *fid)
201 {
202         struct filter_export_data       *fed = &exp->exp_filter_data;
203         struct ofd_mod_data             *fmd;
204
205         spin_lock(&fed->fed_lock);
206         fmd = ofd_fmd_find_nolock(exp, fid);
207         if (fmd)
208                 fmd->fmd_refcount++;    /* caller reference */
209         spin_unlock(&fed->fed_lock);
210
211         return fmd;
212 }
213
214 /**
215  * Find FMD by FID or create a new one if none is found.
216  *
217  * It is possible for this function to return NULL under memory pressure,
218  * or if the passed FID is zero (which will only cause old entries to expire).
219  * Currently this is not fatal because any FMD state is transient and
220  * may also be freed when it gets sufficiently old.
221  *
222  * \param[in] exp       OBD export
223  * \param[in] fid       FID of FMD to find
224  *
225  * \retval              struct ofd_mod_data found by FID
226  * \retval              NULL indicates FMD is not found
227  */
228 struct ofd_mod_data *ofd_fmd_get(struct obd_export *exp, const struct lu_fid *fid)
229 {
230         struct filter_export_data       *fed = &exp->exp_filter_data;
231         struct ofd_device               *ofd = ofd_exp(exp);
232         struct ofd_mod_data             *found = NULL, *fmd_new = NULL;
233
234         cfs_time_t now = cfs_time_current();
235
236         OBD_SLAB_ALLOC_PTR(fmd_new, ll_fmd_cachep);
237
238         spin_lock(&fed->fed_lock);
239         found = ofd_fmd_find_nolock(exp, fid);
240         if (fmd_new) {
241                 if (found == NULL) {
242                         list_add_tail(&fmd_new->fmd_list,
243                                       &fed->fed_mod_list);
244                         fmd_new->fmd_fid = *fid;
245                         fmd_new->fmd_refcount++;   /* list reference */
246                         found = fmd_new;
247                         fed->fed_mod_count++;
248                 } else {
249                         OBD_SLAB_FREE_PTR(fmd_new, ll_fmd_cachep);
250                 }
251         }
252         if (found) {
253                 found->fmd_refcount++; /* caller reference */
254                 found->fmd_expire = cfs_time_add(now, ofd->ofd_fmd_max_age);
255         }
256
257         spin_unlock(&fed->fed_lock);
258
259         return found;
260 }
261
262 #ifdef DO_FMD_DROP
263 /**
264  * Drop FMD list reference so it will disappear when last reference is dropped
265  * to zero.
266  *
267  * This function is called from ofd_object_destroy() and may only affect
268  * the one client that is doing the unlink and at worst we have an stale entry
269  * referencing an object that should never be used again.
270  *
271  * NB: this function is used only if DO_FMD_DROP is defined. It is not
272  * currently defined, so FMD drop doesn't happen and FMD are dropped only
273  * when expired.
274  *
275  * \param[in] exp       OBD export
276  * \param[in] fid       FID of FMD to drop
277  */
278 void ofd_fmd_drop(struct obd_export *exp, const struct lu_fid *fid)
279 {
280         struct filter_export_data       *fed = &exp->exp_filter_data;
281         struct ofd_mod_data             *found = NULL;
282
283         spin_lock(&fed->fed_lock);
284         found = ofd_fmd_find_nolock(exp, fid);
285         if (found) {
286                 list_del_init(&found->fmd_list);
287                 ofd_fmd_put_nolock(exp, found);
288         }
289         spin_unlock(&fed->fed_lock);
290 }
291 #endif
292
293 /**
294  * Remove all entries from FMD list.
295  *
296  * Cleanup function to free all FMD enries on the given export.
297  *
298  * \param[in] exp       OBD export
299  */
300 void ofd_fmd_cleanup(struct obd_export *exp)
301 {
302         struct filter_export_data       *fed = &exp->exp_filter_data;
303         struct ofd_mod_data             *fmd = NULL, *tmp;
304
305         spin_lock(&fed->fed_lock);
306         list_for_each_entry_safe(fmd, tmp, &fed->fed_mod_list, fmd_list) {
307                 list_del_init(&fmd->fmd_list);
308                 if (fmd->fmd_refcount > 1) {
309                         CDEBUG(D_INFO, "fmd %p still referenced (refcount = %d)\n",
310                                fmd, fmd->fmd_refcount);
311                 }
312                 ofd_fmd_put_nolock(exp, fmd);
313         }
314         spin_unlock(&fed->fed_lock);
315 }
316
317 /**
318  * Initialize FMD subsystem.
319  *
320  * This function is called upon OFD setup and initialize memory to be used
321  * by FMD entries.
322  */
323 int ofd_fmd_init(void)
324 {
325         ll_fmd_cachep = kmem_cache_create("ll_fmd_cache",
326                                           sizeof(struct ofd_mod_data),
327                                           0, 0, NULL);
328         if (!ll_fmd_cachep)
329                 return -ENOMEM;
330         else
331                 return 0;
332 }
333
334 /**
335  * Stop FMD subsystem.
336  *
337  * This function is called upon OFD cleanup and destroy memory used
338  * by FMD entries.
339  */
340 void ofd_fmd_exit(void)
341 {
342         if (ll_fmd_cachep) {
343                 kmem_cache_destroy(ll_fmd_cachep);
344                 ll_fmd_cachep = NULL;
345         }
346 }