+ return 0;
+}
+
+/* drop fmd reference, free it if last ref. must be called with fed_lock held.*/
+static inline void filter_fmd_put_nolock(struct filter_export_data *fed,
+ struct filter_mod_data *fmd)
+{
+ 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--;
+ list_del(&fmd->fmd_list);
+ OBD_SLAB_FREE(fmd, ll_fmd_cachep, sizeof(*fmd));
+ }
+}
+
+/* drop fmd reference, free it if last ref */
+void filter_fmd_put(struct obd_export *exp, struct filter_mod_data *fmd)
+{
+ struct filter_export_data *fed;
+
+ if (fmd == NULL)
+ return;
+
+ fed = &exp->exp_filter_data;
+ spin_lock(&fed->fed_lock);
+ filter_fmd_put_nolock(fed, fmd); /* caller reference */
+ 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 filter_fmd_expire_nolock(struct filter_obd *filter,
+ struct filter_export_data *fed,
+ struct filter_mod_data *keep)
+{
+ struct filter_mod_data *fmd, *tmp;
+
+ list_for_each_entry_safe(fmd, tmp, &fed->fed_mod_list, fmd_list) {
+ if (fmd == keep)
+ break;
+
+ if (time_before(jiffies, fmd->fmd_expire) &&
+ fed->fed_mod_count < filter->fo_fmd_max_num)
+ break;
+
+ list_del_init(&fmd->fmd_list);
+ filter_fmd_put_nolock(fed, fmd); /* list reference */
+ }
+}
+
+void filter_fmd_expire(struct obd_export *exp)
+{
+ spin_lock(&exp->exp_filter_data.fed_lock);
+ filter_fmd_expire_nolock(&exp->exp_obd->u.filter,
+ &exp->exp_filter_data, NULL);
+ spin_unlock(&exp->exp_filter_data.fed_lock);
+}
+
+/* find specified objid, group in export fmd list.
+ * caller must hold fed_lock and take fmd reference itself */
+static struct filter_mod_data *filter_fmd_find_nolock(struct filter_obd *filter,
+ struct filter_export_data *fed,
+ obd_id objid, obd_gr group)
+{
+ struct filter_mod_data *found = NULL, *fmd;
+
+ LASSERT_SPIN_LOCKED(&fed->fed_lock);
+
+ list_for_each_entry_reverse(fmd, &fed->fed_mod_list, fmd_list) {
+ if (fmd->fmd_id == objid && fmd->fmd_gr == group) {
+ found = fmd;
+ list_del(&fmd->fmd_list);
+ list_add_tail(&fmd->fmd_list, &fed->fed_mod_list);
+ fmd->fmd_expire = jiffies + filter->fo_fmd_max_age;
+ break;
+ }
+ }
+
+ filter_fmd_expire_nolock(filter, fed, found);
+
+ return found;
+}
+
+/* Find fmd based on objid and group, or return NULL if not found. */
+struct filter_mod_data *filter_fmd_find(struct obd_export *exp,
+ obd_id objid, obd_gr group)
+{
+ struct filter_mod_data *fmd;
+
+ spin_lock(&exp->exp_filter_data.fed_lock);
+ fmd = filter_fmd_find_nolock(&exp->exp_obd->u.filter,
+ &exp->exp_filter_data, objid, group);
+ if (fmd)
+ fmd->fmd_refcount++; /* caller reference */
+ spin_unlock(&exp->exp_filter_data.fed_lock);
+
+ return fmd;
+}
+
+/* Find fmd based on objid and group, or create a new one if none is found.
+ * It is possible for this function to return NULL under memory pressure,
+ * or if objid = 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 filter_mod_data *filter_fmd_get(struct obd_export *exp,
+ obd_id objid, obd_gr group)
+{
+ struct filter_export_data *fed = &exp->exp_filter_data;
+ struct filter_mod_data *found = NULL, *fmd_new = NULL;
+
+ OBD_SLAB_ALLOC(fmd_new, ll_fmd_cachep, CFS_ALLOC_IO, sizeof(*fmd_new));
+
+ spin_lock(&fed->fed_lock);
+ found = filter_fmd_find_nolock(&exp->exp_obd->u.filter,fed,objid,group);
+ if (fmd_new) {
+ if (found == NULL) {
+ list_add_tail(&fmd_new->fmd_list, &fed->fed_mod_list);
+ fmd_new->fmd_id = objid;
+ fmd_new->fmd_gr = group;
+ fmd_new->fmd_refcount++; /* list reference */
+ found = fmd_new;
+ fed->fed_mod_count++;
+ } else {
+ OBD_SLAB_FREE(fmd_new, ll_fmd_cachep, sizeof(*fmd_new));
+ }
+ }
+ if (found) {
+ found->fmd_refcount++; /* caller reference */
+ found->fmd_expire = jiffies +
+ exp->exp_obd->u.filter.fo_fmd_max_age;
+ }
+
+ 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. */
+static void filter_fmd_drop(struct obd_export *exp, obd_id objid, obd_gr group)
+{
+ struct filter_mod_data *found = NULL;
+
+ spin_lock(&exp->exp_filter_data.fed_lock);
+ found = filter_fmd_find_nolock(&exp->exp_filter_data, objid, group);
+ if (found) {
+ list_del_init(&found->fmd_list);
+ filter_fmd_put_nolock(&exp->exp_filter_data, found);
+ }
+ spin_unlock(&exp->exp_filter_data.fed_lock);
+}
+#else
+#define filter_fmd_drop(exp, objid, group)
+#endif
+
+/* remove all entries from fmd list */
+static void filter_fmd_cleanup(struct obd_export *exp)
+{
+ struct filter_export_data *fed = &exp->exp_filter_data;
+ struct filter_mod_data *fmd = NULL, *tmp;
+
+ spin_lock(&fed->fed_lock);
+ list_for_each_entry_safe(fmd, tmp, &fed->fed_mod_list, fmd_list) {
+ list_del_init(&fmd->fmd_list);
+ filter_fmd_put_nolock(fed, fmd);
+ }
+ spin_unlock(&fed->fed_lock);
+}
+
+static int filter_init_export(struct obd_export *exp)
+{
+ spin_lock_init(&exp->exp_filter_data.fed_lock);
+ INIT_LIST_HEAD(&exp->exp_filter_data.fed_mod_list);
+
+ spin_lock(&exp->exp_lock);
+ exp->exp_connecting = 1;
+ spin_unlock(&exp->exp_lock);
+
+ return 0;