]> nv-tegra.nvidia Code Review - linux-2.6.git/blob - drivers/w1/w1_family.c
W1: fix deadlocks and remove w1_control_thread
[linux-2.6.git] / drivers / w1 / w1_family.c
1 /*
2  *      w1_family.c
3  *
4  * Copyright (c) 2004 Evgeniy Polyakov <johnpol@2ka.mipt.ru>
5  *
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20  */
21
22 #include <linux/spinlock.h>
23 #include <linux/list.h>
24 #include <linux/sched.h>        /* schedule_timeout() */
25 #include <linux/delay.h>
26
27 #include "w1_family.h"
28 #include "w1.h"
29
30 DEFINE_SPINLOCK(w1_flock);
31 static LIST_HEAD(w1_families);
32
33 int w1_register_family(struct w1_family *newf)
34 {
35         struct list_head *ent, *n;
36         struct w1_family *f;
37         int ret = 0;
38
39         spin_lock(&w1_flock);
40         list_for_each_safe(ent, n, &w1_families) {
41                 f = list_entry(ent, struct w1_family, family_entry);
42
43                 if (f->fid == newf->fid) {
44                         ret = -EEXIST;
45                         break;
46                 }
47         }
48
49         if (!ret) {
50                 atomic_set(&newf->refcnt, 0);
51                 newf->need_exit = 0;
52                 list_add_tail(&newf->family_entry, &w1_families);
53         }
54         spin_unlock(&w1_flock);
55
56         /* check default devices against the new set of drivers */
57         w1_reconnect_slaves(newf, 1);
58
59         return ret;
60 }
61
62 void w1_unregister_family(struct w1_family *fent)
63 {
64         struct list_head *ent, *n;
65         struct w1_family *f;
66
67         spin_lock(&w1_flock);
68         list_for_each_safe(ent, n, &w1_families) {
69                 f = list_entry(ent, struct w1_family, family_entry);
70
71                 if (f->fid == fent->fid) {
72                         list_del(&fent->family_entry);
73                         break;
74                 }
75         }
76
77         fent->need_exit = 1;
78
79         spin_unlock(&w1_flock);
80
81         /* deatch devices using this family code */
82         w1_reconnect_slaves(fent, 0);
83
84         while (atomic_read(&fent->refcnt)) {
85                 printk(KERN_INFO "Waiting for family %u to become free: refcnt=%d.\n",
86                                 fent->fid, atomic_read(&fent->refcnt));
87
88                 if (msleep_interruptible(1000))
89                         flush_signals(current);
90         }
91 }
92
93 /*
94  * Should be called under w1_flock held.
95  */
96 struct w1_family * w1_family_registered(u8 fid)
97 {
98         struct list_head *ent, *n;
99         struct w1_family *f = NULL;
100         int ret = 0;
101
102         list_for_each_safe(ent, n, &w1_families) {
103                 f = list_entry(ent, struct w1_family, family_entry);
104
105                 if (f->fid == fid) {
106                         ret = 1;
107                         break;
108                 }
109         }
110
111         return (ret) ? f : NULL;
112 }
113
114 static void __w1_family_put(struct w1_family *f)
115 {
116         if (atomic_dec_and_test(&f->refcnt))
117                 f->need_exit = 1;
118 }
119
120 void w1_family_put(struct w1_family *f)
121 {
122         spin_lock(&w1_flock);
123         __w1_family_put(f);
124         spin_unlock(&w1_flock);
125 }
126
127 #if 0
128 void w1_family_get(struct w1_family *f)
129 {
130         spin_lock(&w1_flock);
131         __w1_family_get(f);
132         spin_unlock(&w1_flock);
133 }
134 #endif  /*  0  */
135
136 void __w1_family_get(struct w1_family *f)
137 {
138         smp_mb__before_atomic_inc();
139         atomic_inc(&f->refcnt);
140         smp_mb__after_atomic_inc();
141 }
142
143 EXPORT_SYMBOL(w1_unregister_family);
144 EXPORT_SYMBOL(w1_register_family);