ieee1394: csr1212: proper refcounting
At least since nodemgr got rid of coarse global locking, accesses to struct csr1212_keyval's reference counter should be atomic and coupled with proper barriers. Also, calls to csr1212_keep_keyval(kv) should occur before kv is being used. (We probably should convert refcnt to struct kref, but how to keep csr1212_destroy_keyval's implementation non-recursively then?) Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
This commit is contained in:
parent
638d5bb816
commit
17a19b795e
3 changed files with 36 additions and 31 deletions
|
@ -218,12 +218,10 @@ static struct csr1212_keyval *csr1212_new_keyval(u8 type, u8 key)
|
||||||
if (!kv)
|
if (!kv)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
atomic_set(&kv->refcnt, 1);
|
||||||
kv->key.type = type;
|
kv->key.type = type;
|
||||||
kv->key.id = key;
|
kv->key.id = key;
|
||||||
|
|
||||||
kv->associate = NULL;
|
kv->associate = NULL;
|
||||||
kv->refcnt = 1;
|
|
||||||
|
|
||||||
kv->next = NULL;
|
kv->next = NULL;
|
||||||
kv->prev = NULL;
|
kv->prev = NULL;
|
||||||
kv->offset = 0;
|
kv->offset = 0;
|
||||||
|
@ -326,12 +324,13 @@ void csr1212_associate_keyval(struct csr1212_keyval *kv,
|
||||||
if (kv->associate)
|
if (kv->associate)
|
||||||
csr1212_release_keyval(kv->associate);
|
csr1212_release_keyval(kv->associate);
|
||||||
|
|
||||||
associate->refcnt++;
|
csr1212_keep_keyval(associate);
|
||||||
kv->associate = associate;
|
kv->associate = associate;
|
||||||
}
|
}
|
||||||
|
|
||||||
int csr1212_attach_keyval_to_directory(struct csr1212_keyval *dir,
|
static int __csr1212_attach_keyval_to_directory(struct csr1212_keyval *dir,
|
||||||
struct csr1212_keyval *kv)
|
struct csr1212_keyval *kv,
|
||||||
|
bool keep_keyval)
|
||||||
{
|
{
|
||||||
struct csr1212_dentry *dentry;
|
struct csr1212_dentry *dentry;
|
||||||
|
|
||||||
|
@ -341,10 +340,10 @@ int csr1212_attach_keyval_to_directory(struct csr1212_keyval *dir,
|
||||||
if (!dentry)
|
if (!dentry)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
if (keep_keyval)
|
||||||
|
csr1212_keep_keyval(kv);
|
||||||
dentry->kv = kv;
|
dentry->kv = kv;
|
||||||
|
|
||||||
kv->refcnt++;
|
|
||||||
|
|
||||||
dentry->next = NULL;
|
dentry->next = NULL;
|
||||||
dentry->prev = dir->value.directory.dentries_tail;
|
dentry->prev = dir->value.directory.dentries_tail;
|
||||||
|
|
||||||
|
@ -358,6 +357,12 @@ int csr1212_attach_keyval_to_directory(struct csr1212_keyval *dir,
|
||||||
return CSR1212_SUCCESS;
|
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) \
|
#define CSR1212_DESCRIPTOR_LEAF_DATA(kv) \
|
||||||
(&((kv)->value.leaf.data[1]))
|
(&((kv)->value.leaf.data[1]))
|
||||||
|
|
||||||
|
@ -483,15 +488,18 @@ void csr1212_detach_keyval_from_directory(struct csr1212_keyval *dir,
|
||||||
|
|
||||||
/* This function is used to free the memory taken by a keyval. If the given
|
/* 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
|
* 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
|
* list manipulation, this routine will descend a directory structure in a
|
||||||
* non-recursive manner. */
|
* 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_keyval *k, *a;
|
||||||
struct csr1212_dentry dentry;
|
struct csr1212_dentry dentry;
|
||||||
struct csr1212_dentry *head, *tail;
|
struct csr1212_dentry *head, *tail;
|
||||||
|
|
||||||
|
if (!atomic_dec_and_test(&kv->refcnt))
|
||||||
|
return;
|
||||||
|
|
||||||
dentry.kv = kv;
|
dentry.kv = kv;
|
||||||
dentry.next = NULL;
|
dentry.next = NULL;
|
||||||
dentry.prev = NULL;
|
dentry.prev = NULL;
|
||||||
|
@ -503,9 +511,8 @@ static void csr1212_destroy_keyval(struct csr1212_keyval *kv)
|
||||||
k = head->kv;
|
k = head->kv;
|
||||||
|
|
||||||
while (k) {
|
while (k) {
|
||||||
k->refcnt--;
|
/* must not dec_and_test kv->refcnt again */
|
||||||
|
if (k != kv && !atomic_dec_and_test(&k->refcnt))
|
||||||
if (k->refcnt > 0)
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
a = k->associate;
|
a = k->associate;
|
||||||
|
@ -536,14 +543,6 @@ static void csr1212_destroy_keyval(struct csr1212_keyval *kv)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
void csr1212_destroy_csr(struct csr1212_csr *csr)
|
||||||
{
|
{
|
||||||
struct csr1212_csr_rom_cache *c, *oc;
|
struct csr1212_csr_rom_cache *c, *oc;
|
||||||
|
@ -1126,6 +1125,7 @@ csr1212_parse_dir_entry(struct csr1212_keyval *dir, u32 ki, u32 kv_pos)
|
||||||
int ret = CSR1212_SUCCESS;
|
int ret = CSR1212_SUCCESS;
|
||||||
struct csr1212_keyval *k = NULL;
|
struct csr1212_keyval *k = NULL;
|
||||||
u32 offset;
|
u32 offset;
|
||||||
|
bool keep_keyval = true;
|
||||||
|
|
||||||
switch (CSR1212_KV_KEY_TYPE(ki)) {
|
switch (CSR1212_KV_KEY_TYPE(ki)) {
|
||||||
case CSR1212_KV_TYPE_IMMEDIATE:
|
case CSR1212_KV_TYPE_IMMEDIATE:
|
||||||
|
@ -1135,8 +1135,8 @@ csr1212_parse_dir_entry(struct csr1212_keyval *dir, u32 ki, u32 kv_pos)
|
||||||
ret = -ENOMEM;
|
ret = -ENOMEM;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
/* Don't keep local reference when parsing. */
|
||||||
k->refcnt = 0; /* Don't keep local reference when parsing. */
|
keep_keyval = false;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CSR1212_KV_TYPE_CSR_OFFSET:
|
case CSR1212_KV_TYPE_CSR_OFFSET:
|
||||||
|
@ -1146,7 +1146,8 @@ csr1212_parse_dir_entry(struct csr1212_keyval *dir, u32 ki, u32 kv_pos)
|
||||||
ret = -ENOMEM;
|
ret = -ENOMEM;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
k->refcnt = 0; /* Don't keep local reference when parsing. */
|
/* Don't keep local reference when parsing. */
|
||||||
|
keep_keyval = false;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -1174,8 +1175,10 @@ csr1212_parse_dir_entry(struct csr1212_keyval *dir, u32 ki, u32 kv_pos)
|
||||||
ret = -ENOMEM;
|
ret = -ENOMEM;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
k->refcnt = 0; /* Don't keep local reference when parsing. */
|
/* Don't keep local reference when parsing. */
|
||||||
k->valid = 0; /* Contents not read yet so it's not valid. */
|
keep_keyval = false;
|
||||||
|
/* Contents not read yet so it's not valid. */
|
||||||
|
k->valid = 0;
|
||||||
k->offset = offset;
|
k->offset = offset;
|
||||||
|
|
||||||
k->prev = dir;
|
k->prev = dir;
|
||||||
|
@ -1183,7 +1186,7 @@ csr1212_parse_dir_entry(struct csr1212_keyval *dir, u32 ki, u32 kv_pos)
|
||||||
dir->next->prev = k;
|
dir->next->prev = k;
|
||||||
dir->next = k;
|
dir->next = k;
|
||||||
}
|
}
|
||||||
ret = csr1212_attach_keyval_to_directory(dir, k);
|
ret = __csr1212_attach_keyval_to_directory(dir, k, keep_keyval);
|
||||||
out:
|
out:
|
||||||
if (ret != CSR1212_SUCCESS && k != NULL)
|
if (ret != CSR1212_SUCCESS && k != NULL)
|
||||||
free_keyval(k);
|
free_keyval(k);
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
|
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
#include <asm/atomic.h>
|
||||||
|
|
||||||
#define CSR1212_MALLOC(size) kmalloc((size), GFP_KERNEL)
|
#define CSR1212_MALLOC(size) kmalloc((size), GFP_KERNEL)
|
||||||
#define CSR1212_FREE(ptr) kfree(ptr)
|
#define CSR1212_FREE(ptr) kfree(ptr)
|
||||||
|
@ -149,7 +150,7 @@ struct csr1212_keyval {
|
||||||
struct csr1212_directory directory;
|
struct csr1212_directory directory;
|
||||||
} value;
|
} value;
|
||||||
struct csr1212_keyval *associate;
|
struct csr1212_keyval *associate;
|
||||||
int refcnt;
|
atomic_t refcnt;
|
||||||
|
|
||||||
/* used in generating and/or parsing CSR image */
|
/* used in generating and/or parsing CSR image */
|
||||||
struct csr1212_keyval *next, *prev; /* flat list of CSR elements */
|
struct csr1212_keyval *next, *prev; /* flat list of CSR elements */
|
||||||
|
@ -350,7 +351,8 @@ csr1212_get_keyval(struct csr1212_csr *csr, struct csr1212_keyval *kv);
|
||||||
* need for code to retain a keyval that has been parsed. */
|
* need for code to retain a keyval that has been parsed. */
|
||||||
static inline void csr1212_keep_keyval(struct csr1212_keyval *kv)
|
static inline void csr1212_keep_keyval(struct csr1212_keyval *kv)
|
||||||
{
|
{
|
||||||
kv->refcnt++;
|
atomic_inc(&kv->refcnt);
|
||||||
|
smp_mb__after_atomic_inc();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1014,13 +1014,13 @@ static struct unit_directory *nodemgr_process_unit_directory
|
||||||
CSR1212_TEXTUAL_DESCRIPTOR_LEAF_LANGUAGE(kv) == 0) {
|
CSR1212_TEXTUAL_DESCRIPTOR_LEAF_LANGUAGE(kv) == 0) {
|
||||||
switch (last_key_id) {
|
switch (last_key_id) {
|
||||||
case CSR1212_KV_ID_VENDOR:
|
case CSR1212_KV_ID_VENDOR:
|
||||||
ud->vendor_name_kv = kv;
|
|
||||||
csr1212_keep_keyval(kv);
|
csr1212_keep_keyval(kv);
|
||||||
|
ud->vendor_name_kv = kv;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CSR1212_KV_ID_MODEL:
|
case CSR1212_KV_ID_MODEL:
|
||||||
ud->model_name_kv = kv;
|
|
||||||
csr1212_keep_keyval(kv);
|
csr1212_keep_keyval(kv);
|
||||||
|
ud->model_name_kv = kv;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue