diff options
author | Peter Hurley <peter@hurleysoftware.com> | 2013-06-15 09:14:17 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2013-07-23 19:42:59 -0400 |
commit | 32f13521ca68bc624ff6effc77f308a52b038bf0 (patch) | |
tree | 9d92c06722c7200133366d088eea2bafd2a86c08 /drivers/tty/n_tty.c | |
parent | 88bb0de389a464521034e87816e5d6c7c489a664 (diff) |
n_tty: Line copy to user buffer in canonical mode
Instead of pushing one char per loop, pre-compute the data length
to copy and copy all at once.
Signed-off-by: Peter Hurley <peter@hurleysoftware.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/tty/n_tty.c')
-rw-r--r-- | drivers/tty/n_tty.c | 110 |
1 files changed, 77 insertions, 33 deletions
diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index 7fd77746f2d0..74dcedd06a8b 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c | |||
@@ -74,6 +74,13 @@ | |||
74 | #define ECHO_OP_SET_CANON_COL 0x81 | 74 | #define ECHO_OP_SET_CANON_COL 0x81 |
75 | #define ECHO_OP_ERASE_TAB 0x82 | 75 | #define ECHO_OP_ERASE_TAB 0x82 |
76 | 76 | ||
77 | #undef N_TTY_TRACE | ||
78 | #ifdef N_TTY_TRACE | ||
79 | # define n_tty_trace(f, args...) trace_printk(f, ##args) | ||
80 | #else | ||
81 | # define n_tty_trace(f, args...) | ||
82 | #endif | ||
83 | |||
77 | struct n_tty_data { | 84 | struct n_tty_data { |
78 | unsigned int column; | 85 | unsigned int column; |
79 | unsigned long overrun_time; | 86 | unsigned long overrun_time; |
@@ -1748,58 +1755,95 @@ static int copy_from_read_buf(struct tty_struct *tty, | |||
1748 | } | 1755 | } |
1749 | 1756 | ||
1750 | /** | 1757 | /** |
1751 | * canon_copy_to_user - copy read data in canonical mode | 1758 | * canon_copy_from_read_buf - copy read data in canonical mode |
1752 | * @tty: terminal device | 1759 | * @tty: terminal device |
1753 | * @b: user data | 1760 | * @b: user data |
1754 | * @nr: size of data | 1761 | * @nr: size of data |
1755 | * | 1762 | * |
1756 | * Helper function for n_tty_read. It is only called when ICANON is on; | 1763 | * Helper function for n_tty_read. It is only called when ICANON is on; |
1757 | * it copies characters one at a time from the read buffer to the user | 1764 | * it copies one line of input up to and including the line-delimiting |
1758 | * space buffer. | 1765 | * character into the user-space buffer. |
1759 | * | 1766 | * |
1760 | * Called under the atomic_read_lock mutex | 1767 | * Called under the atomic_read_lock mutex |
1761 | */ | 1768 | */ |
1762 | 1769 | ||
1763 | static int canon_copy_to_user(struct tty_struct *tty, | 1770 | static int canon_copy_from_read_buf(struct tty_struct *tty, |
1764 | unsigned char __user **b, | 1771 | unsigned char __user **b, |
1765 | size_t *nr) | 1772 | size_t *nr) |
1766 | { | 1773 | { |
1767 | struct n_tty_data *ldata = tty->disc_data; | 1774 | struct n_tty_data *ldata = tty->disc_data; |
1768 | unsigned long flags; | 1775 | unsigned long flags; |
1769 | int eol, c; | 1776 | size_t n, size, more, c; |
1777 | unsigned long eol; | ||
1778 | int ret, tail, found = 0; | ||
1770 | 1779 | ||
1771 | /* N.B. avoid overrun if nr == 0 */ | 1780 | /* N.B. avoid overrun if nr == 0 */ |
1781 | |||
1772 | raw_spin_lock_irqsave(&ldata->read_lock, flags); | 1782 | raw_spin_lock_irqsave(&ldata->read_lock, flags); |
1773 | while (*nr && ldata->read_cnt) { | 1783 | |
1774 | 1784 | n = min_t(size_t, *nr, ldata->read_cnt); | |
1775 | eol = test_and_clear_bit(ldata->read_tail, ldata->read_flags); | 1785 | if (!n) { |
1776 | c = ldata->read_buf[ldata->read_tail]; | ||
1777 | ldata->read_tail = (ldata->read_tail+1) & (N_TTY_BUF_SIZE-1); | ||
1778 | ldata->read_cnt--; | ||
1779 | if (eol) { | ||
1780 | /* this test should be redundant: | ||
1781 | * we shouldn't be reading data if | ||
1782 | * canon_data is 0 | ||
1783 | */ | ||
1784 | if (--ldata->canon_data < 0) | ||
1785 | ldata->canon_data = 0; | ||
1786 | } | ||
1787 | raw_spin_unlock_irqrestore(&ldata->read_lock, flags); | 1786 | raw_spin_unlock_irqrestore(&ldata->read_lock, flags); |
1787 | return 0; | ||
1788 | } | ||
1788 | 1789 | ||
1789 | if (!eol || (c != __DISABLED_CHAR)) { | 1790 | tail = ldata->read_tail; |
1790 | if (tty_put_user(tty, c, *b)) | 1791 | size = min_t(size_t, tail + n, N_TTY_BUF_SIZE); |
1791 | return -EFAULT; | 1792 | |
1792 | *b += 1; | 1793 | n_tty_trace("%s: nr:%zu tail:%d n:%zu size:%zu\n", |
1793 | *nr -= 1; | 1794 | __func__, *nr, tail, n, size); |
1794 | } | 1795 | |
1795 | if (eol) { | 1796 | eol = find_next_bit(ldata->read_flags, size, tail); |
1796 | tty_audit_push(tty); | 1797 | more = n - (size - tail); |
1797 | return 0; | 1798 | if (eol == N_TTY_BUF_SIZE && more) { |
1798 | } | 1799 | /* scan wrapped without finding set bit */ |
1799 | raw_spin_lock_irqsave(&ldata->read_lock, flags); | 1800 | eol = find_next_bit(ldata->read_flags, more, 0); |
1801 | if (eol != more) | ||
1802 | found = 1; | ||
1803 | } else if (eol != size) | ||
1804 | found = 1; | ||
1805 | |||
1806 | size = N_TTY_BUF_SIZE - tail; | ||
1807 | n = (found + eol + size) & (N_TTY_BUF_SIZE - 1); | ||
1808 | c = n; | ||
1809 | |||
1810 | if (found && ldata->read_buf[eol] == __DISABLED_CHAR) | ||
1811 | n--; | ||
1812 | |||
1813 | n_tty_trace("%s: eol:%lu found:%d n:%zu c:%zu size:%zu more:%zu\n", | ||
1814 | __func__, eol, found, n, c, size, more); | ||
1815 | |||
1816 | raw_spin_unlock_irqrestore(&ldata->read_lock, flags); | ||
1817 | |||
1818 | if (n > size) { | ||
1819 | ret = copy_to_user(*b, &ldata->read_buf[tail], size); | ||
1820 | if (ret) | ||
1821 | return -EFAULT; | ||
1822 | ret = copy_to_user(*b + size, ldata->read_buf, n - size); | ||
1823 | } else | ||
1824 | ret = copy_to_user(*b, &ldata->read_buf[tail], n); | ||
1825 | |||
1826 | if (ret) | ||
1827 | return -EFAULT; | ||
1828 | *b += n; | ||
1829 | *nr -= n; | ||
1830 | |||
1831 | raw_spin_lock_irqsave(&ldata->read_lock, flags); | ||
1832 | ldata->read_tail = (ldata->read_tail + c) & (N_TTY_BUF_SIZE - 1); | ||
1833 | ldata->read_cnt -= c; | ||
1834 | if (found) { | ||
1835 | __clear_bit(eol, ldata->read_flags); | ||
1836 | /* this test should be redundant: | ||
1837 | * we shouldn't be reading data if | ||
1838 | * canon_data is 0 | ||
1839 | */ | ||
1840 | if (--ldata->canon_data < 0) | ||
1841 | ldata->canon_data = 0; | ||
1800 | } | 1842 | } |
1801 | raw_spin_unlock_irqrestore(&ldata->read_lock, flags); | 1843 | raw_spin_unlock_irqrestore(&ldata->read_lock, flags); |
1802 | 1844 | ||
1845 | if (found) | ||
1846 | tty_audit_push(tty); | ||
1803 | return 0; | 1847 | return 0; |
1804 | } | 1848 | } |
1805 | 1849 | ||
@@ -1972,7 +2016,7 @@ do_it_again: | |||
1972 | } | 2016 | } |
1973 | 2017 | ||
1974 | if (ldata->icanon && !L_EXTPROC(tty)) { | 2018 | if (ldata->icanon && !L_EXTPROC(tty)) { |
1975 | retval = canon_copy_to_user(tty, &b, &nr); | 2019 | retval = canon_copy_from_read_buf(tty, &b, &nr); |
1976 | if (retval) | 2020 | if (retval) |
1977 | break; | 2021 | break; |
1978 | } else { | 2022 | } else { |