return aa_get_label(l);
}
+/**
+ * aa_get_newest_label_condref - find the newest version of @l
+ * @l: the label to check for newer versions of
+ * @needput: returns whether the reference needs put
+ *
+ * Returns: refcounted newest version of @l taking into account
+ * replacement, renames and removals
+ * return @l.
+ */
+static inline struct aa_label *aa_get_newest_label_condref(struct aa_label *l,
+ bool *needput)
+{
+ if (l && unlikely(label_is_stale(l))) {
+ struct aa_label *tmp;
+
+ AA_BUG(!l->proxy);
+ AA_BUG(!l->proxy->label);
+ /* BUG: only way this can happen is @l ref count and its
+ * replacement count have gone to 0 and are on their way
+ * to destruction. ie. we have a refcounting error
+ */
+ tmp = aa_get_label_rcu(&l->proxy->label);
+ AA_BUG(!tmp);
+
+ *needput = true;
+ return tmp;
+ }
+
+ *needput = false;
+ return l;
+}
+
static inline void aa_put_label(struct aa_label *l)
{
if (l)
struct aa_label *aa_label_find_merge(struct aa_label *a, struct aa_label *b)
{
struct aa_labelset *ls;
- struct aa_label *label, *ar = NULL, *br = NULL;
+ struct aa_label *label;
unsigned long flags;
+ bool a_needput, b_needput;
AA_BUG(!a);
AA_BUG(!b);
- if (label_is_stale(a))
- a = ar = aa_get_newest_label(a);
- if (label_is_stale(b))
- b = br = aa_get_newest_label(b);
+ a = aa_get_newest_label_condref(a, &a_needput);
+ b = aa_get_newest_label_condref(b, &b_needput);
ls = labelset_of_merge(a, b);
read_lock_irqsave(&ls->lock, flags);
label = __label_find_merge(ls, a, b);
read_unlock_irqrestore(&ls->lock, flags);
- aa_put_label(ar);
- aa_put_label(br);
+ aa_put_label_condref(a, a_needput);
+ aa_put_label_condref(b, b_needput);
return label;
}
if (!label) {
struct aa_label *new;
+ bool a_needput, b_needput;
- a = aa_get_newest_label(a);
- b = aa_get_newest_label(b);
+ a = aa_get_newest_label_condref(a, &a_needput);
+ b = aa_get_newest_label_condref(b, &b_needput);
/* could use label_merge_len(a, b), but requires double
* comparison for small savings
label = label_merge_insert(new, a, b);
label_free_or_put_new(label, new);
out:
- aa_put_label(a);
- aa_put_label(b);
+ aa_put_label_condref(a, a_needput);
+ aa_put_label_condref(b, b_needput);
}
return label;