A general question on SMP-safe driver code.

From: Jim Nelson
Date: Fri Dec 24 2004 - 18:00:19 EST


Looking at a few older drivers, I'm trying to figure out the best ways to handle some locking. Using drivers/char/esp.c as an example (since it's the one I'm trying to grok right now), here is one example:

static void rs_wait_until_sent(struct tty_struct *tty, int timeout)
{
struct esp_struct *info = (struct esp_struct *)tty->driver_data;
unsigned long orig_jiffies, char_time;
unsigned long flags;

if (serial_paranoia_check(info, tty->name, "rs_wait_until_sent"))
return;

orig_jiffies = jiffies;
char_time = ((info->timeout - HZ / 50) / 1024) / 5;

if (!char_time)
char_time = 1;

save_flags(flags); cli();
serial_out(info, UART_ESI_CMD1, ESI_NO_COMMAND);
serial_out(info, UART_ESI_CMD1, ESI_GET_TX_AVAIL);

while ((serial_in(info, UART_ESI_STAT1) != 0x03) ||
(serial_in(info, UART_ESI_STAT2) != 0xff)) {
msleep_interruptible(jiffies_to_msecs(char_time));

if (signal_pending(current))
break;

if (timeout && time_after(jiffies, orig_jiffies + timeout))
break;

serial_out(info, UART_ESI_CMD1, ESI_NO_COMMAND);
serial_out(info, UART_ESI_CMD1, ESI_GET_TX_AVAIL);
}

restore_flags(flags);
set_current_state(TASK_RUNNING);
}

Now, it seems like the cli()/sti() pair is to prevent other code from interrupting the whole sequence. It looks like the only things actually need interrupts disabled (from a command sequencing perspective) is the serial_out pairs, but you want to keep other parts of the driver from sending other commands to the board.

So, would:

down_interruptible(&info->sem);
spin_lock_irq(&info->esp_lock);
serial_out(info, UART_ESI_CMD1, ESI_NO_COMMAND);
serial_out(info, UART_ESI_CMD1, ESI_GET_TX_AVAIL);
spin_unlock_irq(&info->esp_lock);

while ((serial_in(info, UART_ESI_STAT1) != 0x03) ||
(serial_in(info, UART_ESI_STAT2) != 0xff)) {
msleep_interruptible(jiffies_to_msecs(char_time));

if (signal_pending(current))
break;

if (timeout && time_after(jiffies, orig_jiffies + timeout))
break;

spin_lock_irq(&info->esp_lock);
serial_out(info, UART_ESI_CMD1, ESI_NO_COMMAND);
serial_out(info, UART_ESI_CMD1, ESI_GET_TX_AVAIL);
spin_unlock_irq(&info->esp_lock);
up(&info->esp_sem);

work if all other areas of the driver that send commands to the board also try for the semaphore?

Is there an easier way of doing this?

Jim
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/