]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/char/tty_io.c
[PATCH] ptmx: fix duplicate idr_remove
[karo-tx-linux.git] / drivers / char / tty_io.c
index 1eda82b31a61e8d890d551ead0488fdb675993eb..b1f9a1582dd7d4be0ef363eeda933c7416bfd369 100644 (file)
@@ -130,7 +130,7 @@ LIST_HEAD(tty_drivers);                     /* linked list of tty drivers */
 
 /* Semaphore to protect creating and releasing a tty. This is shared with
    vt.c for deeply disgusting hack reasons */
-DECLARE_MUTEX(tty_sem);
+DEFINE_MUTEX(tty_mutex);
 
 #ifdef CONFIG_UNIX98_PTYS
 extern struct tty_driver *ptm_driver;  /* Unix98 pty masters; for /dev/ptmx */
@@ -253,6 +253,7 @@ static void tty_buffer_free_all(struct tty_struct *tty)
 
 static void tty_buffer_init(struct tty_struct *tty)
 {
+       spin_lock_init(&tty->buf.lock);
        tty->buf.head = NULL;
        tty->buf.tail = NULL;
        tty->buf.free = NULL;
@@ -266,6 +267,9 @@ static struct tty_buffer *tty_buffer_alloc(size_t size)
        p->used = 0;
        p->size = size;
        p->next = NULL;
+       p->active = 0;
+       p->commit = 0;
+       p->read = 0;
        p->char_buf_ptr = (char *)(p->data);
        p->flag_buf_ptr = (unsigned char *)p->char_buf_ptr + size;
 /*     printk("Flip create %p\n", p); */
@@ -296,8 +300,10 @@ static struct tty_buffer *tty_buffer_find(struct tty_struct *tty, size_t size)
                        *tbh = t->next;
                        t->next = NULL;
                        t->used = 0;
+                       t->commit = 0;
+                       t->read = 0;
                        /* DEBUG ONLY */
-                       memset(t->data, '*', size);
+/*                     memset(t->data, '*', size); */
 /*                     printk("Flip recycle %p\n", t); */
                        return t;
                }
@@ -312,32 +318,43 @@ static struct tty_buffer *tty_buffer_find(struct tty_struct *tty, size_t size)
 
 int tty_buffer_request_room(struct tty_struct *tty, size_t size)
 {
-       struct tty_buffer *b = tty->buf.head, *n;
-       int left = 0;
+       struct tty_buffer *b, *n;
+       int left;
+       unsigned long flags;
+
+       spin_lock_irqsave(&tty->buf.lock, flags);
 
        /* OPTIMISATION: We could keep a per tty "zero" sized buffer to
           remove this conditional if its worth it. This would be invisible
           to the callers */
-       if(b != NULL)
+       if ((b = tty->buf.tail) != NULL) {
                left = b->size - b->used;
-       if(left >= size)
-               return size;
-       /* This is the slow path - looking for new buffers to use */
-       n = tty_buffer_find(tty, size);
-       if(n == NULL)
-               return left;
-       n->next = b;
-       if(b != NULL)
-               b->next = n;
-       else
-               tty->buf.head = n;
-       tty->buf.tail = n;
+               b->active = 1;
+       } else
+               left = 0;
+
+       if (left < size) {
+               /* This is the slow path - looking for new buffers to use */
+               if ((n = tty_buffer_find(tty, size)) != NULL) {
+                       if (b != NULL) {
+                               b->next = n;
+                               b->active = 0;
+                               b->commit = b->used;
+                       } else
+                               tty->buf.head = n;
+                       tty->buf.tail = n;
+                       n->active = 1;
+               } else
+                       size = left;
+       }
+
+       spin_unlock_irqrestore(&tty->buf.lock, flags);
        return size;
 }
-
 EXPORT_SYMBOL_GPL(tty_buffer_request_room);
 
-int tty_insert_flip_string(struct tty_struct *tty, unsigned char *chars, size_t size)
+int tty_insert_flip_string(struct tty_struct *tty, const unsigned char *chars,
+                               size_t size)
 {
        int copied = 0;
        do {
@@ -351,17 +368,16 @@ int tty_insert_flip_string(struct tty_struct *tty, unsigned char *chars, size_t
                tb->used += space;
                copied += space;
                chars += space;
-/*             printk("Flip insert %d.\n", space); */
        }
        /* There is a small chance that we need to split the data over
           several buffers. If this is the case we must loop */
        while (unlikely(size > copied));
        return copied;
 }
+EXPORT_SYMBOL(tty_insert_flip_string);
 
-EXPORT_SYMBOL_GPL(tty_insert_flip_string);
-
-int tty_insert_flip_string_flags(struct tty_struct *tty, unsigned char *chars, char *flags, size_t size)
+int tty_insert_flip_string_flags(struct tty_struct *tty,
+               const unsigned char *chars, const char *flags, size_t size)
 {
        int copied = 0;
        do {
@@ -382,9 +398,20 @@ int tty_insert_flip_string_flags(struct tty_struct *tty, unsigned char *chars, c
        while (unlikely(size > copied));
        return copied;
 }
-
 EXPORT_SYMBOL_GPL(tty_insert_flip_string_flags);
 
+void tty_schedule_flip(struct tty_struct *tty)
+{
+       unsigned long flags;
+       spin_lock_irqsave(&tty->buf.lock, flags);
+       if (tty->buf.tail != NULL) {
+               tty->buf.tail->active = 0;
+               tty->buf.tail->commit = tty->buf.tail->used;
+       }
+       spin_unlock_irqrestore(&tty->buf.lock, flags);
+       schedule_delayed_work(&tty->buf.work, 1);
+}
+EXPORT_SYMBOL(tty_schedule_flip);
 
 /*
  *     Prepare a block of space in the buffer for data. Returns the length
@@ -397,10 +424,12 @@ EXPORT_SYMBOL_GPL(tty_insert_flip_string_flags);
 int tty_prepare_flip_string(struct tty_struct *tty, unsigned char **chars, size_t size)
 {
        int space = tty_buffer_request_room(tty, size);
-       struct tty_buffer *tb = tty->buf.tail;
-       *chars = tb->char_buf_ptr + tb->used;
-       memset(tb->flag_buf_ptr + tb->used, TTY_NORMAL, space);
-       tb->used += space;
+       if (likely(space)) {
+               struct tty_buffer *tb = tty->buf.tail;
+               *chars = tb->char_buf_ptr + tb->used;
+               memset(tb->flag_buf_ptr + tb->used, TTY_NORMAL, space);
+               tb->used += space;
+       }
        return space;
 }
 
@@ -417,10 +446,12 @@ EXPORT_SYMBOL_GPL(tty_prepare_flip_string);
 int tty_prepare_flip_string_flags(struct tty_struct *tty, unsigned char **chars, char **flags, size_t size)
 {
        int space = tty_buffer_request_room(tty, size);
-       struct tty_buffer *tb = tty->buf.tail;
-       *chars = tb->char_buf_ptr + tb->used;
-       *flags = tb->flag_buf_ptr + tb->used;
-       tb->used += space;
+       if (likely(space)) {
+               struct tty_buffer *tb = tty->buf.tail;
+               *chars = tb->char_buf_ptr + tb->used;
+               *flags = tb->flag_buf_ptr + tb->used;
+               tb->used += space;
+       }
        return space;
 }
 
@@ -522,14 +553,12 @@ void tty_ldisc_put(int disc)
        struct tty_ldisc *ld;
        unsigned long flags;
        
-       if (disc < N_TTY || disc >= NR_LDISCS)
-               BUG();
+       BUG_ON(disc < N_TTY || disc >= NR_LDISCS);
                
        spin_lock_irqsave(&tty_ldisc_lock, flags);
        ld = &tty_ldiscs[disc];
-       if(ld->refcount == 0)
-               BUG();
-       ld->refcount --;
+       BUG_ON(ld->refcount == 0);
+       ld->refcount--;
        module_put(ld->owner);
        spin_unlock_irqrestore(&tty_ldisc_lock, flags);
 }
@@ -624,8 +653,7 @@ void tty_ldisc_deref(struct tty_ldisc *ld)
 {
        unsigned long flags;
 
-       if(ld == NULL)
-               BUG();
+       BUG_ON(ld == NULL);
                
        spin_lock_irqsave(&tty_ldisc_lock, flags);
        if(ld->refcount == 0)
@@ -1076,8 +1104,8 @@ static void do_tty_hangup(void *data)
                                p->signal->tty = NULL;
                        if (!p->signal->leader)
                                continue;
-                       send_group_sig_info(SIGHUP, SEND_SIG_PRIV, p);
-                       send_group_sig_info(SIGCONT, SEND_SIG_PRIV, p);
+                       group_send_sig_info(SIGHUP, SEND_SIG_PRIV, p);
+                       group_send_sig_info(SIGCONT, SEND_SIG_PRIV, p);
                        if (tty->pgrp > 0)
                                p->signal->tty_old_pgrp = tty->pgrp;
                } while_each_task_pid(tty->session, PIDTYPE_SID, p);
@@ -1167,11 +1195,11 @@ void disassociate_ctty(int on_exit)
 
        lock_kernel();
 
-       down(&tty_sem);
+       mutex_lock(&tty_mutex);
        tty = current->signal->tty;
        if (tty) {
                tty_pgrp = tty->pgrp;
-               up(&tty_sem);
+               mutex_unlock(&tty_mutex);
                if (on_exit && tty->driver->type != TTY_DRIVER_TYPE_PTY)
                        tty_vhangup(tty);
        } else {
@@ -1179,7 +1207,7 @@ void disassociate_ctty(int on_exit)
                        kill_pg(current->signal->tty_old_pgrp, SIGHUP, on_exit);
                        kill_pg(current->signal->tty_old_pgrp, SIGCONT, on_exit);
                }
-               up(&tty_sem);
+               mutex_unlock(&tty_mutex);
                unlock_kernel();        
                return;
        }
@@ -1190,7 +1218,7 @@ void disassociate_ctty(int on_exit)
        }
 
        /* Must lock changes to tty_old_pgrp */
-       down(&tty_sem);
+       mutex_lock(&tty_mutex);
        current->signal->tty_old_pgrp = 0;
        tty->session = 0;
        tty->pgrp = -1;
@@ -1201,7 +1229,7 @@ void disassociate_ctty(int on_exit)
                p->signal->tty = NULL;
        } while_each_task_pid(current->signal->session, PIDTYPE_SID, p);
        read_unlock(&tasklist_lock);
-       up(&tty_sem);
+       mutex_unlock(&tty_mutex);
        unlock_kernel();
 }
 
@@ -1285,7 +1313,7 @@ static inline ssize_t do_tty_write(
        ssize_t ret = 0, written = 0;
        unsigned int chunk;
        
-       if (down_interruptible(&tty->atomic_write)) {
+       if (mutex_lock_interruptible(&tty->atomic_write_lock)) {
                return -ERESTARTSYS;
        }
 
@@ -1308,7 +1336,7 @@ static inline ssize_t do_tty_write(
        if (count < chunk)
                chunk = count;
 
-       /* write_buf/write_cnt is protected by the atomic_write semaphore */
+       /* write_buf/write_cnt is protected by the atomic_write_lock mutex */
        if (tty->write_cnt < chunk) {
                unsigned char *buf;
 
@@ -1317,7 +1345,7 @@ static inline ssize_t do_tty_write(
 
                buf = kmalloc(chunk, GFP_KERNEL);
                if (!buf) {
-                       up(&tty->atomic_write);
+                       mutex_unlock(&tty->atomic_write_lock);
                        return -ENOMEM;
                }
                kfree(tty->write_buf);
@@ -1353,7 +1381,7 @@ static inline ssize_t do_tty_write(
                inode->i_mtime = current_fs_time(inode->i_sb);
                ret = written;
        }
-       up(&tty->atomic_write);
+       mutex_unlock(&tty->atomic_write_lock);
        return ret;
 }
 
@@ -1421,8 +1449,8 @@ static inline void tty_line_name(struct tty_driver *driver, int index, char *p)
 
 /*
  * WSH 06/09/97: Rewritten to remove races and properly clean up after a
- * failed open.  The new code protects the open with a semaphore, so it's
- * really quite straightforward.  The semaphore locking can probably be
+ * failed open.  The new code protects the open with a mutex, so it's
+ * really quite straightforward.  The mutex locking can probably be
  * relaxed for the (most common) case of reopening a tty.
  */
 static int init_dev(struct tty_driver *driver, int idx,
@@ -1619,7 +1647,7 @@ fast_track:
 success:
        *ret_tty = tty;
        
-       /* All paths come through here to release the semaphore */
+       /* All paths come through here to release the mutex */
 end_init:
        return retval;
 
@@ -1816,11 +1844,10 @@ static void release_dev(struct file * filp)
                /* Guard against races with tty->count changes elsewhere and
                   opens on /dev/tty */
                   
-               down(&tty_sem);
+               mutex_lock(&tty_mutex);
                tty_closing = tty->count <= 1;
                o_tty_closing = o_tty &&
                        (o_tty->count <= (pty_master ? 1 : 0));
-               up(&tty_sem);
                do_sleep = 0;
 
                if (tty_closing) {
@@ -1848,6 +1875,7 @@ static void release_dev(struct file * filp)
 
                printk(KERN_WARNING "release_dev: %s: read/write wait queue "
                                    "active!\n", tty_name(tty, buf));
+               mutex_unlock(&tty_mutex);
                schedule();
        }       
 
@@ -1856,8 +1884,6 @@ static void release_dev(struct file * filp)
         * both sides, and we've completed the last operation that could 
         * block, so it's safe to proceed with closing.
         */
-        
-       down(&tty_sem);
        if (pty_master) {
                if (--o_tty->count < 0) {
                        printk(KERN_WARNING "release_dev: bad pty slave count "
@@ -1871,7 +1897,6 @@ static void release_dev(struct file * filp)
                       tty->count, tty_name(tty, buf));
                tty->count = 0;
        }
-       up(&tty_sem);
        
        /*
         * We've decremented tty->count, so we need to remove this file
@@ -1916,6 +1941,8 @@ static void release_dev(struct file * filp)
                read_unlock(&tasklist_lock);
        }
 
+       mutex_unlock(&tty_mutex);
+
        /* check whether both sides are closing ... */
        if (!tty_closing || (o_tty && !o_tty_closing))
                return;
@@ -2020,11 +2047,11 @@ retry_open:
        index  = -1;
        retval = 0;
        
-       down(&tty_sem);
+       mutex_lock(&tty_mutex);
 
        if (device == MKDEV(TTYAUX_MAJOR,0)) {
                if (!current->signal->tty) {
-                       up(&tty_sem);
+                       mutex_unlock(&tty_mutex);
                        return -ENXIO;
                }
                driver = current->signal->tty->driver;
@@ -2050,18 +2077,18 @@ retry_open:
                        noctty = 1;
                        goto got_driver;
                }
-               up(&tty_sem);
+               mutex_unlock(&tty_mutex);
                return -ENODEV;
        }
 
        driver = get_tty_driver(device, &index);
        if (!driver) {
-               up(&tty_sem);
+               mutex_unlock(&tty_mutex);
                return -ENODEV;
        }
 got_driver:
        retval = init_dev(driver, index, &tty);
-       up(&tty_sem);
+       mutex_unlock(&tty_mutex);
        if (retval)
                return retval;
 
@@ -2147,9 +2174,9 @@ static int ptmx_open(struct inode * inode, struct file * filp)
        }
        up(&allocated_ptys_lock);
 
-       down(&tty_sem);
+       mutex_lock(&tty_mutex);
        retval = init_dev(ptm_driver, index, &tty);
-       up(&tty_sem);
+       mutex_unlock(&tty_mutex);
        
        if (retval)
                goto out;
@@ -2168,6 +2195,7 @@ static int ptmx_open(struct inode * inode, struct file * filp)
                return 0;
 out1:
        release_dev(filp);
+       return retval;
 out:
        down(&allocated_ptys_lock);
        idr_remove(&allocated_ptys, index);
@@ -2655,7 +2683,7 @@ static void __do_SAK(void *arg)
        tty_hangup(tty);
 #else
        struct tty_struct *tty = arg;
-       struct task_struct *p;
+       struct task_struct *g, *p;
        int session;
        int             i;
        struct file     *filp;
@@ -2676,8 +2704,18 @@ static void __do_SAK(void *arg)
                tty->driver->flush_buffer(tty);
        
        read_lock(&tasklist_lock);
+       /* Kill the entire session */
        do_each_task_pid(session, PIDTYPE_SID, p) {
-               if (p->signal->tty == tty || session > 0) {
+               printk(KERN_NOTICE "SAK: killed process %d"
+                       " (%s): p->signal->session==tty->session\n",
+                       p->pid, p->comm);
+               send_sig(SIGKILL, p, 1);
+       } while_each_task_pid(session, PIDTYPE_SID, p);
+       /* Now kill any processes that happen to have the
+        * tty open.
+        */
+       do_each_thread(g, p) {
+               if (p->signal->tty == tty) {
                        printk(KERN_NOTICE "SAK: killed process %d"
                            " (%s): p->signal->session==tty->session\n",
                            p->pid, p->comm);
@@ -2704,7 +2742,7 @@ static void __do_SAK(void *arg)
                        rcu_read_unlock();
                }
                task_unlock(p);
-       } while_each_task_pid(session, PIDTYPE_SID, p);
+       } while_each_thread(g, p);
        read_unlock(&tasklist_lock);
 #endif
 }
@@ -2736,6 +2774,9 @@ static void flush_to_ldisc(void *private_)
        unsigned long   flags;
        struct tty_ldisc *disc;
        struct tty_buffer *tbuf;
+       int count;
+       char *char_buf;
+       unsigned char *flag_buf;
 
        disc = tty_ldisc_ref(tty);
        if (disc == NULL)       /*  !TTY_LDISC */
@@ -2748,19 +2789,24 @@ static void flush_to_ldisc(void *private_)
                schedule_delayed_work(&tty->buf.work, 1);
                goto out;
        }
-       spin_lock_irqsave(&tty->read_lock, flags);
+       spin_lock_irqsave(&tty->buf.lock, flags);
        while((tbuf = tty->buf.head) != NULL) {
+               while ((count = tbuf->commit - tbuf->read) != 0) {
+                       char_buf = tbuf->char_buf_ptr + tbuf->read;
+                       flag_buf = tbuf->flag_buf_ptr + tbuf->read;
+                       tbuf->read += count;
+                       spin_unlock_irqrestore(&tty->buf.lock, flags);
+                       disc->receive_buf(tty, char_buf, flag_buf, count);
+                       spin_lock_irqsave(&tty->buf.lock, flags);
+               }
+               if (tbuf->active)
+                       break;
                tty->buf.head = tbuf->next;
-               spin_unlock_irqrestore(&tty->read_lock, flags);
-               /* printk("Process buffer %p for %d\n", tbuf, tbuf->used); */
-               disc->receive_buf(tty, tbuf->char_buf_ptr,
-                                      tbuf->flag_buf_ptr,
-                                      tbuf->used);
-               spin_lock_irqsave(&tty->read_lock, flags);
+               if (tty->buf.head == NULL)
+                       tty->buf.tail = NULL;
                tty_buffer_free(tty, tbuf);
        }
-       tty->buf.tail = NULL;
-       spin_unlock_irqrestore(&tty->read_lock, flags);
+       spin_unlock_irqrestore(&tty->buf.lock, flags);
 out:
        tty_ldisc_deref(disc);
 }
@@ -2852,6 +2898,14 @@ EXPORT_SYMBOL(tty_get_baud_rate);
 
 void tty_flip_buffer_push(struct tty_struct *tty)
 {
+       unsigned long flags;
+       spin_lock_irqsave(&tty->buf.lock, flags);
+       if (tty->buf.tail != NULL) {
+               tty->buf.tail->active = 0;
+               tty->buf.tail->commit = tty->buf.tail->used;
+       }
+       spin_unlock_irqrestore(&tty->buf.lock, flags);
+
        if (tty->low_latency)
                flush_to_ldisc((void *) tty);
        else
@@ -2879,8 +2933,8 @@ static void initialize_tty_struct(struct tty_struct *tty)
        init_waitqueue_head(&tty->write_wait);
        init_waitqueue_head(&tty->read_wait);
        INIT_WORK(&tty->hangup_work, do_tty_hangup, tty);
-       sema_init(&tty->atomic_read, 1);
-       sema_init(&tty->atomic_write, 1);
+       mutex_init(&tty->atomic_read_lock);
+       mutex_init(&tty->atomic_write_lock);
        spin_lock_init(&tty->read_lock);
        INIT_LIST_HEAD(&tty->tty_files);
        INIT_WORK(&tty->SAK_work, NULL, NULL);