diff options
author | Stefan Richter <stefanr@s5r6.in-berlin.de> | 2007-09-15 08:50:25 -0400 |
---|---|---|
committer | Stefan Richter <stefanr@s5r6.in-berlin.de> | 2007-10-16 17:59:59 -0400 |
commit | 17a19b795e9187d65b6e45cb22797725d50f7edb (patch) | |
tree | 60db3734335f3956378063c2f8126c036b83bfed | |
parent | 638d5bb8167c2c88552257d5af23f7f65ab4defd (diff) |
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>
-rw-r--r-- | drivers/ieee1394/csr1212.c | 57 | ||||
-rw-r--r-- | drivers/ieee1394/csr1212.h | 6 | ||||
-rw-r--r-- | drivers/ieee1394/nodemgr.c | 4 |
3 files changed, 36 insertions, 31 deletions
diff --git a/drivers/ieee1394/csr1212.c b/drivers/ieee1394/csr1212.c index d08166bda1c5..e8122def164d 100644 --- a/drivers/ieee1394/csr1212.c +++ b/drivers/ieee1394/csr1212.c | |||
@@ -218,12 +218,10 @@ static struct csr1212_keyval *csr1212_new_keyval(u8 type, u8 key) | |||
218 | if (!kv) | 218 | if (!kv) |
219 | return NULL; | 219 | return NULL; |
220 | 220 | ||
221 | atomic_set(&kv->refcnt, 1); | ||
221 | kv->key.type = type; | 222 | kv->key.type = type; |
222 | kv->key.id = key; | 223 | kv->key.id = key; |
223 | |||
224 | kv->associate = NULL; | 224 | kv->associate = NULL; |
225 | kv->refcnt = 1; | ||
226 | |||
227 | kv->next = NULL; | 225 | kv->next = NULL; |
228 | kv->prev = NULL; | 226 | kv->prev = NULL; |
229 | kv->offset = 0; | 227 | kv->offset = 0; |
@@ -326,12 +324,13 @@ void csr1212_associate_keyval(struct csr1212_keyval *kv, | |||
326 | if (kv->associate) | 324 | if (kv->associate) |
327 | csr1212_release_keyval(kv->associate); | 325 | csr1212_release_keyval(kv->associate); |
328 | 326 | ||
329 | associate->refcnt++; | 327 | csr1212_keep_keyval(associate); |
330 | kv->associate = associate; | 328 | kv->associate = associate; |
331 | } | 329 | } |
332 | 330 | ||
333 | int csr1212_attach_keyval_to_directory(struct csr1212_keyval *dir, | 331 | static int __csr1212_attach_keyval_to_directory(struct csr1212_keyval *dir, |
334 | struct csr1212_keyval *kv) | 332 | struct csr1212_keyval *kv, |
333 | bool keep_keyval) | ||
335 | { | 334 | { |
336 | struct csr1212_dentry *dentry; | 335 | struct csr1212_dentry *dentry; |
337 | 336 | ||
@@ -341,10 +340,10 @@ int csr1212_attach_keyval_to_directory(struct csr1212_keyval *dir, | |||
341 | if (!dentry) | 340 | if (!dentry) |
342 | return -ENOMEM; | 341 | return -ENOMEM; |
343 | 342 | ||
343 | if (keep_keyval) | ||
344 | csr1212_keep_keyval(kv); | ||
344 | dentry->kv = kv; | 345 | dentry->kv = kv; |
345 | 346 | ||
346 | kv->refcnt++; | ||
347 | |||
348 | dentry->next = NULL; | 347 | dentry->next = NULL; |
349 | dentry->prev = dir->value.directory.dentries_tail; | 348 | dentry->prev = dir->value.directory.dentries_tail; |
350 | 349 | ||
@@ -358,6 +357,12 @@ int csr1212_attach_keyval_to_directory(struct csr1212_keyval *dir, | |||
358 | return CSR1212_SUCCESS; | 357 | return CSR1212_SUCCESS; |
359 | } | 358 | } |
360 | 359 | ||
360 | int csr1212_attach_keyval_to_directory(struct csr1212_keyval *dir, | ||
361 | struct csr1212_keyval *kv) | ||
362 | { | ||
363 | return __csr1212_attach_keyval_to_directory(dir, kv, true); | ||
364 | } | ||
365 | |||
361 | #define CSR1212_DESCRIPTOR_LEAF_DATA(kv) \ | 366 | #define CSR1212_DESCRIPTOR_LEAF_DATA(kv) \ |
362 | (&((kv)->value.leaf.data[1])) | 367 | (&((kv)->value.leaf.data[1])) |
363 | 368 | ||
@@ -483,15 +488,18 @@ void csr1212_detach_keyval_from_directory(struct csr1212_keyval *dir, | |||
483 | 488 | ||
484 | /* This function is used to free the memory taken by a keyval. If the given | 489 | /* This function is used to free the memory taken by a keyval. If the given |
485 | * keyval is a directory type, then any keyvals contained in that directory | 490 | * keyval is a directory type, then any keyvals contained in that directory |
486 | * will be destroyed as well if their respective refcnts are 0. By means of | 491 | * will be destroyed as well if noone holds a reference on them. By means of |
487 | * list manipulation, this routine will descend a directory structure in a | 492 | * list manipulation, this routine will descend a directory structure in a |
488 | * non-recursive manner. */ | 493 | * non-recursive manner. */ |
489 | static void csr1212_destroy_keyval(struct csr1212_keyval *kv) | 494 | void csr1212_release_keyval(struct csr1212_keyval *kv) |
490 | { | 495 | { |
491 | struct csr1212_keyval *k, *a; | 496 | struct csr1212_keyval *k, *a; |
492 | struct csr1212_dentry dentry; | 497 | struct csr1212_dentry dentry; |
493 | struct csr1212_dentry *head, *tail; | 498 | struct csr1212_dentry *head, *tail; |
494 | 499 | ||
500 | if (!atomic_dec_and_test(&kv->refcnt)) | ||
501 | return; | ||
502 | |||
495 | dentry.kv = kv; | 503 | dentry.kv = kv; |
496 | dentry.next = NULL; | 504 | dentry.next = NULL; |
497 | dentry.prev = NULL; | 505 | dentry.prev = NULL; |
@@ -503,9 +511,8 @@ static void csr1212_destroy_keyval(struct csr1212_keyval *kv) | |||
503 | k = head->kv; | 511 | k = head->kv; |
504 | 512 | ||
505 | while (k) { | 513 | while (k) { |
506 | k->refcnt--; | 514 | /* must not dec_and_test kv->refcnt again */ |
507 | 515 | if (k != kv && !atomic_dec_and_test(&k->refcnt)) | |
508 | if (k->refcnt > 0) | ||
509 | break; | 516 | break; |
510 | 517 | ||
511 | a = k->associate; | 518 | a = k->associate; |
@@ -536,14 +543,6 @@ static void csr1212_destroy_keyval(struct csr1212_keyval *kv) | |||
536 | } | 543 | } |
537 | } | 544 | } |
538 | 545 | ||
539 | void csr1212_release_keyval(struct csr1212_keyval *kv) | ||
540 | { | ||
541 | if (kv->refcnt > 1) | ||
542 | kv->refcnt--; | ||
543 | else | ||
544 | csr1212_destroy_keyval(kv); | ||
545 | } | ||
546 | |||
547 | void csr1212_destroy_csr(struct csr1212_csr *csr) | 546 | void csr1212_destroy_csr(struct csr1212_csr *csr) |
548 | { | 547 | { |
549 | struct csr1212_csr_rom_cache *c, *oc; | 548 | struct csr1212_csr_rom_cache *c, *oc; |
@@ -1126,6 +1125,7 @@ csr1212_parse_dir_entry(struct csr1212_keyval *dir, u32 ki, u32 kv_pos) | |||
1126 | int ret = CSR1212_SUCCESS; | 1125 | int ret = CSR1212_SUCCESS; |
1127 | struct csr1212_keyval *k = NULL; | 1126 | struct csr1212_keyval *k = NULL; |
1128 | u32 offset; | 1127 | u32 offset; |
1128 | bool keep_keyval = true; | ||
1129 | 1129 | ||
1130 | switch (CSR1212_KV_KEY_TYPE(ki)) { | 1130 | switch (CSR1212_KV_KEY_TYPE(ki)) { |
1131 | case CSR1212_KV_TYPE_IMMEDIATE: | 1131 | case CSR1212_KV_TYPE_IMMEDIATE: |
@@ -1135,8 +1135,8 @@ csr1212_parse_dir_entry(struct csr1212_keyval *dir, u32 ki, u32 kv_pos) | |||
1135 | ret = -ENOMEM; | 1135 | ret = -ENOMEM; |
1136 | goto out; | 1136 | goto out; |
1137 | } | 1137 | } |
1138 | 1138 | /* Don't keep local reference when parsing. */ | |
1139 | k->refcnt = 0; /* Don't keep local reference when parsing. */ | 1139 | keep_keyval = false; |
1140 | break; | 1140 | break; |
1141 | 1141 | ||
1142 | case CSR1212_KV_TYPE_CSR_OFFSET: | 1142 | case CSR1212_KV_TYPE_CSR_OFFSET: |
@@ -1146,7 +1146,8 @@ csr1212_parse_dir_entry(struct csr1212_keyval *dir, u32 ki, u32 kv_pos) | |||
1146 | ret = -ENOMEM; | 1146 | ret = -ENOMEM; |
1147 | goto out; | 1147 | goto out; |
1148 | } | 1148 | } |
1149 | k->refcnt = 0; /* Don't keep local reference when parsing. */ | 1149 | /* Don't keep local reference when parsing. */ |
1150 | keep_keyval = false; | ||
1150 | break; | 1151 | break; |
1151 | 1152 | ||
1152 | default: | 1153 | default: |
@@ -1174,8 +1175,10 @@ csr1212_parse_dir_entry(struct csr1212_keyval *dir, u32 ki, u32 kv_pos) | |||
1174 | ret = -ENOMEM; | 1175 | ret = -ENOMEM; |
1175 | goto out; | 1176 | goto out; |
1176 | } | 1177 | } |
1177 | k->refcnt = 0; /* Don't keep local reference when parsing. */ | 1178 | /* Don't keep local reference when parsing. */ |
1178 | k->valid = 0; /* Contents not read yet so it's not valid. */ | 1179 | keep_keyval = false; |
1180 | /* Contents not read yet so it's not valid. */ | ||
1181 | k->valid = 0; | ||
1179 | k->offset = offset; | 1182 | k->offset = offset; |
1180 | 1183 | ||
1181 | k->prev = dir; | 1184 | k->prev = dir; |
@@ -1183,7 +1186,7 @@ csr1212_parse_dir_entry(struct csr1212_keyval *dir, u32 ki, u32 kv_pos) | |||
1183 | dir->next->prev = k; | 1186 | dir->next->prev = k; |
1184 | dir->next = k; | 1187 | dir->next = k; |
1185 | } | 1188 | } |
1186 | ret = csr1212_attach_keyval_to_directory(dir, k); | 1189 | ret = __csr1212_attach_keyval_to_directory(dir, k, keep_keyval); |
1187 | out: | 1190 | out: |
1188 | if (ret != CSR1212_SUCCESS && k != NULL) | 1191 | if (ret != CSR1212_SUCCESS && k != NULL) |
1189 | free_keyval(k); | 1192 | free_keyval(k); |
diff --git a/drivers/ieee1394/csr1212.h b/drivers/ieee1394/csr1212.h index df909ce66304..043039fc63ec 100644 --- a/drivers/ieee1394/csr1212.h +++ b/drivers/ieee1394/csr1212.h | |||
@@ -32,6 +32,7 @@ | |||
32 | 32 | ||
33 | #include <linux/types.h> | 33 | #include <linux/types.h> |
34 | #include <linux/slab.h> | 34 | #include <linux/slab.h> |
35 | #include <asm/atomic.h> | ||
35 | 36 | ||
36 | #define CSR1212_MALLOC(size) kmalloc((size), GFP_KERNEL) | 37 | #define CSR1212_MALLOC(size) kmalloc((size), GFP_KERNEL) |
37 | #define CSR1212_FREE(ptr) kfree(ptr) | 38 | #define CSR1212_FREE(ptr) kfree(ptr) |
@@ -149,7 +150,7 @@ struct csr1212_keyval { | |||
149 | struct csr1212_directory directory; | 150 | struct csr1212_directory directory; |
150 | } value; | 151 | } value; |
151 | struct csr1212_keyval *associate; | 152 | struct csr1212_keyval *associate; |
152 | int refcnt; | 153 | atomic_t refcnt; |
153 | 154 | ||
154 | /* used in generating and/or parsing CSR image */ | 155 | /* used in generating and/or parsing CSR image */ |
155 | struct csr1212_keyval *next, *prev; /* flat list of CSR elements */ | 156 | 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); | |||
350 | * need for code to retain a keyval that has been parsed. */ | 351 | * need for code to retain a keyval that has been parsed. */ |
351 | static inline void csr1212_keep_keyval(struct csr1212_keyval *kv) | 352 | static inline void csr1212_keep_keyval(struct csr1212_keyval *kv) |
352 | { | 353 | { |
353 | kv->refcnt++; | 354 | atomic_inc(&kv->refcnt); |
355 | smp_mb__after_atomic_inc(); | ||
354 | } | 356 | } |
355 | 357 | ||
356 | 358 | ||
diff --git a/drivers/ieee1394/nodemgr.c b/drivers/ieee1394/nodemgr.c index ec8edd2f19de..90dc75be3418 100644 --- a/drivers/ieee1394/nodemgr.c +++ b/drivers/ieee1394/nodemgr.c | |||
@@ -1014,13 +1014,13 @@ static struct unit_directory *nodemgr_process_unit_directory | |||
1014 | CSR1212_TEXTUAL_DESCRIPTOR_LEAF_LANGUAGE(kv) == 0) { | 1014 | CSR1212_TEXTUAL_DESCRIPTOR_LEAF_LANGUAGE(kv) == 0) { |
1015 | switch (last_key_id) { | 1015 | switch (last_key_id) { |
1016 | case CSR1212_KV_ID_VENDOR: | 1016 | case CSR1212_KV_ID_VENDOR: |
1017 | ud->vendor_name_kv = kv; | ||
1018 | csr1212_keep_keyval(kv); | 1017 | csr1212_keep_keyval(kv); |
1018 | ud->vendor_name_kv = kv; | ||
1019 | break; | 1019 | break; |
1020 | 1020 | ||
1021 | case CSR1212_KV_ID_MODEL: | 1021 | case CSR1212_KV_ID_MODEL: |
1022 | ud->model_name_kv = kv; | ||
1023 | csr1212_keep_keyval(kv); | 1022 | csr1212_keep_keyval(kv); |
1023 | ud->model_name_kv = kv; | ||
1024 | break; | 1024 | break; |
1025 | 1025 | ||
1026 | } | 1026 | } |