diff options
-rw-r--r-- | drivers/char/tty_ldisc.c | 65 |
1 files changed, 39 insertions, 26 deletions
diff --git a/drivers/char/tty_ldisc.c b/drivers/char/tty_ldisc.c index e48af9f79219..cbfacc0bbea5 100644 --- a/drivers/char/tty_ldisc.c +++ b/drivers/char/tty_ldisc.c | |||
@@ -145,6 +145,34 @@ int tty_unregister_ldisc(int disc) | |||
145 | } | 145 | } |
146 | EXPORT_SYMBOL(tty_unregister_ldisc); | 146 | EXPORT_SYMBOL(tty_unregister_ldisc); |
147 | 147 | ||
148 | static struct tty_ldisc_ops *get_ldops(int disc) | ||
149 | { | ||
150 | unsigned long flags; | ||
151 | struct tty_ldisc_ops *ldops, *ret; | ||
152 | |||
153 | spin_lock_irqsave(&tty_ldisc_lock, flags); | ||
154 | ret = ERR_PTR(-EINVAL); | ||
155 | ldops = tty_ldiscs[disc]; | ||
156 | if (ldops) { | ||
157 | ret = ERR_PTR(-EAGAIN); | ||
158 | if (try_module_get(ldops->owner)) { | ||
159 | ldops->refcount++; | ||
160 | ret = ldops; | ||
161 | } | ||
162 | } | ||
163 | spin_unlock_irqrestore(&tty_ldisc_lock, flags); | ||
164 | return ret; | ||
165 | } | ||
166 | |||
167 | static void put_ldops(struct tty_ldisc_ops *ldops) | ||
168 | { | ||
169 | unsigned long flags; | ||
170 | |||
171 | spin_lock_irqsave(&tty_ldisc_lock, flags); | ||
172 | ldops->refcount--; | ||
173 | module_put(ldops->owner); | ||
174 | spin_unlock_irqrestore(&tty_ldisc_lock, flags); | ||
175 | } | ||
148 | 176 | ||
149 | /** | 177 | /** |
150 | * tty_ldisc_try_get - try and reference an ldisc | 178 | * tty_ldisc_try_get - try and reference an ldisc |
@@ -156,36 +184,21 @@ EXPORT_SYMBOL(tty_unregister_ldisc); | |||
156 | 184 | ||
157 | static struct tty_ldisc *tty_ldisc_try_get(int disc) | 185 | static struct tty_ldisc *tty_ldisc_try_get(int disc) |
158 | { | 186 | { |
159 | unsigned long flags; | ||
160 | struct tty_ldisc *ld; | 187 | struct tty_ldisc *ld; |
161 | struct tty_ldisc_ops *ldops; | 188 | struct tty_ldisc_ops *ldops; |
162 | int err = -EINVAL; | ||
163 | 189 | ||
164 | ld = kmalloc(sizeof(struct tty_ldisc), GFP_KERNEL); | 190 | ld = kmalloc(sizeof(struct tty_ldisc), GFP_KERNEL); |
165 | if (ld == NULL) | 191 | if (ld == NULL) |
166 | return ERR_PTR(-ENOMEM); | 192 | return ERR_PTR(-ENOMEM); |
167 | 193 | ||
168 | spin_lock_irqsave(&tty_ldisc_lock, flags); | 194 | ldops = get_ldops(disc); |
169 | ld->ops = NULL; | 195 | if (IS_ERR(ldops)) { |
170 | ldops = tty_ldiscs[disc]; | ||
171 | /* Check the entry is defined */ | ||
172 | if (ldops) { | ||
173 | /* If the module is being unloaded we can't use it */ | ||
174 | if (!try_module_get(ldops->owner)) | ||
175 | err = -EAGAIN; | ||
176 | else { | ||
177 | /* lock it */ | ||
178 | ldops->refcount++; | ||
179 | ld->ops = ldops; | ||
180 | atomic_set(&ld->users, 1); | ||
181 | err = 0; | ||
182 | } | ||
183 | } | ||
184 | spin_unlock_irqrestore(&tty_ldisc_lock, flags); | ||
185 | if (err) { | ||
186 | kfree(ld); | 196 | kfree(ld); |
187 | return ERR_PTR(err); | 197 | return ERR_CAST(ldops); |
188 | } | 198 | } |
199 | |||
200 | ld->ops = ldops; | ||
201 | atomic_set(&ld->users, 1); | ||
189 | return ld; | 202 | return ld; |
190 | } | 203 | } |
191 | 204 | ||
@@ -234,13 +247,13 @@ static void tty_ldiscs_seq_stop(struct seq_file *m, void *v) | |||
234 | static int tty_ldiscs_seq_show(struct seq_file *m, void *v) | 247 | static int tty_ldiscs_seq_show(struct seq_file *m, void *v) |
235 | { | 248 | { |
236 | int i = *(loff_t *)v; | 249 | int i = *(loff_t *)v; |
237 | struct tty_ldisc *ld; | 250 | struct tty_ldisc_ops *ldops; |
238 | 251 | ||
239 | ld = tty_ldisc_try_get(i); | 252 | ldops = get_ldops(i); |
240 | if (IS_ERR(ld)) | 253 | if (IS_ERR(ldops)) |
241 | return 0; | 254 | return 0; |
242 | seq_printf(m, "%-10s %2d\n", ld->ops->name ? ld->ops->name : "???", i); | 255 | seq_printf(m, "%-10s %2d\n", ldops->name ? ldops->name : "???", i); |
243 | put_ldisc(ld); | 256 | put_ldops(ldops); |
244 | return 0; | 257 | return 0; |
245 | } | 258 | } |
246 | 259 | ||