rhashtable: add rhashtable_lookup_get_insert_key()
[cascardo/linux.git] / include / linux / rhashtable.h
index 3eef080..26b7a05 100644 (file)
@@ -343,7 +343,8 @@ int rhashtable_init(struct rhashtable *ht,
 struct bucket_table *rhashtable_insert_slow(struct rhashtable *ht,
                                            const void *key,
                                            struct rhash_head *obj,
-                                           struct bucket_table *old_tbl);
+                                           struct bucket_table *old_tbl,
+                                           void **data);
 int rhashtable_insert_rehash(struct rhashtable *ht, struct bucket_table *tbl);
 
 int rhashtable_walk_init(struct rhashtable *ht, struct rhashtable_iter *iter,
@@ -563,8 +564,11 @@ restart:
        return NULL;
 }
 
-/* Internal function, please use rhashtable_insert_fast() instead */
-static inline int __rhashtable_insert_fast(
+/* Internal function, please use rhashtable_insert_fast() instead. This
+ * function returns the existing element already in hashes in there is a clash,
+ * otherwise it returns an error via ERR_PTR().
+ */
+static inline void *__rhashtable_insert_fast(
        struct rhashtable *ht, const void *key, struct rhash_head *obj,
        const struct rhashtable_params params)
 {
@@ -577,6 +581,7 @@ static inline int __rhashtable_insert_fast(
        spinlock_t *lock;
        unsigned int elasticity;
        unsigned int hash;
+       void *data = NULL;
        int err;
 
 restart:
@@ -601,11 +606,14 @@ restart:
 
        new_tbl = rht_dereference_rcu(tbl->future_tbl, ht);
        if (unlikely(new_tbl)) {
-               tbl = rhashtable_insert_slow(ht, key, obj, new_tbl);
+               tbl = rhashtable_insert_slow(ht, key, obj, new_tbl, &data);
                if (!IS_ERR_OR_NULL(tbl))
                        goto slow_path;
 
                err = PTR_ERR(tbl);
+               if (err == -EEXIST)
+                       err = 0;
+
                goto out;
        }
 
@@ -619,25 +627,25 @@ slow_path:
                err = rhashtable_insert_rehash(ht, tbl);
                rcu_read_unlock();
                if (err)
-                       return err;
+                       return ERR_PTR(err);
 
                goto restart;
        }
 
-       err = -EEXIST;
+       err = 0;
        elasticity = ht->elasticity;
        rht_for_each(head, tbl, hash) {
                if (key &&
                    unlikely(!(params.obj_cmpfn ?
                               params.obj_cmpfn(&arg, rht_obj(ht, head)) :
-                              rhashtable_compare(&arg, rht_obj(ht, head)))))
+                              rhashtable_compare(&arg, rht_obj(ht, head))))) {
+                       data = rht_obj(ht, head);
                        goto out;
+               }
                if (!--elasticity)
                        goto slow_path;
        }
 
-       err = 0;
-
        head = rht_dereference_bucket(tbl->buckets[hash], tbl, hash);
 
        RCU_INIT_POINTER(obj->next, head);
@@ -652,7 +660,7 @@ out:
        spin_unlock_bh(lock);
        rcu_read_unlock();
 
-       return err;
+       return err ? ERR_PTR(err) : data;
 }
 
 /**
@@ -675,7 +683,13 @@ static inline int rhashtable_insert_fast(
        struct rhashtable *ht, struct rhash_head *obj,
        const struct rhashtable_params params)
 {
-       return __rhashtable_insert_fast(ht, NULL, obj, params);
+       void *ret;
+
+       ret = __rhashtable_insert_fast(ht, NULL, obj, params);
+       if (IS_ERR(ret))
+               return PTR_ERR(ret);
+
+       return ret == NULL ? 0 : -EEXIST;
 }
 
 /**
@@ -704,11 +718,15 @@ static inline int rhashtable_lookup_insert_fast(
        const struct rhashtable_params params)
 {
        const char *key = rht_obj(ht, obj);
+       void *ret;
 
        BUG_ON(ht->p.obj_hashfn);
 
-       return __rhashtable_insert_fast(ht, key + ht->p.key_offset, obj,
-                                       params);
+       ret = __rhashtable_insert_fast(ht, key + ht->p.key_offset, obj, params);
+       if (IS_ERR(ret))
+               return PTR_ERR(ret);
+
+       return ret == NULL ? 0 : -EEXIST;
 }
 
 /**
@@ -736,6 +754,32 @@ static inline int rhashtable_lookup_insert_fast(
 static inline int rhashtable_lookup_insert_key(
        struct rhashtable *ht, const void *key, struct rhash_head *obj,
        const struct rhashtable_params params)
+{
+       void *ret;
+
+       BUG_ON(!ht->p.obj_hashfn || !key);
+
+       ret = __rhashtable_insert_fast(ht, key, obj, params);
+       if (IS_ERR(ret))
+               return PTR_ERR(ret);
+
+       return ret == NULL ? 0 : -EEXIST;
+}
+
+/**
+ * rhashtable_lookup_get_insert_key - lookup and insert object into hash table
+ * @ht:                hash table
+ * @obj:       pointer to hash head inside object
+ * @params:    hash table parameters
+ * @data:      pointer to element data already in hashes
+ *
+ * Just like rhashtable_lookup_insert_key(), but this function returns the
+ * object if it exists, NULL if it does not and the insertion was successful,
+ * and an ERR_PTR otherwise.
+ */
+static inline void *rhashtable_lookup_get_insert_key(
+       struct rhashtable *ht, const void *key, struct rhash_head *obj,
+       const struct rhashtable_params params)
 {
        BUG_ON(!ht->p.obj_hashfn || !key);