return retval;
}
+int lprocfs_ofd_rd_fmd_max_num(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ struct obd_device *obd = data;
+ struct ofd_device *ofd = ofd_dev(obd->obd_lu_dev);
+ int rc;
+
+ rc = snprintf(page, count, "%u\n", ofd->ofd_fmd_max_num);
+ return rc;
+}
+
+int lprocfs_ofd_wr_fmd_max_num(struct file *file, const char *buffer,
+ unsigned long count, void *data)
+{
+ struct obd_device *obd = data;
+ struct ofd_device *ofd = ofd_dev(obd->obd_lu_dev);
+ int val;
+ int rc;
+
+ rc = lprocfs_write_helper(buffer, count, &val);
+ if (rc)
+ return rc;
+
+ if (val > 65536 || val < 1)
+ return -EINVAL;
+
+ ofd->ofd_fmd_max_num = val;
+ return count;
+}
+
+int lprocfs_ofd_rd_fmd_max_age(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ struct obd_device *obd = data;
+ struct ofd_device *ofd = ofd_dev(obd->obd_lu_dev);
+ int rc;
+
+ rc = snprintf(page, count, "%ld\n", ofd->ofd_fmd_max_age / CFS_HZ);
+ return rc;
+}
+
+int lprocfs_ofd_wr_fmd_max_age(struct file *file, const char *buffer,
+ unsigned long count, void *data)
+{
+ struct obd_device *obd = data;
+ struct ofd_device *ofd = ofd_dev(obd->obd_lu_dev);
+ int val;
+ int rc;
+
+ rc = lprocfs_write_helper(buffer, count, &val);
+ if (rc)
+ return rc;
+
+ if (val > 65536 || val < 1)
+ return -EINVAL;
+
+ ofd->ofd_fmd_max_age = val * CFS_HZ;
+ return count;
+}
+
static int lprocfs_ofd_rd_capa(char *page, char **start, off_t off,
int count, int *eof, void *data)
{
{ "instance", lprocfs_target_rd_instance, 0 },
{ "ir_factor", lprocfs_obd_rd_ir_factor,
lprocfs_obd_wr_ir_factor, 0},
+ { "client_cache_count", lprocfs_ofd_rd_fmd_max_num,
+ lprocfs_ofd_wr_fmd_max_num, 0 },
+ { "client_cache_seconds", lprocfs_ofd_rd_fmd_max_age,
+ lprocfs_ofd_wr_fmd_max_age, 0 },
{ "capa", lprocfs_ofd_rd_capa,
lprocfs_ofd_wr_capa, 0 },
{ "capa_count", lprocfs_ofd_rd_capa_count, 0, 0 },
--- /dev/null
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Whamcloud, Inc.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/ofd/filter_fmd.c
+ */
+
+#define DEBUG_SUBSYSTEM S_FILTER
+
+#include "ofd_internal.h"
+
+static cfs_mem_cache_t *ll_fmd_cachep;
+
+/* drop fmd reference, free it if last ref. must be called with fed_lock held.*/
+static inline void ofd_fmd_put_nolock(struct obd_export *exp,
+ struct ofd_mod_data *fmd)
+{
+ struct filter_export_data *fed = &exp->exp_filter_data;
+
+ LASSERT_SPIN_LOCKED(&fed->fed_lock);
+ if (--fmd->fmd_refcount == 0) {
+ /* XXX when we have persistent reservations and the handle
+ * is stored herein we need to drop it here. */
+ fed->fed_mod_count--;
+ cfs_list_del(&fmd->fmd_list);
+ OBD_SLAB_FREE(fmd, ll_fmd_cachep, sizeof(*fmd));
+ }
+}
+
+/* drop fmd reference, free it if last ref */
+void ofd_fmd_put(struct obd_export *exp, struct ofd_mod_data *fmd)
+{
+ struct filter_export_data *fed = &exp->exp_filter_data;
+
+ if (fmd == NULL)
+ return;
+
+ cfs_spin_lock(&fed->fed_lock);
+ ofd_fmd_put_nolock(exp, fmd); /* caller reference */
+ cfs_spin_unlock(&fed->fed_lock);
+}
+
+/* expire entries from the end of the list if there are too many
+ * or they are too old */
+static void ofd_fmd_expire_nolock(struct obd_export *exp,
+ struct ofd_mod_data *keep)
+{
+ struct filter_export_data *fed = &exp->exp_filter_data;
+ struct ofd_device *ofd = ofd_exp(exp);
+ struct ofd_mod_data *fmd, *tmp;
+
+ cfs_time_t now = cfs_time_current();
+
+ cfs_list_for_each_entry_safe(fmd, tmp, &fed->fed_mod_list, fmd_list) {
+ if (fmd == keep)
+ break;
+
+ if (cfs_time_before(now, fmd->fmd_expire) &&
+ fed->fed_mod_count < ofd->ofd_fmd_max_num)
+ break;
+
+ cfs_list_del_init(&fmd->fmd_list);
+ ofd_fmd_put_nolock(exp, fmd); /* list reference */
+ }
+}
+
+void ofd_fmd_expire(struct obd_export *exp)
+{
+ struct filter_export_data *fed = &exp->exp_filter_data;
+
+ cfs_spin_lock(&fed->fed_lock);
+ ofd_fmd_expire_nolock(exp, NULL);
+ cfs_spin_unlock(&fed->fed_lock);
+}
+
+/* find specified fid in fed_fmd_list.
+ * caller must hold fed_lock and take fmd reference itself */
+static struct ofd_mod_data *ofd_fmd_find_nolock(struct obd_export *exp,
+ const struct lu_fid *fid)
+{
+ struct filter_export_data *fed = &exp->exp_filter_data;
+ struct ofd_mod_data *found = NULL, *fmd;
+ struct ofd_device *ofd = ofd_exp(exp);
+
+ cfs_time_t now = cfs_time_current();
+
+ LASSERT_SPIN_LOCKED(&fed->fed_lock);
+
+ cfs_list_for_each_entry_reverse(fmd, &fed->fed_mod_list, fmd_list) {
+ if (lu_fid_eq(&fmd->fmd_fid, fid)) {
+ found = fmd;
+ cfs_list_del(&fmd->fmd_list);
+ cfs_list_add_tail(&fmd->fmd_list, &fed->fed_mod_list);
+ fmd->fmd_expire = cfs_time_add(now, ofd->ofd_fmd_max_age);
+ break;
+ }
+ }
+
+ ofd_fmd_expire_nolock(exp, found);
+
+ return found;
+}
+
+/* Find fmd based on fid or return NULL if not found. */
+struct ofd_mod_data *ofd_fmd_find(struct obd_export *exp,
+ struct lu_fid *fid)
+{
+ struct filter_export_data *fed = &exp->exp_filter_data;
+ struct ofd_mod_data *fmd;
+
+ cfs_spin_lock(&fed->fed_lock);
+ fmd = ofd_fmd_find_nolock(exp, fid);
+ if (fmd)
+ fmd->fmd_refcount++; /* caller reference */
+ cfs_spin_unlock(&fed->fed_lock);
+
+ return fmd;
+}
+
+/* Find fmd based on FID, or create a new one if none is found.
+ * It is possible for this function to return NULL under memory pressure,
+ * or if fid = 0 is passed (which will only cause old entries to expire).
+ * Currently this is not fatal because any fmd state is transient and
+ * may also be freed when it gets sufficiently old. */
+struct ofd_mod_data *ofd_fmd_get(struct obd_export *exp, struct lu_fid *fid)
+{
+ struct filter_export_data *fed = &exp->exp_filter_data;
+ struct ofd_device *ofd = ofd_exp(exp);
+ struct ofd_mod_data *found = NULL, *fmd_new = NULL;
+
+ cfs_time_t now = cfs_time_current();
+
+ OBD_SLAB_ALLOC_PTR(fmd_new, ll_fmd_cachep);
+
+ cfs_spin_lock(&fed->fed_lock);
+ found = ofd_fmd_find_nolock(exp, fid);
+ if (fmd_new) {
+ if (found == NULL) {
+ cfs_list_add_tail(&fmd_new->fmd_list,
+ &fed->fed_mod_list);
+ fmd_new->fmd_fid = *fid;
+ fmd_new->fmd_refcount++; /* list reference */
+ found = fmd_new;
+ fed->fed_mod_count++;
+ } else {
+ OBD_SLAB_FREE_PTR(fmd_new, ll_fmd_cachep);
+ }
+ }
+ if (found) {
+ found->fmd_refcount++; /* caller reference */
+ found->fmd_expire = cfs_time_add(now, ofd->ofd_fmd_max_age);
+ }
+
+ cfs_spin_unlock(&fed->fed_lock);
+
+ return found;
+}
+
+#ifdef DO_FMD_DROP
+/* drop fmd list reference so it will disappear when last reference is put.
+ * This isn't so critical because it would in fact only affect the one client
+ * that is doing the unlink and at worst we have an stale entry referencing
+ * an object that should never be used again. */
+void ofd_fmd_drop(struct obd_export *exp, struct lu_fid *fid)
+{
+ struct filter_export_data *fed = &exp->exp_filter_data;
+ struct ofd_mod_data *found = NULL;
+
+ cfs_spin_lock(&fed->fed_lock);
+ found = ofd_fmd_find_nolock(exp, fid);
+ if (found) {
+ cfs_list_del_init(&found->fmd_list);
+ ofd_fmd_put_nolock(exp, found);
+ }
+ cfs_spin_unlock(&fed->fed_lock);
+}
+#endif
+
+/* remove all entries from fmd list */
+void ofd_fmd_cleanup(struct obd_export *exp)
+{
+ struct filter_export_data *fed = &exp->exp_filter_data;
+ struct ofd_mod_data *fmd = NULL, *tmp;
+
+ cfs_spin_lock(&fed->fed_lock);
+ cfs_list_for_each_entry_safe(fmd, tmp, &fed->fed_mod_list, fmd_list) {
+ cfs_list_del_init(&fmd->fmd_list);
+ if (fmd->fmd_refcount > 1) {
+ CDEBUG(D_INFO, "fmd %p still referenced (refcount = %d)\n",
+ fmd, fmd->fmd_refcount);
+ }
+ ofd_fmd_put_nolock(exp, fmd);
+ }
+ cfs_spin_unlock(&fed->fed_lock);
+}
+
+int ofd_fmd_init(void)
+{
+ ll_fmd_cachep = cfs_mem_cache_create("ll_fmd_cache",
+ sizeof(struct ofd_mod_data),
+ 0, 0);
+ if (!ll_fmd_cachep)
+ return -ENOMEM;
+ else
+ return 0;
+}
+
+void ofd_fmd_exit(void)
+{
+ if (ll_fmd_cachep) {
+ int rc = cfs_mem_cache_destroy(ll_fmd_cachep);
+
+ LASSERTF(rc == 0, "Cannot destroy ll_fmd_cachep: rc %d\n", rc);
+ ll_fmd_cachep = NULL;
+ }
+}
OBD_INCOMPAT_COMMON_LR)
#define OFD_MAX_GROUPS 256
+/* per-client-per-object persistent state (LRU) */
+struct ofd_mod_data {
+ cfs_list_t fmd_list; /* linked to fed_mod_list */
+ struct lu_fid fmd_fid; /* FID being written to */
+ __u64 fmd_mactime_xid; /* xid highest {m,a,c}time setattr */
+ cfs_time_t fmd_expire; /* time when the fmd should expire */
+ int fmd_refcount; /* reference counter - list holds 1 */
+};
+
+#define OFD_FMD_MAX_NUM_DEFAULT 128
+#define OFD_FMD_MAX_AGE_DEFAULT ((obd_timeout + 10) * CFS_HZ)
+
enum {
LPROC_OFD_READ_BYTES = 0,
LPROC_OFD_WRITE_BYTES = 1,
struct dt_object *ofd_lastid_obj[OFD_MAX_GROUPS];
cfs_spinlock_t ofd_objid_lock;
+ /* ofd mod data: ofd_device wide values */
+ int ofd_fmd_max_num; /* per ofd ofd_mod_data */
+ cfs_duration_t ofd_fmd_max_age; /* time to fmd expiry */
+
cfs_spinlock_t ofd_flags_lock;
unsigned long ofd_raid_degraded:1,
/* sync journal on writes */
int lproc_ofd_attach_seqstat(struct obd_device *dev);
extern struct file_operations ofd_per_nid_stats_fops;
+/* ofd_fmd.c */
+int ofd_fmd_init(void);
+void ofd_fmd_exit(void);
+struct ofd_mod_data *ofd_fmd_find(struct obd_export *exp,
+ struct lu_fid *fid);
+struct ofd_mod_data *ofd_fmd_get(struct obd_export *exp,
+ struct lu_fid *fid);
+void ofd_fmd_put(struct obd_export *exp, struct ofd_mod_data *fmd);
+void ofd_fmd_expire(struct obd_export *exp);
+void ofd_fmd_cleanup(struct obd_export *exp);
+#ifdef DO_FMD_DROP
+void ofd_fmd_drop(struct obd_export *exp, struct lu_fid *fid);
+#else
+#define ofd_fmd_drop(exp, fid) do {} while (0)
+#endif
+
static inline struct ofd_thread_info * ofd_info(const struct lu_env *env)
{
struct ofd_thread_info *info;