Whamcloud - gitweb
LU-8475 target: use slab allocation
[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         time64_t now = ktime_get_seconds();
117         struct ofd_mod_data *fmd, *tmp;
118
119         list_for_each_entry_safe(fmd, tmp, &fed->fed_mod_list, fmd_list) {
120                 if (fmd == keep)
121                         break;
122
123                 if (now < fmd->fmd_expire &&
124                     fed->fed_mod_count < ofd->ofd_fmd_max_num)
125                         break;
126
127                 list_del_init(&fmd->fmd_list);
128                 ofd_fmd_put_nolock(exp, fmd); /* list reference */
129         }
130 }
131
132 /**
133  * Expire FMD entries.
134  *
135  * This is a wrapper to call ofd_fmd_expire_nolock() with the required lock.
136  *
137  * \param[in] exp       OBD export
138  */
139 void ofd_fmd_expire(struct obd_export *exp)
140 {
141         struct filter_export_data *fed = &exp->exp_filter_data;
142
143         spin_lock(&fed->fed_lock);
144         ofd_fmd_expire_nolock(exp, NULL);
145         spin_unlock(&fed->fed_lock);
146 }
147
148 /**
149  * Find FMD by specified FID.
150  *
151  * Function finds FMD entry by FID in the filter_export_data::fed_fmd_list.
152  *
153  * Caller must hold filter_export_data::fed_lock and take FMD reference.
154  *
155  * \param[in] exp       OBD export
156  * \param[in] fid       FID of FMD to find
157  *
158  * \retval              struct ofd_mod_data found by FID
159  * \retval              NULL is FMD is not found
160  */
161 static struct ofd_mod_data *ofd_fmd_find_nolock(struct obd_export *exp,
162                                                 const struct lu_fid *fid)
163 {
164         struct filter_export_data *fed = &exp->exp_filter_data;
165         struct ofd_mod_data *found = NULL, *fmd;
166         struct ofd_device *ofd = ofd_exp(exp);
167         time64_t now = ktime_get_seconds();
168
169         assert_spin_locked(&fed->fed_lock);
170
171         list_for_each_entry_reverse(fmd, &fed->fed_mod_list, fmd_list) {
172                 if (lu_fid_eq(&fmd->fmd_fid, fid)) {
173                         found = fmd;
174                         list_del(&fmd->fmd_list);
175                         list_add_tail(&fmd->fmd_list, &fed->fed_mod_list);
176                         fmd->fmd_expire = now + ofd->ofd_fmd_max_age;
177                         break;
178                 }
179         }
180
181         ofd_fmd_expire_nolock(exp, found);
182
183         return found;
184 }
185
186 /**
187  * Find FMD by specified FID with locking.
188  *
189  * Wrapper to the ofd_fmd_find_nolock() with correct locks.
190  *
191  * \param[in] exp       OBD export
192  * \param[in] fid       FID of FMD to find
193  *
194  * \retval              struct ofd_mod_data found by FID
195  * \retval              NULL indicates FMD is not found
196  */
197 struct ofd_mod_data *ofd_fmd_find(struct obd_export *exp,
198                                   const struct lu_fid *fid)
199 {
200         struct filter_export_data       *fed = &exp->exp_filter_data;
201         struct ofd_mod_data             *fmd;
202
203         spin_lock(&fed->fed_lock);
204         fmd = ofd_fmd_find_nolock(exp, fid);
205         if (fmd)
206                 fmd->fmd_refcount++;    /* caller reference */
207         spin_unlock(&fed->fed_lock);
208
209         return fmd;
210 }
211
212 /**
213  * Find FMD by FID or create a new one if none is found.
214  *
215  * It is possible for this function to return NULL under memory pressure,
216  * or if the passed FID is zero (which will only cause old entries to expire).
217  * Currently this is not fatal because any FMD state is transient and
218  * may also be freed when it gets sufficiently old.
219  *
220  * \param[in] exp       OBD export
221  * \param[in] fid       FID of FMD to find
222  *
223  * \retval              struct ofd_mod_data found by FID
224  * \retval              NULL indicates FMD is not found
225  */
226 struct ofd_mod_data *ofd_fmd_get(struct obd_export *exp, const struct lu_fid *fid)
227 {
228         struct filter_export_data       *fed = &exp->exp_filter_data;
229         struct ofd_device               *ofd = ofd_exp(exp);
230         struct ofd_mod_data             *found = NULL, *fmd_new = NULL;
231         time64_t now = ktime_get_seconds();
232
233         OBD_SLAB_ALLOC_PTR(fmd_new, ll_fmd_cachep);
234
235         spin_lock(&fed->fed_lock);
236         found = ofd_fmd_find_nolock(exp, fid);
237         if (fmd_new) {
238                 if (found == NULL) {
239                         list_add_tail(&fmd_new->fmd_list,
240                                       &fed->fed_mod_list);
241                         fmd_new->fmd_fid = *fid;
242                         fmd_new->fmd_refcount++;   /* list reference */
243                         found = fmd_new;
244                         fed->fed_mod_count++;
245                 } else {
246                         OBD_SLAB_FREE_PTR(fmd_new, ll_fmd_cachep);
247                 }
248         }
249         if (found) {
250                 found->fmd_refcount++; /* caller reference */
251                 found->fmd_expire = now + ofd->ofd_fmd_max_age;
252         }
253
254         spin_unlock(&fed->fed_lock);
255
256         return found;
257 }
258
259 #ifdef DO_FMD_DROP
260 /**
261  * Drop FMD list reference so it will disappear when last reference is dropped
262  * to zero.
263  *
264  * This function is called from ofd_destroy() and may only affect
265  * the one client that is doing the unlink and at worst we have an stale entry
266  * referencing an object that should never be used again.
267  *
268  * NB: this function is used only if DO_FMD_DROP is defined. It is not
269  * currently defined, so FMD drop doesn't happen and FMD are dropped only
270  * when expired.
271  *
272  * \param[in] exp       OBD export
273  * \param[in] fid       FID of FMD to drop
274  */
275 void ofd_fmd_drop(struct obd_export *exp, const struct lu_fid *fid)
276 {
277         struct filter_export_data       *fed = &exp->exp_filter_data;
278         struct ofd_mod_data             *found = NULL;
279
280         spin_lock(&fed->fed_lock);
281         found = ofd_fmd_find_nolock(exp, fid);
282         if (found) {
283                 list_del_init(&found->fmd_list);
284                 ofd_fmd_put_nolock(exp, found);
285         }
286         spin_unlock(&fed->fed_lock);
287 }
288 #endif
289
290 /**
291  * Remove all entries from FMD list.
292  *
293  * Cleanup function to free all FMD enries on the given export.
294  *
295  * \param[in] exp       OBD export
296  */
297 void ofd_fmd_cleanup(struct obd_export *exp)
298 {
299         struct filter_export_data       *fed = &exp->exp_filter_data;
300         struct ofd_mod_data             *fmd = NULL, *tmp;
301
302         spin_lock(&fed->fed_lock);
303         list_for_each_entry_safe(fmd, tmp, &fed->fed_mod_list, fmd_list) {
304                 list_del_init(&fmd->fmd_list);
305                 if (fmd->fmd_refcount > 1) {
306                         CDEBUG(D_INFO, "fmd %p still referenced (refcount = %d)\n",
307                                fmd, fmd->fmd_refcount);
308                 }
309                 ofd_fmd_put_nolock(exp, fmd);
310         }
311         spin_unlock(&fed->fed_lock);
312 }
313
314 /**
315  * Initialize FMD subsystem.
316  *
317  * This function is called upon OFD setup and initialize memory to be used
318  * by FMD entries.
319  */
320 int ofd_fmd_init(void)
321 {
322         ll_fmd_cachep = kmem_cache_create("ll_fmd_cache",
323                                           sizeof(struct ofd_mod_data),
324                                           0, 0, NULL);
325         if (!ll_fmd_cachep)
326                 return -ENOMEM;
327         else
328                 return 0;
329 }
330
331 /**
332  * Stop FMD subsystem.
333  *
334  * This function is called upon OFD cleanup and destroy memory used
335  * by FMD entries.
336  */
337 void ofd_fmd_exit(void)
338 {
339         if (ll_fmd_cachep) {
340                 kmem_cache_destroy(ll_fmd_cachep);
341                 ll_fmd_cachep = NULL;
342         }
343 }