if (!kv)
return NULL;
+ atomic_set(&kv->refcnt, 1);
kv->key.type = type;
kv->key.id = key;
-
kv->associate = NULL;
- kv->refcnt = 1;
-
kv->next = NULL;
kv->prev = NULL;
kv->offset = 0;
if (kv->associate)
csr1212_release_keyval(kv->associate);
- associate->refcnt++;
+ csr1212_keep_keyval(associate);
kv->associate = associate;
}
-int csr1212_attach_keyval_to_directory(struct csr1212_keyval *dir,
- struct csr1212_keyval *kv)
+static int __csr1212_attach_keyval_to_directory(struct csr1212_keyval *dir,
+ struct csr1212_keyval *kv,
+ bool keep_keyval)
{
struct csr1212_dentry *dentry;
if (!dentry)
return -ENOMEM;
+ if (keep_keyval)
+ csr1212_keep_keyval(kv);
dentry->kv = kv;
- kv->refcnt++;
-
dentry->next = NULL;
dentry->prev = dir->value.directory.dentries_tail;
return CSR1212_SUCCESS;
}
+int csr1212_attach_keyval_to_directory(struct csr1212_keyval *dir,
+ struct csr1212_keyval *kv)
+{
+ return __csr1212_attach_keyval_to_directory(dir, kv, true);
+}
+
#define CSR1212_DESCRIPTOR_LEAF_DATA(kv) \
(&((kv)->value.leaf.data[1]))
/* This function is used to free the memory taken by a keyval. If the given
* keyval is a directory type, then any keyvals contained in that directory
- * will be destroyed as well if their respective refcnts are 0. By means of
+ * will be destroyed as well if noone holds a reference on them. By means of
* list manipulation, this routine will descend a directory structure in a
* non-recursive manner. */
-static void csr1212_destroy_keyval(struct csr1212_keyval *kv)
+void csr1212_release_keyval(struct csr1212_keyval *kv)
{
struct csr1212_keyval *k, *a;
struct csr1212_dentry dentry;
struct csr1212_dentry *head, *tail;
+ if (!atomic_dec_and_test(&kv->refcnt))
+ return;
+
dentry.kv = kv;
dentry.next = NULL;
dentry.prev = NULL;
k = head->kv;
while (k) {
- k->refcnt--;
-
- if (k->refcnt > 0)
+ /* must not dec_and_test kv->refcnt again */
+ if (k != kv && !atomic_dec_and_test(&k->refcnt))
break;
a = k->associate;
}
}
-void csr1212_release_keyval(struct csr1212_keyval *kv)
-{
- if (kv->refcnt > 1)
- kv->refcnt--;
- else
- csr1212_destroy_keyval(kv);
-}
-
void csr1212_destroy_csr(struct csr1212_csr *csr)
{
struct csr1212_csr_rom_cache *c, *oc;
int ret = CSR1212_SUCCESS;
struct csr1212_keyval *k = NULL;
u32 offset;
+ bool keep_keyval = true;
switch (CSR1212_KV_KEY_TYPE(ki)) {
case CSR1212_KV_TYPE_IMMEDIATE:
ret = -ENOMEM;
goto out;
}
-
- k->refcnt = 0; /* Don't keep local reference when parsing. */
+ /* Don't keep local reference when parsing. */
+ keep_keyval = false;
break;
case CSR1212_KV_TYPE_CSR_OFFSET:
ret = -ENOMEM;
goto out;
}
- k->refcnt = 0; /* Don't keep local reference when parsing. */
+ /* Don't keep local reference when parsing. */
+ keep_keyval = false;
break;
default:
ret = -ENOMEM;
goto out;
}
- k->refcnt = 0; /* Don't keep local reference when parsing. */
- k->valid = 0; /* Contents not read yet so it's not valid. */
+ /* Don't keep local reference when parsing. */
+ keep_keyval = false;
+ /* Contents not read yet so it's not valid. */
+ k->valid = 0;
k->offset = offset;
k->prev = dir;
dir->next->prev = k;
dir->next = k;
}
- ret = csr1212_attach_keyval_to_directory(dir, k);
+ ret = __csr1212_attach_keyval_to_directory(dir, k, keep_keyval);
out:
if (ret != CSR1212_SUCCESS && k != NULL)
free_keyval(k);
#include <linux/types.h>
#include <linux/slab.h>
+#include <asm/atomic.h>
#define CSR1212_MALLOC(size) kmalloc((size), GFP_KERNEL)
#define CSR1212_FREE(ptr) kfree(ptr)
struct csr1212_directory directory;
} value;
struct csr1212_keyval *associate;
- int refcnt;
+ atomic_t refcnt;
/* used in generating and/or parsing CSR image */
struct csr1212_keyval *next, *prev; /* flat list of CSR elements */
* need for code to retain a keyval that has been parsed. */
static inline void csr1212_keep_keyval(struct csr1212_keyval *kv)
{
- kv->refcnt++;
+ atomic_inc(&kv->refcnt);
+ smp_mb__after_atomic_inc();
}