Whamcloud - gitweb
8c8e263ad5ed5b58b4f0de9ad2267cccaa5b842e
[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, 2014, 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 /**
54  * Drop FMD reference and free it if reference drops to zero.
55  *
56  * Must be called with ted_fmd_lock held.
57  *
58  * \param[in] exp       OBD export
59  * \param[in] fmd       FMD to put
60  */
61 static inline void ofd_fmd_put_nolock(struct obd_export *exp,
62                                       struct tgt_fmd_data *fmd)
63 {
64         struct tg_export_data *ted = &exp->exp_target_data;
65
66         assert_spin_locked(&ted->ted_fmd_lock);
67         if (--fmd->fmd_refcount == 0) {
68                 ted->ted_fmd_count--;
69                 list_del(&fmd->fmd_list);
70                 OBD_SLAB_FREE_PTR(fmd, tgt_fmd_kmem);
71         }
72 }
73
74 /**
75  * Wrapper to drop FMD reference with ted_fmd_lock held.
76  *
77  * \param[in] exp       OBD export
78  * \param[in] fmd       FMD to put
79  */
80 void ofd_fmd_put(struct obd_export *exp, struct tgt_fmd_data *fmd)
81 {
82         struct tg_export_data *ted = &exp->exp_target_data;
83
84         spin_lock(&ted->ted_fmd_lock);
85         ofd_fmd_put_nolock(exp, fmd); /* caller reference */
86         spin_unlock(&ted->ted_fmd_lock);
87 }
88
89 /**
90  * Expire FMD entries.
91  *
92  * Expire entries from the FMD list if there are too many
93  * of them or they are too old.
94  *
95  * This function must be called with ted_fmd_lock held.
96  *
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
99  * expiring.
100  *
101  * \param[in] exp       OBD export
102  * \param[in] keep      FMD to keep always
103  */
104 static void ofd_fmd_expire_nolock(struct obd_export *exp,
105                                   struct tgt_fmd_data *keep)
106 {
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;
111
112         list_for_each_entry_safe(fmd, tmp, &ted->ted_fmd_list, fmd_list) {
113                 if (fmd == keep)
114                         break;
115
116                 if (now < fmd->fmd_expire &&
117                     ted->ted_fmd_count < lut->lut_fmd_max_num)
118                         break;
119
120                 list_del_init(&fmd->fmd_list);
121                 ofd_fmd_put_nolock(exp, fmd); /* list reference */
122         }
123 }
124
125 /**
126  * Expire FMD entries.
127  *
128  * This is a wrapper to call ofd_fmd_expire_nolock() with the required lock.
129  *
130  * \param[in] exp       OBD export
131  */
132 void ofd_fmd_expire(struct obd_export *exp)
133 {
134         struct tg_export_data *ted = &exp->exp_target_data;
135
136         spin_lock(&ted->ted_fmd_lock);
137         ofd_fmd_expire_nolock(exp, NULL);
138         spin_unlock(&ted->ted_fmd_lock);
139 }
140
141 /**
142  * Find FMD by specified FID.
143  *
144  * Function finds FMD entry by FID in the tg_export_data::ted_fmd_list.
145  *
146  * Caller must hold tg_export_data::ted_fmd_lock and take FMD reference.
147  *
148  * \param[in] exp       OBD export
149  * \param[in] fid       FID of FMD to find
150  *
151  * \retval              struct tgt_fmd_data found by FID
152  * \retval              NULL is FMD is not found
153  */
154 static struct tgt_fmd_data *ofd_fmd_find_nolock(struct obd_export *exp,
155                                                 const struct lu_fid *fid)
156 {
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;
160
161         assert_spin_locked(&ted->ted_fmd_lock);
162
163         list_for_each_entry_reverse(fmd, &ted->ted_fmd_list, fmd_list) {
164                 if (lu_fid_eq(&fmd->fmd_fid, fid)) {
165                         found = fmd;
166                         list_move_tail(&fmd->fmd_list, &ted->ted_fmd_list);
167                         fmd->fmd_expire = ktime_get_seconds() +
168                                           lut->lut_fmd_max_age;
169                         break;
170                 }
171         }
172
173         ofd_fmd_expire_nolock(exp, found);
174
175         return found;
176 }
177
178 /**
179  * Find FMD by specified FID with locking.
180  *
181  * Wrapper to the ofd_fmd_find_nolock() with correct locks.
182  *
183  * \param[in] exp       OBD export
184  * \param[in] fid       FID of FMD to find
185  *
186  * \retval              struct tgt_fmd_data found by FID
187  * \retval              NULL indicates FMD is not found
188  */
189 struct tgt_fmd_data *ofd_fmd_find(struct obd_export *exp,
190                                   const struct lu_fid *fid)
191 {
192         struct tg_export_data *ted = &exp->exp_target_data;
193         struct tgt_fmd_data *fmd;
194
195         spin_lock(&ted->ted_fmd_lock);
196         fmd = ofd_fmd_find_nolock(exp, fid);
197         if (fmd)
198                 fmd->fmd_refcount++;    /* caller reference */
199         spin_unlock(&ted->ted_fmd_lock);
200
201         return fmd;
202 }
203
204 /**
205  * Find FMD by FID or create a new one if none is found.
206  *
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.
211  *
212  * \param[in] exp       OBD export
213  * \param[in] fid       FID of FMD to find
214  *
215  * \retval              struct tgt_fmd_data found by FID
216  * \retval              NULL indicates FMD is not found
217  */
218 struct tgt_fmd_data *ofd_fmd_get(struct obd_export *exp,
219                                  const struct lu_fid *fid)
220 {
221         struct tg_export_data *ted = &exp->exp_target_data;
222         struct tgt_fmd_data *found = NULL, *fmd_new = NULL;
223
224         OBD_SLAB_ALLOC_PTR(fmd_new, tgt_fmd_kmem);
225
226         spin_lock(&ted->ted_fmd_lock);
227         found = ofd_fmd_find_nolock(exp, fid);
228         if (fmd_new) {
229                 if (found == NULL) {
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 */
233                         found = fmd_new;
234                         ted->ted_fmd_count++;
235                 } else {
236                         OBD_SLAB_FREE_PTR(fmd_new, tgt_fmd_kmem);
237                 }
238         }
239         if (found) {
240                 found->fmd_refcount++; /* caller reference */
241                 found->fmd_expire = ktime_get_seconds() +
242                         class_exp2tgt(exp)->lut_fmd_max_age;
243         } else {
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));
247         }
248         spin_unlock(&ted->ted_fmd_lock);
249
250         return found;
251 }
252
253 #ifdef DO_FMD_DROP
254 /**
255  * Drop FMD list reference so it will disappear when last reference is dropped
256  * to zero.
257  *
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.
261  *
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
264  * when expired.
265  *
266  * \param[in] exp       OBD export
267  * \param[in] fid       FID of FMD to drop
268  */
269 void ofd_fmd_drop(struct obd_export *exp, const struct lu_fid *fid)
270 {
271         struct tg_export_data *ted = &exp->exp_target_data;
272         struct tgt_fmd_data *found = NULL;
273
274         spin_lock(&ted->ted_fmd_lock);
275         found = ofd_fmd_find_nolock(exp, fid);
276         if (found) {
277                 list_del_init(&found->fmd_list);
278                 ofd_fmd_put_nolock(exp, found);
279         }
280         spin_unlock(&ted->ted_fmd_lock);
281 }
282 #endif
283
284 /**
285  * Remove all entries from FMD list.
286  *
287  * Cleanup function to free all FMD enries on the given export.
288  *
289  * \param[in] exp       OBD export
290  */
291 void ofd_fmd_cleanup(struct obd_export *exp)
292 {
293         struct tg_export_data *ted = &exp->exp_target_data;
294         struct tgt_fmd_data *fmd = NULL, *tmp;
295
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) {
300                         CDEBUG(D_INFO,
301                                "fmd %p is still referenced (refcount = %d)\n",
302                                fmd, fmd->fmd_refcount);
303                 }
304                 ofd_fmd_put_nolock(exp, fmd);
305         }
306         spin_unlock(&ted->ted_fmd_lock);
307 }
308
309 /**
310  * Update FMD with the latest request XID.
311  *
312  * Save a new setattr/punch XID in FMD if exists.
313  *
314  * \param[in] exp       OBD export
315  * \param[in] fid       FID of FMD to find
316  * \param[in] xid       request XID
317  */
318 void tgt_fmd_update(struct obd_export *exp, const struct lu_fid *fid, __u64 xid)
319 {
320         struct tgt_fmd_data *fmd;
321
322         fmd = ofd_fmd_get(exp, fid);
323         if (fmd) {
324                 if (fmd->fmd_mactime_xid < xid)
325                         fmd->fmd_mactime_xid = xid;
326                 ofd_fmd_put(exp, fmd);
327         }
328 }
329
330 /**
331  * Chech that time can be updated by the request with given XID.
332  *
333  * Check FMD XID if exists to be less than supplied XID
334  *
335  * \param[in] exp       OBD export
336  * \param[in] fid       FID of FMD to find
337  * \param[in] xid       request XID
338  *
339  * \retval true if FMD has no greater XID, so time attr can be updated
340  */
341 bool tgt_fmd_check(struct obd_export *exp, const struct lu_fid *fid, __u64 xid)
342 {
343         struct tgt_fmd_data *fmd;
344         bool can_update = true;
345
346         fmd = ofd_fmd_find(exp, fid);
347         if (fmd) {
348                 can_update = fmd->fmd_mactime_xid < xid;
349                 ofd_fmd_put(exp, fmd);
350         }
351
352         return can_update;
353 }
354