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.
29 * This file is part of Lustre, http://www.lustre.org/
30 * Lustre is a trademark of Sun Microsystems, Inc.
32 * lustre/ofd/ofd_fmd.c
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
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.
43 * FMD can expire if there are no updates for a long time to keep the list
46 * Author: Andreas Dilger <andreas.dilger@intel.com>
49 #define DEBUG_SUBSYSTEM S_FILTER
51 #include "ofd_internal.h"
54 * Drop FMD reference and free it if reference drops to zero.
56 * Must be called with ted_fmd_lock held.
58 * \param[in] exp OBD export
59 * \param[in] fmd FMD to put
61 static inline void ofd_fmd_put_nolock(struct obd_export *exp,
62 struct tgt_fmd_data *fmd)
64 struct tg_export_data *ted = &exp->exp_target_data;
66 assert_spin_locked(&ted->ted_fmd_lock);
67 if (--fmd->fmd_refcount == 0) {
69 list_del(&fmd->fmd_list);
70 OBD_SLAB_FREE_PTR(fmd, tgt_fmd_kmem);
75 * Wrapper to drop FMD reference with ted_fmd_lock held.
77 * \param[in] exp OBD export
78 * \param[in] fmd FMD to put
80 void ofd_fmd_put(struct obd_export *exp, struct tgt_fmd_data *fmd)
82 struct tg_export_data *ted = &exp->exp_target_data;
84 spin_lock(&ted->ted_fmd_lock);
85 ofd_fmd_put_nolock(exp, fmd); /* caller reference */
86 spin_unlock(&ted->ted_fmd_lock);
92 * Expire entries from the FMD list if there are too many
93 * of them or they are too old.
95 * This function must be called with ted_fmd_lock held.
97 * The \a keep FMD is not to be expired in any case. This parameter is used
98 * by ofd_fmd_find_nolock() to prohibit a FMD that was just found from
101 * \param[in] exp OBD export
102 * \param[in] keep FMD to keep always
104 static void ofd_fmd_expire_nolock(struct obd_export *exp,
105 struct tgt_fmd_data *keep)
107 struct tg_export_data *ted = &exp->exp_target_data;
108 struct lu_target *lut = exp->exp_obd->u.obt.obt_lut;
109 time64_t now = ktime_get_seconds();
110 struct tgt_fmd_data *fmd, *tmp;
112 list_for_each_entry_safe(fmd, tmp, &ted->ted_fmd_list, fmd_list) {
116 if (now < fmd->fmd_expire &&
117 ted->ted_fmd_count < lut->lut_fmd_max_num)
120 list_del_init(&fmd->fmd_list);
121 ofd_fmd_put_nolock(exp, fmd); /* list reference */
126 * Expire FMD entries.
128 * This is a wrapper to call ofd_fmd_expire_nolock() with the required lock.
130 * \param[in] exp OBD export
132 void ofd_fmd_expire(struct obd_export *exp)
134 struct tg_export_data *ted = &exp->exp_target_data;
136 spin_lock(&ted->ted_fmd_lock);
137 ofd_fmd_expire_nolock(exp, NULL);
138 spin_unlock(&ted->ted_fmd_lock);
142 * Find FMD by specified FID.
144 * Function finds FMD entry by FID in the tg_export_data::ted_fmd_list.
146 * Caller must hold tg_export_data::ted_fmd_lock and take FMD reference.
148 * \param[in] exp OBD export
149 * \param[in] fid FID of FMD to find
151 * \retval struct tgt_fmd_data found by FID
152 * \retval NULL is FMD is not found
154 static struct tgt_fmd_data *ofd_fmd_find_nolock(struct obd_export *exp,
155 const struct lu_fid *fid)
157 struct tg_export_data *ted = &exp->exp_target_data;
158 struct tgt_fmd_data *found = NULL, *fmd;
159 struct lu_target *lut = exp->exp_obd->u.obt.obt_lut;
161 assert_spin_locked(&ted->ted_fmd_lock);
163 list_for_each_entry_reverse(fmd, &ted->ted_fmd_list, fmd_list) {
164 if (lu_fid_eq(&fmd->fmd_fid, fid)) {
166 list_move_tail(&fmd->fmd_list, &ted->ted_fmd_list);
167 fmd->fmd_expire = ktime_get_seconds() +
168 lut->lut_fmd_max_age;
173 ofd_fmd_expire_nolock(exp, found);
179 * Find FMD by specified FID with locking.
181 * Wrapper to the ofd_fmd_find_nolock() with correct locks.
183 * \param[in] exp OBD export
184 * \param[in] fid FID of FMD to find
186 * \retval struct tgt_fmd_data found by FID
187 * \retval NULL indicates FMD is not found
189 struct tgt_fmd_data *ofd_fmd_find(struct obd_export *exp,
190 const struct lu_fid *fid)
192 struct tg_export_data *ted = &exp->exp_target_data;
193 struct tgt_fmd_data *fmd;
195 spin_lock(&ted->ted_fmd_lock);
196 fmd = ofd_fmd_find_nolock(exp, fid);
198 fmd->fmd_refcount++; /* caller reference */
199 spin_unlock(&ted->ted_fmd_lock);
205 * Find FMD by FID or create a new one if none is found.
207 * It is possible for this function to return NULL under memory pressure,
208 * or if the passed FID is zero (which will only cause old entries to expire).
209 * Currently this is not fatal because any FMD state is transient and
210 * may also be freed when it gets sufficiently old.
212 * \param[in] exp OBD export
213 * \param[in] fid FID of FMD to find
215 * \retval struct tgt_fmd_data found by FID
216 * \retval NULL indicates FMD is not found
218 struct tgt_fmd_data *ofd_fmd_get(struct obd_export *exp,
219 const struct lu_fid *fid)
221 struct tg_export_data *ted = &exp->exp_target_data;
222 struct tgt_fmd_data *found = NULL, *fmd_new = NULL;
224 OBD_SLAB_ALLOC_PTR(fmd_new, tgt_fmd_kmem);
226 spin_lock(&ted->ted_fmd_lock);
227 found = ofd_fmd_find_nolock(exp, fid);
230 list_add_tail(&fmd_new->fmd_list, &ted->ted_fmd_list);
231 fmd_new->fmd_fid = *fid;
232 fmd_new->fmd_refcount++; /* list reference */
234 ted->ted_fmd_count++;
236 OBD_SLAB_FREE_PTR(fmd_new, tgt_fmd_kmem);
240 found->fmd_refcount++; /* caller reference */
241 found->fmd_expire = ktime_get_seconds() +
242 class_exp2tgt(exp)->lut_fmd_max_age;
244 LCONSOLE_WARN("%s: cannot allocate FMD for "DFID
245 ", timestamps may be out of sync\n",
246 exp->exp_obd->obd_name, PFID(fid));
248 spin_unlock(&ted->ted_fmd_lock);
255 * Drop FMD list reference so it will disappear when last reference is dropped
258 * This function is called from ofd_destroy() and may only affect
259 * the one client that is doing the unlink and at worst we have an stale entry
260 * referencing an object that should never be used again.
262 * NB: this function is used only if DO_FMD_DROP is defined. It is not
263 * currently defined, so FMD drop doesn't happen and FMD are dropped only
266 * \param[in] exp OBD export
267 * \param[in] fid FID of FMD to drop
269 void ofd_fmd_drop(struct obd_export *exp, const struct lu_fid *fid)
271 struct tg_export_data *ted = &exp->exp_target_data;
272 struct tgt_fmd_data *found = NULL;
274 spin_lock(&ted->ted_fmd_lock);
275 found = ofd_fmd_find_nolock(exp, fid);
277 list_del_init(&found->fmd_list);
278 ofd_fmd_put_nolock(exp, found);
280 spin_unlock(&ted->ted_fmd_lock);
285 * Remove all entries from FMD list.
287 * Cleanup function to free all FMD enries on the given export.
289 * \param[in] exp OBD export
291 void ofd_fmd_cleanup(struct obd_export *exp)
293 struct tg_export_data *ted = &exp->exp_target_data;
294 struct tgt_fmd_data *fmd = NULL, *tmp;
296 spin_lock(&ted->ted_fmd_lock);
297 list_for_each_entry_safe(fmd, tmp, &ted->ted_fmd_list, fmd_list) {
298 list_del_init(&fmd->fmd_list);
299 if (fmd->fmd_refcount > 1) {
301 "fmd %p is still referenced (refcount = %d)\n",
302 fmd, fmd->fmd_refcount);
304 ofd_fmd_put_nolock(exp, fmd);
306 spin_unlock(&ted->ted_fmd_lock);
310 * Update FMD with the latest request XID.
312 * Save a new setattr/punch XID in FMD if exists.
314 * \param[in] exp OBD export
315 * \param[in] fid FID of FMD to find
316 * \param[in] xid request XID
318 void tgt_fmd_update(struct obd_export *exp, const struct lu_fid *fid, __u64 xid)
320 struct tgt_fmd_data *fmd;
322 fmd = ofd_fmd_get(exp, fid);
324 if (fmd->fmd_mactime_xid < xid)
325 fmd->fmd_mactime_xid = xid;
326 ofd_fmd_put(exp, fmd);
331 * Chech that time can be updated by the request with given XID.
333 * Check FMD XID if exists to be less than supplied XID
335 * \param[in] exp OBD export
336 * \param[in] fid FID of FMD to find
337 * \param[in] xid request XID
339 * \retval true if FMD has no greater XID, so time attr can be updated
341 bool tgt_fmd_check(struct obd_export *exp, const struct lu_fid *fid, __u64 xid)
343 struct tgt_fmd_data *fmd;
344 bool can_update = true;
346 fmd = ofd_fmd_find(exp, fid);
348 can_update = fmd->fmd_mactime_xid < xid;
349 ofd_fmd_put(exp, fmd);