4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
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.
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).
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
23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved
24 * Use is subject to license terms.
26 * Copyright (c) 2012, 2014, Intel Corporation.
28 * Copyright (c) 2019, DDN Storage Corporation.
31 * This file is part of Lustre, http://www.lustre.org/
33 * lustre/target/tgt_fmd.c
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
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.
44 * FMD can expire if there are no updates for a long time to keep the list
47 * Author: Andreas Dilger <adilger@whamcloud.com>
48 * Author: Mike Pershin <mpershin@whamcloud.com>
51 #define DEBUG_SUBSYSTEM S_CLASS
54 #include <obd_class.h>
56 #include "tgt_internal.h"
59 * Drop FMD reference and free it if reference drops to zero.
61 * Must be called with ted_fmd_lock held.
63 * \param[in] exp OBD export
64 * \param[in] fmd FMD to put
66 static inline void tgt_fmd_put_nolock(struct obd_export *exp,
67 struct tgt_fmd_data *fmd)
69 struct tg_export_data *ted = &exp->exp_target_data;
71 assert_spin_locked(&ted->ted_fmd_lock);
72 if (--fmd->fmd_refcount == 0) {
74 list_del(&fmd->fmd_list);
75 OBD_SLAB_FREE_PTR(fmd, tgt_fmd_kmem);
80 * Wrapper to drop FMD reference with ted_fmd_lock held.
82 * \param[in] exp OBD export
83 * \param[in] fmd FMD to put
85 void tgt_fmd_put(struct obd_export *exp, struct tgt_fmd_data *fmd)
87 struct tg_export_data *ted = &exp->exp_target_data;
89 spin_lock(&ted->ted_fmd_lock);
90 tgt_fmd_put_nolock(exp, fmd); /* caller reference */
91 spin_unlock(&ted->ted_fmd_lock);
97 * Expire entries from the FMD list if there are too many
98 * of them or they are too old.
100 * This function must be called with ted_fmd_lock held.
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
106 * \param[in] exp OBD export
107 * \param[in] keep FMD to keep always
109 static void tgt_fmd_expire_nolock(struct obd_export *exp,
110 struct tgt_fmd_data *keep)
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;
117 list_for_each_entry_safe(fmd, tmp, &ted->ted_fmd_list, fmd_list) {
121 if (now < fmd->fmd_expire &&
122 ted->ted_fmd_count < lut->lut_fmd_max_num)
125 list_del_init(&fmd->fmd_list);
126 tgt_fmd_put_nolock(exp, fmd); /* list reference */
131 * Expire FMD entries.
133 * This is a wrapper to call ofd_fmd_expire_nolock() with the required lock.
135 * \param[in] exp OBD export
137 void tgt_fmd_expire(struct obd_export *exp)
139 struct tg_export_data *ted = &exp->exp_target_data;
141 spin_lock(&ted->ted_fmd_lock);
142 tgt_fmd_expire_nolock(exp, NULL);
143 spin_unlock(&ted->ted_fmd_lock);
147 * Find FMD by specified FID.
149 * Function finds FMD entry by FID in the tg_export_data::ted_fmd_list.
151 * Caller must hold tg_export_data::ted_fmd_lock and take FMD reference.
153 * \param[in] exp OBD export
154 * \param[in] fid FID of FMD to find
156 * \retval struct tgt_fmd_data found by FID
157 * \retval NULL is FMD is not found
159 static struct tgt_fmd_data *tgt_fmd_find_nolock(struct obd_export *exp,
160 const struct lu_fid *fid)
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();
167 assert_spin_locked(&ted->ted_fmd_lock);
169 list_for_each_entry_reverse(fmd, &ted->ted_fmd_list, fmd_list) {
170 if (lu_fid_eq(&fmd->fmd_fid, fid)) {
172 list_move_tail(&fmd->fmd_list, &ted->ted_fmd_list);
173 fmd->fmd_expire = now + lut->lut_fmd_max_age;
178 tgt_fmd_expire_nolock(exp, found);
184 * Find FMD by specified FID with locking.
186 * Wrapper to the ofd_fmd_find_nolock() with correct locks.
188 * \param[in] exp OBD export
189 * \param[in] fid FID of FMD to find
191 * \retval struct tgt_fmd_data found by FID
192 * \retval NULL indicates FMD is not found
194 struct tgt_fmd_data *tgt_fmd_find(struct obd_export *exp,
195 const struct lu_fid *fid)
197 struct tg_export_data *ted = &exp->exp_target_data;
198 struct tgt_fmd_data *fmd;
200 spin_lock(&ted->ted_fmd_lock);
201 fmd = tgt_fmd_find_nolock(exp, fid);
203 fmd->fmd_refcount++; /* caller reference */
204 spin_unlock(&ted->ted_fmd_lock);
210 * Find FMD by FID or create a new one if none is found.
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.
217 * \param[in] exp OBD export
218 * \param[in] fid FID of FMD to find
220 * \retval struct tgt_fmd_data found by FID
221 * \retval NULL indicates FMD is not found
223 struct tgt_fmd_data *tgt_fmd_get(struct obd_export *exp,
224 const struct lu_fid *fid)
226 struct tg_export_data *ted = &exp->exp_target_data;
227 struct tgt_fmd_data *found = NULL, *fmd_new = NULL;
229 OBD_SLAB_ALLOC_PTR(fmd_new, tgt_fmd_kmem);
231 spin_lock(&ted->ted_fmd_lock);
232 found = tgt_fmd_find_nolock(exp, fid);
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 */
239 ted->ted_fmd_count++;
241 OBD_SLAB_FREE_PTR(fmd_new, tgt_fmd_kmem);
245 found->fmd_refcount++; /* caller reference */
246 found->fmd_expire = ktime_get_seconds() +
247 class_exp2tgt(exp)->lut_fmd_max_age;
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));
253 spin_unlock(&ted->ted_fmd_lock);
260 * Drop FMD list reference so it will disappear when last reference is dropped
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.
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
271 * \param[in] exp OBD export
272 * \param[in] fid FID of FMD to drop
274 void tgt_fmd_drop(struct obd_export *exp, const struct lu_fid *fid)
276 struct tg_export_data *ted = &exp->exp_target_data;
277 struct tgt_fmd_data *fmd = NULL;
279 spin_lock(&ted->ted_fmd_lock);
280 fmd = tgt_fmd_find_nolock(exp, fid);
282 list_del_init(&fmd->fmd_list);
283 tgt_fmd_put_nolock(exp, fmd);
285 spin_unlock(&ted->ted_fmd_lock);
287 EXPORT_SYMBOL(tgt_fmd_drop);
291 * Remove all entries from FMD list.
293 * Cleanup function to free all FMD enries on the given export.
295 * \param[in] exp OBD export
297 void tgt_fmd_cleanup(struct obd_export *exp)
299 struct tg_export_data *ted = &exp->exp_target_data;
300 struct tgt_fmd_data *fmd = NULL, *tmp;
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) {
307 "fmd %p still referenced (refcount = %d)\n",
308 fmd, fmd->fmd_refcount);
310 tgt_fmd_put_nolock(exp, fmd);
312 spin_unlock(&ted->ted_fmd_lock);
313 LASSERT(list_empty(&exp->exp_target_data.ted_fmd_list));
317 * Update FMD with the latest request XID.
319 * Save a new setattr/punch XID in FMD if exists.
321 * \param[in] exp OBD export
322 * \param[in] fid FID of FMD to find
323 * \param[in] xid request XID
325 void tgt_fmd_update(struct obd_export *exp, const struct lu_fid *fid, __u64 xid)
327 struct tgt_fmd_data *fmd;
329 fmd = tgt_fmd_get(exp, fid);
331 if (fmd->fmd_mactime_xid < xid)
332 fmd->fmd_mactime_xid = xid;
333 tgt_fmd_put(exp, fmd);
336 EXPORT_SYMBOL(tgt_fmd_update);
339 * Chech that time can be updated by the request with given XID.
341 * Check FMD XID if exists to be less than supplied XID
343 * \param[in] exp OBD export
344 * \param[in] fid FID of FMD to find
345 * \param[in] xid request XID
347 * \retval true if FMD has no greater XID, so time attr can be updated
349 bool tgt_fmd_check(struct obd_export *exp, const struct lu_fid *fid, __u64 xid)
351 struct tgt_fmd_data *fmd;
352 bool can_update = true;
354 fmd = tgt_fmd_find(exp, fid);
356 can_update = fmd->fmd_mactime_xid < xid;
357 tgt_fmd_put(exp, fmd);
362 EXPORT_SYMBOL(tgt_fmd_check);