diff options
author | Jiri Slaby <jirislaby@gmail.com> | 2006-12-29 19:48:00 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.osdl.org> | 2006-12-30 13:55:55 -0500 |
commit | cfe7c09ac2be2a89aa46bb23d480d9d908e8c041 (patch) | |
tree | f05d110db6f6dc595e0e672b99397d3f2ea6d6e8 /drivers/char/isicom.c | |
parent | 10f549fa1538849548787879d96bbb3450f06117 (diff) |
[PATCH] Char: isicom, eliminate spinlock recursion
Many spinlock recursion was in the isicom driver. Eliminate it.
Signed-off-by: Jiri Slaby <jirislaby@gmail.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'drivers/char/isicom.c')
-rw-r--r-- | drivers/char/isicom.c | 103 |
1 files changed, 51 insertions, 52 deletions
diff --git a/drivers/char/isicom.c b/drivers/char/isicom.c index 5a747e685993..01084abffddf 100644 --- a/drivers/char/isicom.c +++ b/drivers/char/isicom.c | |||
@@ -230,6 +230,20 @@ static struct isi_port isi_ports[PORT_COUNT]; | |||
230 | * it wants to talk. | 230 | * it wants to talk. |
231 | */ | 231 | */ |
232 | 232 | ||
233 | static inline int WaitTillCardIsFree(u16 base) | ||
234 | { | ||
235 | unsigned int count = 0; | ||
236 | unsigned int a = in_atomic(); /* do we run under spinlock? */ | ||
237 | |||
238 | while (!(inw(base + 0xe) & 0x1) && count++ < 100) | ||
239 | if (a) | ||
240 | mdelay(1); | ||
241 | else | ||
242 | msleep(1); | ||
243 | |||
244 | return !(inw(base + 0xe) & 0x1); | ||
245 | } | ||
246 | |||
233 | static int lock_card(struct isi_board *card) | 247 | static int lock_card(struct isi_board *card) |
234 | { | 248 | { |
235 | char retries; | 249 | char retries; |
@@ -276,69 +290,71 @@ static void unlock_card(struct isi_board *card) | |||
276 | * ISI Card specific ops ... | 290 | * ISI Card specific ops ... |
277 | */ | 291 | */ |
278 | 292 | ||
293 | /* card->lock HAS to be held */ | ||
279 | static void raise_dtr(struct isi_port *port) | 294 | static void raise_dtr(struct isi_port *port) |
280 | { | 295 | { |
281 | struct isi_board *card = port->card; | 296 | struct isi_board *card = port->card; |
282 | unsigned long base = card->base; | 297 | unsigned long base = card->base; |
283 | u16 channel = port->channel; | 298 | u16 channel = port->channel; |
284 | 299 | ||
285 | if (!lock_card(card)) | 300 | if (WaitTillCardIsFree(base)) |
286 | return; | 301 | return; |
287 | 302 | ||
288 | outw(0x8000 | (channel << card->shift_count) | 0x02, base); | 303 | outw(0x8000 | (channel << card->shift_count) | 0x02, base); |
289 | outw(0x0504, base); | 304 | outw(0x0504, base); |
290 | InterruptTheCard(base); | 305 | InterruptTheCard(base); |
291 | port->status |= ISI_DTR; | 306 | port->status |= ISI_DTR; |
292 | unlock_card(card); | ||
293 | } | 307 | } |
294 | 308 | ||
309 | /* card->lock HAS to be held */ | ||
295 | static inline void drop_dtr(struct isi_port *port) | 310 | static inline void drop_dtr(struct isi_port *port) |
296 | { | 311 | { |
297 | struct isi_board *card = port->card; | 312 | struct isi_board *card = port->card; |
298 | unsigned long base = card->base; | 313 | unsigned long base = card->base; |
299 | u16 channel = port->channel; | 314 | u16 channel = port->channel; |
300 | 315 | ||
301 | if (!lock_card(card)) | 316 | if (WaitTillCardIsFree(base)) |
302 | return; | 317 | return; |
303 | 318 | ||
304 | outw(0x8000 | (channel << card->shift_count) | 0x02, base); | 319 | outw(0x8000 | (channel << card->shift_count) | 0x02, base); |
305 | outw(0x0404, base); | 320 | outw(0x0404, base); |
306 | InterruptTheCard(base); | 321 | InterruptTheCard(base); |
307 | port->status &= ~ISI_DTR; | 322 | port->status &= ~ISI_DTR; |
308 | unlock_card(card); | ||
309 | } | 323 | } |
310 | 324 | ||
325 | /* card->lock HAS to be held */ | ||
311 | static inline void raise_rts(struct isi_port *port) | 326 | static inline void raise_rts(struct isi_port *port) |
312 | { | 327 | { |
313 | struct isi_board *card = port->card; | 328 | struct isi_board *card = port->card; |
314 | unsigned long base = card->base; | 329 | unsigned long base = card->base; |
315 | u16 channel = port->channel; | 330 | u16 channel = port->channel; |
316 | 331 | ||
317 | if (!lock_card(card)) | 332 | if (WaitTillCardIsFree(base)) |
318 | return; | 333 | return; |
319 | 334 | ||
320 | outw(0x8000 | (channel << card->shift_count) | 0x02, base); | 335 | outw(0x8000 | (channel << card->shift_count) | 0x02, base); |
321 | outw(0x0a04, base); | 336 | outw(0x0a04, base); |
322 | InterruptTheCard(base); | 337 | InterruptTheCard(base); |
323 | port->status |= ISI_RTS; | 338 | port->status |= ISI_RTS; |
324 | unlock_card(card); | ||
325 | } | 339 | } |
340 | |||
341 | /* card->lock HAS to be held */ | ||
326 | static inline void drop_rts(struct isi_port *port) | 342 | static inline void drop_rts(struct isi_port *port) |
327 | { | 343 | { |
328 | struct isi_board *card = port->card; | 344 | struct isi_board *card = port->card; |
329 | unsigned long base = card->base; | 345 | unsigned long base = card->base; |
330 | u16 channel = port->channel; | 346 | u16 channel = port->channel; |
331 | 347 | ||
332 | if (!lock_card(card)) | 348 | if (WaitTillCardIsFree(base)) |
333 | return; | 349 | return; |
334 | 350 | ||
335 | outw(0x8000 | (channel << card->shift_count) | 0x02, base); | 351 | outw(0x8000 | (channel << card->shift_count) | 0x02, base); |
336 | outw(0x0804, base); | 352 | outw(0x0804, base); |
337 | InterruptTheCard(base); | 353 | InterruptTheCard(base); |
338 | port->status &= ~ISI_RTS; | 354 | port->status &= ~ISI_RTS; |
339 | unlock_card(card); | ||
340 | } | 355 | } |
341 | 356 | ||
357 | /* card->lock MUST NOT be held */ | ||
342 | static inline void raise_dtr_rts(struct isi_port *port) | 358 | static inline void raise_dtr_rts(struct isi_port *port) |
343 | { | 359 | { |
344 | struct isi_board *card = port->card; | 360 | struct isi_board *card = port->card; |
@@ -355,35 +371,20 @@ static inline void raise_dtr_rts(struct isi_port *port) | |||
355 | unlock_card(card); | 371 | unlock_card(card); |
356 | } | 372 | } |
357 | 373 | ||
374 | /* card->lock HAS to be held */ | ||
358 | static void drop_dtr_rts(struct isi_port *port) | 375 | static void drop_dtr_rts(struct isi_port *port) |
359 | { | 376 | { |
360 | struct isi_board *card = port->card; | 377 | struct isi_board *card = port->card; |
361 | unsigned long base = card->base; | 378 | unsigned long base = card->base; |
362 | u16 channel = port->channel; | 379 | u16 channel = port->channel; |
363 | 380 | ||
364 | if (!lock_card(card)) | 381 | if (WaitTillCardIsFree(base)) |
365 | return; | 382 | return; |
366 | 383 | ||
367 | outw(0x8000 | (channel << card->shift_count) | 0x02, base); | 384 | outw(0x8000 | (channel << card->shift_count) | 0x02, base); |
368 | outw(0x0c04, base); | 385 | outw(0x0c04, base); |
369 | InterruptTheCard(base); | 386 | InterruptTheCard(base); |
370 | port->status &= ~(ISI_RTS | ISI_DTR); | 387 | port->status &= ~(ISI_RTS | ISI_DTR); |
371 | unlock_card(card); | ||
372 | } | ||
373 | |||
374 | static inline void kill_queue(struct isi_port *port, short queue) | ||
375 | { | ||
376 | struct isi_board *card = port->card; | ||
377 | unsigned long base = card->base; | ||
378 | u16 channel = port->channel; | ||
379 | |||
380 | if (!lock_card(card)) | ||
381 | return; | ||
382 | |||
383 | outw(0x8000 | (channel << card->shift_count) | 0x02, base); | ||
384 | outw((queue << 8) | 0x06, base); | ||
385 | InterruptTheCard(base); | ||
386 | unlock_card(card); | ||
387 | } | 388 | } |
388 | 389 | ||
389 | /* | 390 | /* |
@@ -744,7 +745,7 @@ static void isicom_config_port(struct isi_port *port) | |||
744 | else | 745 | else |
745 | raise_dtr(port); | 746 | raise_dtr(port); |
746 | 747 | ||
747 | if (lock_card(card)) { | 748 | if (WaitTillCardIsFree(base) == 0) { |
748 | outw(0x8000 | (channel << shift_count) |0x03, base); | 749 | outw(0x8000 | (channel << shift_count) |0x03, base); |
749 | outw(linuxb_to_isib[baud] << 8 | 0x03, base); | 750 | outw(linuxb_to_isib[baud] << 8 | 0x03, base); |
750 | channel_setup = 0; | 751 | channel_setup = 0; |
@@ -772,7 +773,6 @@ static void isicom_config_port(struct isi_port *port) | |||
772 | } | 773 | } |
773 | outw(channel_setup, base); | 774 | outw(channel_setup, base); |
774 | InterruptTheCard(base); | 775 | InterruptTheCard(base); |
775 | unlock_card(card); | ||
776 | } | 776 | } |
777 | if (C_CLOCAL(tty)) | 777 | if (C_CLOCAL(tty)) |
778 | port->flags &= ~ASYNC_CHECK_CD; | 778 | port->flags &= ~ASYNC_CHECK_CD; |
@@ -791,12 +791,11 @@ static void isicom_config_port(struct isi_port *port) | |||
791 | if (I_IXOFF(tty)) | 791 | if (I_IXOFF(tty)) |
792 | flow_ctrl |= ISICOM_INITIATE_XONXOFF; | 792 | flow_ctrl |= ISICOM_INITIATE_XONXOFF; |
793 | 793 | ||
794 | if (lock_card(card)) { | 794 | if (WaitTillCardIsFree(base) == 0) { |
795 | outw(0x8000 | (channel << shift_count) |0x04, base); | 795 | outw(0x8000 | (channel << shift_count) |0x04, base); |
796 | outw(flow_ctrl << 8 | 0x05, base); | 796 | outw(flow_ctrl << 8 | 0x05, base); |
797 | outw((STOP_CHAR(tty)) << 8 | (START_CHAR(tty)), base); | 797 | outw((STOP_CHAR(tty)) << 8 | (START_CHAR(tty)), base); |
798 | InterruptTheCard(base); | 798 | InterruptTheCard(base); |
799 | unlock_card(card); | ||
800 | } | 799 | } |
801 | 800 | ||
802 | /* rx enabled -> enable port for rx on the card */ | 801 | /* rx enabled -> enable port for rx on the card */ |
@@ -821,10 +820,9 @@ static inline void isicom_setup_board(struct isi_board *bp) | |||
821 | } | 820 | } |
822 | port = bp->ports; | 821 | port = bp->ports; |
823 | bp->status |= BOARD_ACTIVE; | 822 | bp->status |= BOARD_ACTIVE; |
824 | spin_unlock_irqrestore(&bp->card_lock, flags); | ||
825 | for (channel = 0; channel < bp->port_count; channel++, port++) | 823 | for (channel = 0; channel < bp->port_count; channel++, port++) |
826 | drop_dtr_rts(port); | 824 | drop_dtr_rts(port); |
827 | return; | 825 | spin_unlock_irqrestore(&bp->card_lock, flags); |
828 | } | 826 | } |
829 | 827 | ||
830 | static int isicom_setup_port(struct isi_port *port) | 828 | static int isicom_setup_port(struct isi_port *port) |
@@ -857,7 +855,12 @@ static int isicom_setup_port(struct isi_port *port) | |||
857 | port->xmit_cnt = port->xmit_head = port->xmit_tail = 0; | 855 | port->xmit_cnt = port->xmit_head = port->xmit_tail = 0; |
858 | 856 | ||
859 | /* discard any residual data */ | 857 | /* discard any residual data */ |
860 | kill_queue(port, ISICOM_KILLTX | ISICOM_KILLRX); | 858 | if (WaitTillCardIsFree(card->base) == 0) { |
859 | outw(0x8000 | (port->channel << card->shift_count) | 0x02, | ||
860 | card->base); | ||
861 | outw(((ISICOM_KILLTX | ISICOM_KILLRX) << 8) | 0x06, card->base); | ||
862 | InterruptTheCard(card->base); | ||
863 | } | ||
861 | 864 | ||
862 | isicom_config_port(port); | 865 | isicom_config_port(port); |
863 | port->flags |= ASYNC_INITIALIZED; | 866 | port->flags |= ASYNC_INITIALIZED; |
@@ -983,28 +986,22 @@ static int isicom_open(struct tty_struct *tty, struct file *filp) | |||
983 | 986 | ||
984 | static inline void isicom_shutdown_board(struct isi_board *bp) | 987 | static inline void isicom_shutdown_board(struct isi_board *bp) |
985 | { | 988 | { |
986 | unsigned long flags; | ||
987 | |||
988 | spin_lock_irqsave(&bp->card_lock, flags); | ||
989 | if (bp->status & BOARD_ACTIVE) { | 989 | if (bp->status & BOARD_ACTIVE) { |
990 | bp->status &= ~BOARD_ACTIVE; | 990 | bp->status &= ~BOARD_ACTIVE; |
991 | } | 991 | } |
992 | spin_unlock_irqrestore(&bp->card_lock, flags); | ||
993 | } | 992 | } |
994 | 993 | ||
994 | /* card->lock HAS to be held */ | ||
995 | static void isicom_shutdown_port(struct isi_port *port) | 995 | static void isicom_shutdown_port(struct isi_port *port) |
996 | { | 996 | { |
997 | struct isi_board *card = port->card; | 997 | struct isi_board *card = port->card; |
998 | struct tty_struct *tty; | 998 | struct tty_struct *tty; |
999 | unsigned long flags; | ||
1000 | 999 | ||
1001 | tty = port->tty; | 1000 | tty = port->tty; |
1002 | 1001 | ||
1003 | spin_lock_irqsave(&card->card_lock, flags); | 1002 | if (!(port->flags & ASYNC_INITIALIZED)) |
1004 | if (!(port->flags & ASYNC_INITIALIZED)) { | ||
1005 | spin_unlock_irqrestore(&card->card_lock, flags); | ||
1006 | return; | 1003 | return; |
1007 | } | 1004 | |
1008 | if (port->xmit_buf) { | 1005 | if (port->xmit_buf) { |
1009 | free_page((unsigned long) port->xmit_buf); | 1006 | free_page((unsigned long) port->xmit_buf); |
1010 | port->xmit_buf = NULL; | 1007 | port->xmit_buf = NULL; |
@@ -1012,7 +1009,6 @@ static void isicom_shutdown_port(struct isi_port *port) | |||
1012 | port->flags &= ~ASYNC_INITIALIZED; | 1009 | port->flags &= ~ASYNC_INITIALIZED; |
1013 | /* 3rd October 2000 : Vinayak P Risbud */ | 1010 | /* 3rd October 2000 : Vinayak P Risbud */ |
1014 | port->tty = NULL; | 1011 | port->tty = NULL; |
1015 | spin_unlock_irqrestore(&card->card_lock, flags); | ||
1016 | 1012 | ||
1017 | /*Fix done by Anil .S on 30-04-2001 | 1013 | /*Fix done by Anil .S on 30-04-2001 |
1018 | remote login through isi port has dtr toggle problem | 1014 | remote login through isi port has dtr toggle problem |
@@ -1258,10 +1254,12 @@ static int isicom_tiocmset(struct tty_struct *tty, struct file *file, | |||
1258 | unsigned int set, unsigned int clear) | 1254 | unsigned int set, unsigned int clear) |
1259 | { | 1255 | { |
1260 | struct isi_port *port = tty->driver_data; | 1256 | struct isi_port *port = tty->driver_data; |
1257 | unsigned long flags; | ||
1261 | 1258 | ||
1262 | if (isicom_paranoia_check(port, tty->name, "isicom_ioctl")) | 1259 | if (isicom_paranoia_check(port, tty->name, "isicom_ioctl")) |
1263 | return -ENODEV; | 1260 | return -ENODEV; |
1264 | 1261 | ||
1262 | spin_lock_irqsave(&port->card->card_lock, flags); | ||
1265 | if (set & TIOCM_RTS) | 1263 | if (set & TIOCM_RTS) |
1266 | raise_rts(port); | 1264 | raise_rts(port); |
1267 | if (set & TIOCM_DTR) | 1265 | if (set & TIOCM_DTR) |
@@ -1271,6 +1269,7 @@ static int isicom_tiocmset(struct tty_struct *tty, struct file *file, | |||
1271 | drop_rts(port); | 1269 | drop_rts(port); |
1272 | if (clear & TIOCM_DTR) | 1270 | if (clear & TIOCM_DTR) |
1273 | drop_dtr(port); | 1271 | drop_dtr(port); |
1272 | spin_unlock_irqrestore(&port->card->card_lock, flags); | ||
1274 | 1273 | ||
1275 | return 0; | 1274 | return 0; |
1276 | } | 1275 | } |
@@ -1303,7 +1302,10 @@ static int isicom_set_serial_info(struct isi_port *port, | |||
1303 | (newinfo.flags & ASYNC_FLAGS)); | 1302 | (newinfo.flags & ASYNC_FLAGS)); |
1304 | } | 1303 | } |
1305 | if (reconfig_port) { | 1304 | if (reconfig_port) { |
1305 | unsigned long flags; | ||
1306 | spin_lock_irqsave(&port->card->card_lock, flags); | ||
1306 | isicom_config_port(port); | 1307 | isicom_config_port(port); |
1308 | spin_unlock_irqrestore(&port->card->card_lock, flags); | ||
1307 | } | 1309 | } |
1308 | return 0; | 1310 | return 0; |
1309 | } | 1311 | } |
@@ -1384,6 +1386,7 @@ static void isicom_set_termios(struct tty_struct *tty, | |||
1384 | struct ktermios *old_termios) | 1386 | struct ktermios *old_termios) |
1385 | { | 1387 | { |
1386 | struct isi_port *port = tty->driver_data; | 1388 | struct isi_port *port = tty->driver_data; |
1389 | unsigned long flags; | ||
1387 | 1390 | ||
1388 | if (isicom_paranoia_check(port, tty->name, "isicom_set_termios")) | 1391 | if (isicom_paranoia_check(port, tty->name, "isicom_set_termios")) |
1389 | return; | 1392 | return; |
@@ -1392,7 +1395,9 @@ static void isicom_set_termios(struct tty_struct *tty, | |||
1392 | tty->termios->c_iflag == old_termios->c_iflag) | 1395 | tty->termios->c_iflag == old_termios->c_iflag) |
1393 | return; | 1396 | return; |
1394 | 1397 | ||
1398 | spin_lock_irqsave(&port->card->card_lock, flags); | ||
1395 | isicom_config_port(port); | 1399 | isicom_config_port(port); |
1400 | spin_unlock_irqrestore(&port->card->card_lock, flags); | ||
1396 | 1401 | ||
1397 | if ((old_termios->c_cflag & CRTSCTS) && | 1402 | if ((old_termios->c_cflag & CRTSCTS) && |
1398 | !(tty->termios->c_cflag & CRTSCTS)) { | 1403 | !(tty->termios->c_cflag & CRTSCTS)) { |
@@ -1469,11 +1474,15 @@ static void do_isicom_hangup(struct work_struct *work) | |||
1469 | static void isicom_hangup(struct tty_struct *tty) | 1474 | static void isicom_hangup(struct tty_struct *tty) |
1470 | { | 1475 | { |
1471 | struct isi_port *port = tty->driver_data; | 1476 | struct isi_port *port = tty->driver_data; |
1477 | unsigned long flags; | ||
1472 | 1478 | ||
1473 | if (isicom_paranoia_check(port, tty->name, "isicom_hangup")) | 1479 | if (isicom_paranoia_check(port, tty->name, "isicom_hangup")) |
1474 | return; | 1480 | return; |
1475 | 1481 | ||
1482 | spin_lock_irqsave(&port->card->card_lock, flags); | ||
1476 | isicom_shutdown_port(port); | 1483 | isicom_shutdown_port(port); |
1484 | spin_unlock_irqrestore(&port->card->card_lock, flags); | ||
1485 | |||
1477 | port->count = 0; | 1486 | port->count = 0; |
1478 | port->flags &= ~ASYNC_NORMAL_ACTIVE; | 1487 | port->flags &= ~ASYNC_NORMAL_ACTIVE; |
1479 | port->tty = NULL; | 1488 | port->tty = NULL; |
@@ -1578,16 +1587,6 @@ end: | |||
1578 | return retval; | 1587 | return retval; |
1579 | } | 1588 | } |
1580 | 1589 | ||
1581 | static inline int WaitTillCardIsFree(u16 base) | ||
1582 | { | ||
1583 | unsigned long count = 0; | ||
1584 | |||
1585 | while (!(inw(base + 0xe) & 0x1) && count++ < 100) | ||
1586 | msleep(5); | ||
1587 | |||
1588 | return !(inw(base + 0xe) & 0x1); | ||
1589 | } | ||
1590 | |||
1591 | static int __devinit load_firmware(struct pci_dev *pdev, | 1590 | static int __devinit load_firmware(struct pci_dev *pdev, |
1592 | const unsigned int index, const unsigned int signature) | 1591 | const unsigned int index, const unsigned int signature) |
1593 | { | 1592 | { |