The lock-less mutual exclusion protect code against
theoretical possibility of concurrent execution attempt.
That scenario can appear only on UART with FIFO shorter
than 10 characters and likely impossible even in such
cases.
Signed-off-by: Pavel Pisa <pisa@cmp.felk.cvut.cz>
#define SLF_RXEVENT 2 /* Rx wake event */
#define SLF_TXEVENT 3 /* Tx wake event */
#define SLF_MSGEVENT 4 /* CAN message to sent */
#define SLF_RXEVENT 2 /* Rx wake event */
#define SLF_TXEVENT 3 /* Tx wake event */
#define SLF_MSGEVENT 4 /* CAN message to sent */
-#define SLF_TMOUTEVENT 5 /* Timeout on received data */
+#define SLF_TMOUTEVENT 5 /* Timeout on received data */
+#define SLF_TXBUFF_RQ 6 /* Req. to send buffer to UART*/
+#define SLF_TXBUFF_INPR 7 /* Above request in progress */
dev_t line;
struct task_struct *kwthread;
dev_t line;
struct task_struct *kwthread;
cf.can_id = id;
cf.can_dlc = len;
cf.can_id = id;
cf.can_dlc = len;
memcpy(&cf.data, data, cf.can_dlc);
memcpy(&cf.data, data, cf.can_dlc);
skb = dev_alloc_skb(sizeof(struct can_frame));
if (!skb)
skb = dev_alloc_skb(sizeof(struct can_frame));
if (!skb)
*/
static void sllin_write_wakeup(struct tty_struct *tty)
{
*/
static void sllin_write_wakeup(struct tty_struct *tty)
{
int remains;
struct sllin *sl = (struct sllin *) tty->disc_data;
int remains;
struct sllin *sl = (struct sllin *) tty->disc_data;
if (!sl || sl->magic != SLLIN_MAGIC || !netif_running(sl->dev))
return;
if (!sl || sl->magic != SLLIN_MAGIC || !netif_running(sl->dev))
return;
- if (sl->lin_state != SLSTATE_BREAK_SENT)
- remains = sl->tx_lim - sl->tx_cnt;
- else
- remains = SLLIN_BUFF_BREAK + 1 - sl->tx_cnt;
-
- if (remains > 0) {
- actual = tty->ops->write(tty, sl->tx_buff + sl->tx_cnt,
- sl->tx_cnt - sl->tx_lim);
- sl->tx_cnt += actual;
-
- if (sl->tx_cnt < sl->tx_lim) {
- pr_debug("sllin: sllin_write_wakeup sent %d, "
- "remains %d, waiting\n",
- sl->tx_cnt, sl->tx_lim - sl->tx_cnt);
- return;
+ set_bit(SLF_TXBUFF_RQ, &sl->flags);
+ do {
+ if (unlikely(test_and_set_bit(SLF_TXBUFF_INPR, &sl->flags)))
+ return; /* ongoing concurrent processing */
+
+ clear_bit(SLF_TXBUFF_RQ, &sl->flags);
+ smp_mb__after_clear_bit();
+
+ if (sl->lin_state != SLSTATE_BREAK_SENT)
+ remains = sl->tx_lim - sl->tx_cnt;
+ else
+ remains = SLLIN_BUFF_BREAK + 1 - sl->tx_cnt;
+
+ if (remains > 0) {
+ actual = tty->ops->write(tty, sl->tx_buff + sl->tx_cnt,
+ sl->tx_cnt - sl->tx_lim);
+ sl->tx_cnt += actual;
+ remains -= actual;
+ clear_bit(SLF_TXBUFF_INPR, &sl->flags);
+ smp_mb__after_clear_bit();
+
+ } while (unlikely(test_bit(SLF_TXBUFF_RQ, &sl->flags)));
+
+ if ((remains > 0) && (actual >= 0)) {
+ pr_debug("sllin: sllin_write_wakeup sent %d, "
+ "remains %d, waiting\n",
+ sl->tx_cnt, sl->tx_lim - sl->tx_cnt);
+ return;
}
clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
}
clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
goto err_out_unlock;
}
if (sl->tty == NULL) {
goto err_out_unlock;
}
if (sl->tty == NULL) {
+ printk(KERN_WARNING "%s: xmit: no tty device connected\n", dev->name);
{
struct sllin *sl = netdev_priv(dev);
{
struct sllin *sl = netdev_priv(dev);
- pr_debug("sllin: %s() invoked\n", __FUNCTION__);
+ pr_debug("sllin: %s() invoked\n", __func__);
if (sl->tty == NULL)
return -ENODEV;
if (sl->tty == NULL)
return -ENODEV;
unsigned csum = 0;
int i;
unsigned csum = 0;
int i;
for (; i < length; i++) {
csum += data[i];
for (; i < length; i++) {
csum += data[i];
+ set_bit(SLF_TXBUFF_RQ, &sl->flags);
+ do {
+ if (unlikely(test_and_set_bit(SLF_TXBUFF_INPR, &sl->flags)))
+ return 0; /* ongoing concurrent processing */
+
+ clear_bit(SLF_TXBUFF_RQ, &sl->flags);
+ smp_mb__after_clear_bit();
+
- if (sl->lin_state != SLSTATE_BREAK_SENT)
- remains = sl->tx_lim - sl->tx_cnt;
- else
- remains = 1;
+ if (sl->lin_state != SLSTATE_BREAK_SENT)
+ remains = sl->tx_lim - sl->tx_cnt;
+ else
+ remains = 1;
- remains = sl->tx_lim - sl->tx_cnt;
+ remains = sl->tx_lim - sl->tx_cnt;
- res = tty->ops->write(tty, sl->tx_buff + sl->tx_cnt, remains);
- if (res < 0)
- return -1;
-
- remains -= res;
- sl->tx_cnt += res;
-
- if (remains > 0) {
- set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
res = tty->ops->write(tty, sl->tx_buff + sl->tx_cnt, remains);
res = tty->ops->write(tty, sl->tx_buff + sl->tx_cnt, remains);
- if (res < 0) {
- clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
- return -1;
- }
+ if (res < 0)
+ goto error_in_write;
remains -= res;
sl->tx_cnt += res;
remains -= res;
sl->tx_cnt += res;
- pr_debug("sllin: sllin_send_tx_buff sent %d, remains %d\n",
- sl->tx_cnt, remains);
+ if (remains > 0) {
+ set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
+ res = tty->ops->write(tty, sl->tx_buff + sl->tx_cnt, remains);
+ if (res < 0) {
+ clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
+ goto error_in_write;
+ }
+
+ remains -= res;
+ sl->tx_cnt += res;
+ }
+
+ pr_debug("sllin: sllin_send_tx_buff sent %d, remains %d\n",
+ sl->tx_cnt, remains);
+
+ clear_bit(SLF_TXBUFF_INPR, &sl->flags);
+ smp_mb__after_clear_bit();
+
+ } while (unlikely(test_bit(SLF_TXBUFF_RQ, &sl->flags)));
+
+error_in_write:
+ clear_bit(SLF_TXBUFF_INPR, &sl->flags);
+ return -1;
+
struct sllin_conf_entry *sce;
pr_debug("sllin: %s: RTR SFF CAN frame, ID = %x\n",
struct sllin_conf_entry *sce;
pr_debug("sllin: %s: RTR SFF CAN frame, ID = %x\n",
- __FUNCTION__, cf->can_id & LIN_ID_MASK);
+ __func__, cf->can_id & LIN_ID_MASK);
sce = &sl->linfr_cache[cf->can_id & LIN_ID_MASK];
spin_lock_irqsave(&sl->linfr_lock, flags);
sce = &sl->linfr_cache[cf->can_id & LIN_ID_MASK];
spin_lock_irqsave(&sl->linfr_lock, flags);
} else { /* SFF NON-RTR CAN frame -> LIN header + LIN response */
pr_debug("sllin: %s: NON-RTR SFF CAN frame, ID = %x\n",
} else { /* SFF NON-RTR CAN frame -> LIN header + LIN response */
pr_debug("sllin: %s: NON-RTR SFF CAN frame, ID = %x\n",
- __FUNCTION__, (int)cf->can_id & LIN_ID_MASK);
+ __func__, (int)cf->can_id & LIN_ID_MASK);
lin_data = cf->data;
lin_dlc = cf->can_dlc;
lin_data = cf->data;
lin_dlc = cf->can_dlc;
{
struct sllin *sl;
int err;
{
struct sllin *sl;
int err;
- pr_debug("sllin: %s() invoked\n", __FUNCTION__);
+ pr_debug("sllin: %s() invoked\n", __func__);
if (!capable(CAP_NET_ADMIN))
return -EPERM;
if (!capable(CAP_NET_ADMIN))
return -EPERM;