Whamcloud - gitweb
LU-10496 tgt: move FMD handling from OFD to target
[fs/lustre-release.git] / lustre / target / tgt_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, 2014, Intel Corporation.
27  *
28  * Copyright (c) 2019, DDN Storage Corporation.
29  */
30 /*
31  * This file is part of Lustre, http://www.lustre.org/
32  *
33  * lustre/target/tgt_fmd.c
34  *
35  * This file provides functions to handle Filter Modification Data (FMD).
36  * The FMD is responsible for file attributes to be applied in
37  * Transaction ID (XID) order, so older requests can't re-write newer
38  * attributes.
39  *
40  * FMD is organized as per-client list and identified by FID of object. Each
41  * FMD stores FID of object and the highest received XID of modification
42  * request for this object.
43  *
44  * FMD can expire if there are no updates for a long time to keep the list
45  * reasonably small.
46  *
47  * Author: Andreas Dilger <adilger@whamcloud.com>
48  * Author: Mike Pershin <mpershin@whamcloud.com>
49  */
50
51 #define DEBUG_SUBSYSTEM S_CLASS
52
53 #include <obd.h>
54 #include <obd_class.h>
55
56 #include "tgt_internal.h"
57
58 /**
59  * Drop FMD reference and free it if reference drops to zero.
60  *
61  * Must be called with ted_fmd_lock held.
62  *
63  * \param[in] exp       OBD export
64  * \param[in] fmd       FMD to put
65  */
66 static inline void tgt_fmd_put_nolock(struct obd_export *exp,
67                                       struct tgt_fmd_data *fmd)
68 {
69         struct tg_export_data *ted = &exp->exp_target_data;
70
71         assert_spin_locked(&ted->ted_fmd_lock);
72         if (--fmd->fmd_refcount == 0) {
73                 ted->ted_fmd_count--;
74                 list_del(&fmd->fmd_list);
75                 OBD_SLAB_FREE_PTR(fmd, tgt_fmd_kmem);
76         }
77 }
78
79 /**
80  * Wrapper to drop FMD reference with ted_fmd_lock held.
81  *
82  * \param[in] exp       OBD export
83  * \param[in] fmd       FMD to put
84  */
85 void tgt_fmd_put(struct obd_export *exp, struct tgt_fmd_data *fmd)
86 {
87         struct tg_export_data *ted = &exp->exp_target_data;
88
89         spin_lock(&ted->ted_fmd_lock);
90         tgt_fmd_put_nolock(exp, fmd); /* caller reference */
91         spin_unlock(&ted->ted_fmd_lock);
92 }
93
94 /**
95  * Expire FMD entries.
96  *
97  * Expire entries from the FMD list if there are too many
98  * of them or they are too old.
99  *
100  * This function must be called with ted_fmd_lock held.
101  *
102  * The \a keep FMD is not to be expired in any case. This parameter is used
103  * by ofd_fmd_find_nolock() to prohibit a FMD that was just found from
104  * expiring.
105  *
106  * \param[in] exp       OBD export
107  * \param[in] keep      FMD to keep always
108  */
109 static void tgt_fmd_expire_nolock(struct obd_export *exp,
110                                   struct tgt_fmd_data *keep)
111 {
112         struct tg_export_data *ted = &exp->exp_target_data;
113         struct lu_target *lut = exp->exp_obd->u.obt.obt_lut;
114         time64_t now = ktime_get_seconds();
115         struct tgt_fmd_data *fmd, *tmp;
116
117         list_for_each_entry_safe(fmd, tmp, &ted->ted_fmd_list, fmd_list) {
118                 if (fmd == keep)
119                         break;
120
121                 if (now < fmd->fmd_expire &&
122                     ted->ted_fmd_count < lut->lut_fmd_max_num)
123                         break;
124
125                 list_del_init(&fmd->fmd_list);
126                 tgt_fmd_put_nolock(exp, fmd); /* list reference */
127         }
128 }
129
130 /**
131  * Expire FMD entries.
132  *
133  * This is a wrapper to call ofd_fmd_expire_nolock() with the required lock.
134  *
135  * \param[in] exp       OBD export
136  */
137 void tgt_fmd_expire(struct obd_export *exp)
138 {
139         struct tg_export_data *ted = &exp->exp_target_data;
140
141         spin_lock(&ted->ted_fmd_lock);
142         tgt_fmd_expire_nolock(exp, NULL);
143         spin_unlock(&ted->ted_fmd_lock);
144 }
145
146 /**
147  * Find FMD by specified FID.
148  *
149  * Function finds FMD entry by FID in the tg_export_data::ted_fmd_list.
150  *
151  * Caller must hold tg_export_data::ted_fmd_lock and take FMD reference.
152  *
153  * \param[in] exp       OBD export
154  * \param[in] fid       FID of FMD to find
155  *
156  * \retval              struct tgt_fmd_data found by FID
157  * \retval              NULL is FMD is not found
158  */
159 static struct tgt_fmd_data *tgt_fmd_find_nolock(struct obd_export *exp,
160                                                 const struct lu_fid *fid)
161 {
162         struct tg_export_data *ted = &exp->exp_target_data;
163         struct tgt_fmd_data *found = NULL, *fmd;
164         struct lu_target *lut = exp->exp_obd->u.obt.obt_lut;
165         time64_t now = ktime_get_seconds();
166
167         assert_spin_locked(&ted->ted_fmd_lock);
168
169         list_for_each_entry_reverse(fmd, &ted->ted_fmd_list, fmd_list) {
170                 if (lu_fid_eq(&fmd->fmd_fid, fid)) {
171                         found = fmd;
172                         list_move_tail(&fmd->fmd_list, &ted->ted_fmd_list);
173                         fmd->fmd_expire = now + lut->lut_fmd_max_age;
174                         break;
175                 }
176         }
177
178         tgt_fmd_expire_nolock(exp, found);
179
180         return found;
181 }
182
183 /**
184  * Find FMD by specified FID with locking.
185  *
186  * Wrapper to the ofd_fmd_find_nolock() with correct locks.
187  *
188  * \param[in] exp       OBD export
189  * \param[in] fid       FID of FMD to find
190  *
191  * \retval              struct tgt_fmd_data found by FID
192  * \retval              NULL indicates FMD is not found
193  */
194 struct tgt_fmd_data *tgt_fmd_find(struct obd_export *exp,
195                                   const struct lu_fid *fid)
196 {
197         struct tg_export_data *ted = &exp->exp_target_data;
198         struct tgt_fmd_data *fmd;
199
200         spin_lock(&ted->ted_fmd_lock);
201         fmd = tgt_fmd_find_nolock(exp, fid);
202         if (fmd)
203                 fmd->fmd_refcount++;    /* caller reference */
204         spin_unlock(&ted->ted_fmd_lock);
205
206         return fmd;
207 }
208
209 /**
210  * Find FMD by FID or create a new one if none is found.
211  *
212  * It is possible for this function to return NULL under memory pressure,
213  * or if the passed FID is zero (which will only cause old entries to expire).
214  * Currently this is not fatal because any FMD state is transient and
215  * may also be freed when it gets sufficiently old.
216  *
217  * \param[in] exp       OBD export
218  * \param[in] fid       FID of FMD to find
219  *
220  * \retval              struct tgt_fmd_data found by FID
221  * \retval              NULL indicates FMD is not found
222  */
223 struct tgt_fmd_data *tgt_fmd_get(struct obd_export *exp,
224                                  const struct lu_fid *fid)
225 {
226         struct tg_export_data *ted = &exp->exp_target_data;
227         struct tgt_fmd_data *found = NULL, *fmd_new = NULL;
228
229         OBD_SLAB_ALLOC_PTR(fmd_new, tgt_fmd_kmem);
230
231         spin_lock(&ted->ted_fmd_lock);
232         found = tgt_fmd_find_nolock(exp, fid);
233         if (fmd_new) {
234                 if (!found) {
235                         list_add_tail(&fmd_new->fmd_list, &ted->ted_fmd_list);
236                         fmd_new->fmd_fid = *fid;
237                         fmd_new->fmd_refcount++;   /* list reference */
238                         found = fmd_new;
239                         ted->ted_fmd_count++;
240                 } else {
241                         OBD_SLAB_FREE_PTR(fmd_new, tgt_fmd_kmem);
242                 }
243         }
244         if (found) {
245                 found->fmd_refcount++; /* caller reference */
246                 found->fmd_expire = ktime_get_seconds() +
247                         class_exp2tgt(exp)->lut_fmd_max_age;
248         } else {
249                 LCONSOLE_WARN("%s: cannot allocate FMD for "DFID
250                               ", timestamps may be out of sync\n",
251                               exp->exp_obd->obd_name, PFID(fid));
252         }
253         spin_unlock(&ted->ted_fmd_lock);
254
255         return found;
256 }
257
258 #ifdef DO_FMD_DROP
259 /**
260  * Drop FMD list reference so it will disappear when last reference is dropped
261  * to zero.
262  *
263  * This function is called from ofd_destroy() and may only affect
264  * the one client that is doing the unlink and at worst we have an stale entry
265  * referencing an object that should never be used again.
266  *
267  * NB: this function is used only if DO_FMD_DROP is defined. It is not
268  * currently defined, so FMD drop doesn't happen and FMD are dropped only
269  * when expired.
270  *
271  * \param[in] exp       OBD export
272  * \param[in] fid       FID of FMD to drop
273  */
274 void tgt_fmd_drop(struct obd_export *exp, const struct lu_fid *fid)
275 {
276         struct tg_export_data *ted = &exp->exp_target_data;
277         struct tgt_fmd_data *fmd = NULL;
278
279         spin_lock(&ted->ted_fmd_lock);
280         fmd = tgt_fmd_find_nolock(exp, fid);
281         if (fmd) {
282                 list_del_init(&fmd->fmd_list);
283                 tgt_fmd_put_nolock(exp, fmd);
284         }
285         spin_unlock(&ted->ted_fmd_lock);
286 }
287 EXPORT_SYMBOL(tgt_fmd_drop);
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 tgt_fmd_cleanup(struct obd_export *exp)
298 {
299         struct tg_export_data *ted = &exp->exp_target_data;
300         struct tgt_fmd_data *fmd = NULL, *tmp;
301
302         spin_lock(&ted->ted_fmd_lock);
303         list_for_each_entry_safe(fmd, tmp, &ted->ted_fmd_list, fmd_list) {
304                 list_del_init(&fmd->fmd_list);
305                 if (fmd->fmd_refcount > 1) {
306                         CDEBUG(D_INFO,
307                                "fmd %p still referenced (refcount = %d)\n",
308                                fmd, fmd->fmd_refcount);
309                 }
310                 tgt_fmd_put_nolock(exp, fmd);
311         }
312         spin_unlock(&ted->ted_fmd_lock);
313         LASSERT(list_empty(&exp->exp_target_data.ted_fmd_list));
314 }
315
316 /**
317  * Update FMD with the latest request XID.
318  *
319  * Save a new setattr/punch XID in FMD if exists.
320  *
321  * \param[in] exp       OBD export
322  * \param[in] fid       FID of FMD to find
323  * \param[in] xid       request XID
324  */
325 void tgt_fmd_update(struct obd_export *exp, const struct lu_fid *fid, __u64 xid)
326 {
327         struct tgt_fmd_data *fmd;
328
329         fmd = tgt_fmd_get(exp, fid);
330         if (fmd) {
331                 if (fmd->fmd_mactime_xid < xid)
332                         fmd->fmd_mactime_xid = xid;
333                 tgt_fmd_put(exp, fmd);
334         }
335 }
336 EXPORT_SYMBOL(tgt_fmd_update);
337
338 /**
339  * Chech that time can be updated by the request with given XID.
340  *
341  * Check FMD XID if exists to be less than supplied XID
342  *
343  * \param[in] exp       OBD export
344  * \param[in] fid       FID of FMD to find
345  * \param[in] xid       request XID
346  *
347  * \retval true if FMD has no greater XID, so time attr can be updated
348  */
349 bool tgt_fmd_check(struct obd_export *exp, const struct lu_fid *fid, __u64 xid)
350 {
351         struct tgt_fmd_data *fmd;
352         bool can_update = true;
353
354         fmd = tgt_fmd_find(exp, fid);
355         if (fmd) {
356                 can_update = fmd->fmd_mactime_xid < xid;
357                 tgt_fmd_put(exp, fmd);
358         }
359
360         return can_update;
361 }
362 EXPORT_SYMBOL(tgt_fmd_check);
363