static void flush_to_ldisc(struct work_struct *work) { struct tty_struct *tty = container_of(work, struct tty_struct, buf.work); unsigned long flags; struct tty_ldisc *disc; disc = tty_ldisc_ref(tty); if (disc == NULL) /* !TTY_LDISC */ return; spin_lock_irqsave(&tty->buf.lock, flags); if (!test_and_set_bit(TTY_FLUSHING, &tty->flags)) { struct tty_buffer *head; while ((head = tty->buf.head) != NULL) { int count; char *char_buf; unsigned char *flag_buf; count = head->commit - head->read; if (!count) { if (head->next == NULL) break; tty->buf.head = head->next; tty_buffer_free(tty, head); continue; } /* Ldisc or user is trying to flush the buffers we are feeding to the ldisc, stop feeding the line discipline as we want to empty the queue */ if (test_bit(TTY_FLUSHPENDING, &tty->flags)) break; if (!tty->receive_room) break; if (count > tty->receive_room) count = tty->receive_room; char_buf = head->char_buf_ptr + head->read; flag_buf = head->flag_buf_ptr + head->read; head->read += count; spin_unlock_irqrestore(&tty->buf.lock, flags); disc->ops->receive_buf(tty, char_buf, flag_buf, count); spin_lock_irqsave(&tty->buf.lock, flags); } clear_bit(TTY_FLUSHING, &tty->flags); } /* We may have a deferred request to flush the input buffer, if so pull the chain under the lock and empty the queue */ if (test_bit(TTY_FLUSHPENDING, &tty->flags)) { __tty_buffer_flush(tty); clear_bit(TTY_FLUSHPENDING, &tty->flags); wake_up(&tty->read_wait); } spin_unlock_irqrestore(&tty->buf.lock, flags); tty_ldisc_deref(disc); }
void tty_buffer_flush(struct tty_struct *tty) { unsigned long flags; spin_lock_irqsave(&tty->buf.lock, flags); /* If the data is being pushed to the tty layer then we can't process it here. Instead set a flag and the flush_to_ldisc path will process the flush request before it exits */ if (test_bit(TTY_FLUSHING, &tty->flags)) { set_bit(TTY_FLUSHPENDING, &tty->flags); spin_unlock_irqrestore(&tty->buf.lock, flags); wait_event(tty->read_wait, test_bit(TTY_FLUSHPENDING, &tty->flags) == 0); return; } else __tty_buffer_flush(tty); spin_unlock_irqrestore(&tty->buf.lock, flags); }
static void flush_to_ldisc(struct work_struct *work) { struct tty_struct *tty = container_of(work, struct tty_struct, buf.work.work); unsigned long flags; struct tty_ldisc *disc; disc = tty_ldisc_ref(tty); if (disc == NULL) /* !TTY_LDISC */ return; spin_lock_irqsave(&tty->buf.lock, flags); if (!test_and_set_bit(TTY_FLUSHING, &tty->flags)) { struct tty_buffer *head, *tail = tty->buf.tail; int seen_tail = 0; while ((head = tty->buf.head) != NULL) { int count; char *char_buf; unsigned char *flag_buf; count = head->commit - head->read; if (!count) { if (head->next == NULL) break; /* There's a possibility tty might get new buffer added during the unlock window below. We could end up spinning in here forever hogging the CPU completely. To avoid this let's have a rest each time we processed the tail buffer. */ if (tail == head) seen_tail = 1; tty->buf.head = head->next; tty_buffer_free(tty, head); continue; } /* Ldisc or user is trying to flush the buffers we are feeding to the ldisc, stop feeding the line discipline as we want to empty the queue */ if (test_bit(TTY_FLUSHPENDING, &tty->flags)) break; if (!tty->receive_room || seen_tail) { schedule_delayed_work(&tty->buf.work, 1); break; } if (count > tty->receive_room) count = tty->receive_room; char_buf = head->char_buf_ptr + head->read; flag_buf = head->flag_buf_ptr + head->read; head->read += count; spin_unlock_irqrestore(&tty->buf.lock, flags); disc->ops->receive_buf(tty, char_buf, flag_buf, count); spin_lock_irqsave(&tty->buf.lock, flags); } clear_bit(TTY_FLUSHING, &tty->flags); } /* We may have a deferred request to flush the input buffer, if so pull the chain under the lock and empty the queue */ if (test_bit(TTY_FLUSHPENDING, &tty->flags)) { __tty_buffer_flush(tty); clear_bit(TTY_FLUSHPENDING, &tty->flags); wake_up(&tty->read_wait); } spin_unlock_irqrestore(&tty->buf.lock, flags); tty_ldisc_deref(disc); }
static void flush_to_ldisc(struct work_struct *work) { struct tty_struct *tty = container_of(work, struct tty_struct, buf.work); unsigned long flags; struct tty_ldisc *disc; disc = tty_ldisc_ref(tty); if (disc == NULL) /* !TTY_LDISC */ return; spin_lock_irqsave(&tty->buf.lock, flags); if (!test_and_set_bit(TTY_FLUSHING, &tty->flags)) { struct tty_buffer *head; while ((head = tty->buf.head) != NULL) { int count; char *char_buf; unsigned char *flag_buf; unsigned int left = 0; unsigned int max_space; count = head->commit - head->read; if (!count) { if (head->next == NULL) break; tty->buf.head = head->next; tty_buffer_free(tty, head); continue; } /* Ldisc or user is trying to flush the buffers we are feeding to the ldisc, stop feeding the line discipline as we want to empty the queue */ if (test_bit(TTY_FLUSHPENDING, &tty->flags)) break; /* update receive room */ spin_lock(&tty->read_lock); if (tty->update_room_in_ldisc) { if ((tty->read_cnt == N_TTY_BUF_SIZE - 1) && (tty->receive_room == N_TTY_BUF_SIZE - 1)) tty->rr_bug++; left = N_TTY_BUF_SIZE - tty->read_cnt - 1; } spin_unlock(&tty->read_lock); if (!tty->receive_room) break; if (tty->update_room_in_ldisc && !left) { schedule_work(&tty->buf.work); break; } if (tty->update_room_in_ldisc) max_space = min(left, tty->receive_room); else max_space = tty->receive_room; if (count > max_space) count = max_space; char_buf = head->char_buf_ptr + head->read; flag_buf = head->flag_buf_ptr + head->read; head->read += count; spin_unlock_irqrestore(&tty->buf.lock, flags); if(tty->start_debug){ dbg_log_event(NULL, "f_t_l_d:head->read",head->read, "head->commit", head->commit, "receive_room ", tty->receive_room ); } tty->ldisc_cnt += count; disc->ops->receive_buf(tty, char_buf, flag_buf, count); spin_lock_irqsave(&tty->buf.lock, flags); } clear_bit(TTY_FLUSHING, &tty->flags); } /* We may have a deferred request to flush the input buffer, if so pull the chain under the lock and empty the queue */ if (test_bit(TTY_FLUSHPENDING, &tty->flags)) { __tty_buffer_flush(tty); clear_bit(TTY_FLUSHPENDING, &tty->flags); wake_up(&tty->read_wait); } spin_unlock_irqrestore(&tty->buf.lock, flags); tty_ldisc_deref(disc); }