11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com) 31da177e4SLinus Torvalds * Licensed under the GPL 41da177e4SLinus Torvalds */ 51da177e4SLinus Torvalds 61da177e4SLinus Torvalds #include "linux/list.h" 71da177e4SLinus Torvalds #include "linux/sched.h" 81da177e4SLinus Torvalds #include "linux/slab.h" 91da177e4SLinus Torvalds #include "linux/interrupt.h" 101da177e4SLinus Torvalds #include "linux/spinlock.h" 111da177e4SLinus Torvalds #include "linux/errno.h" 121da177e4SLinus Torvalds #include "asm/atomic.h" 131da177e4SLinus Torvalds #include "asm/semaphore.h" 141da177e4SLinus Torvalds #include "asm/errno.h" 151da177e4SLinus Torvalds #include "kern_util.h" 161da177e4SLinus Torvalds #include "kern.h" 171da177e4SLinus Torvalds #include "irq_user.h" 181da177e4SLinus Torvalds #include "irq_kern.h" 191da177e4SLinus Torvalds #include "port.h" 201da177e4SLinus Torvalds #include "init.h" 211da177e4SLinus Torvalds #include "os.h" 221da177e4SLinus Torvalds 231da177e4SLinus Torvalds struct port_list { 241da177e4SLinus Torvalds struct list_head list; 251da177e4SLinus Torvalds atomic_t wait_count; 261da177e4SLinus Torvalds int has_connection; 271da177e4SLinus Torvalds struct completion done; 281da177e4SLinus Torvalds int port; 291da177e4SLinus Torvalds int fd; 301da177e4SLinus Torvalds spinlock_t lock; 311da177e4SLinus Torvalds struct list_head pending; 321da177e4SLinus Torvalds struct list_head connections; 331da177e4SLinus Torvalds }; 341da177e4SLinus Torvalds 351da177e4SLinus Torvalds struct port_dev { 361da177e4SLinus Torvalds struct port_list *port; 371da177e4SLinus Torvalds int helper_pid; 381da177e4SLinus Torvalds int telnetd_pid; 391da177e4SLinus Torvalds }; 401da177e4SLinus Torvalds 411da177e4SLinus Torvalds struct connection { 421da177e4SLinus Torvalds struct list_head list; 431da177e4SLinus Torvalds int fd; 441da177e4SLinus Torvalds int helper_pid; 451da177e4SLinus Torvalds int socket[2]; 461da177e4SLinus Torvalds int telnetd_pid; 471da177e4SLinus Torvalds struct port_list *port; 481da177e4SLinus Torvalds }; 491da177e4SLinus Torvalds 507bea96fdSAl Viro static irqreturn_t pipe_interrupt(int irq, void *data) 511da177e4SLinus Torvalds { 521da177e4SLinus Torvalds struct connection *conn = data; 531da177e4SLinus Torvalds int fd; 541da177e4SLinus Torvalds 551da177e4SLinus Torvalds fd = os_rcv_fd(conn->socket[0], &conn->helper_pid); 561da177e4SLinus Torvalds if(fd < 0){ 571da177e4SLinus Torvalds if(fd == -EAGAIN) 5867608e0cSJeff Dike return IRQ_NONE; 591da177e4SLinus Torvalds 601da177e4SLinus Torvalds printk(KERN_ERR "pipe_interrupt : os_rcv_fd returned %d\n", 611da177e4SLinus Torvalds -fd); 621da177e4SLinus Torvalds os_close_file(conn->fd); 631da177e4SLinus Torvalds } 641da177e4SLinus Torvalds 651da177e4SLinus Torvalds list_del(&conn->list); 661da177e4SLinus Torvalds 671da177e4SLinus Torvalds conn->fd = fd; 681da177e4SLinus Torvalds list_add(&conn->list, &conn->port->connections); 691da177e4SLinus Torvalds 701da177e4SLinus Torvalds complete(&conn->port->done); 7167608e0cSJeff Dike return IRQ_HANDLED; 721da177e4SLinus Torvalds } 731da177e4SLinus Torvalds 741da177e4SLinus Torvalds #define NO_WAITER_MSG \ 751da177e4SLinus Torvalds "****\n" \ 761da177e4SLinus Torvalds "There are currently no UML consoles waiting for port connections.\n" \ 771da177e4SLinus Torvalds "Either disconnect from one to make it available or activate some more\n" \ 781da177e4SLinus Torvalds "by enabling more consoles in the UML /etc/inittab.\n" \ 791da177e4SLinus Torvalds "****\n" 801da177e4SLinus Torvalds 811da177e4SLinus Torvalds static int port_accept(struct port_list *port) 821da177e4SLinus Torvalds { 831da177e4SLinus Torvalds struct connection *conn; 841da177e4SLinus Torvalds int fd, socket[2], pid, ret = 0; 851da177e4SLinus Torvalds 861da177e4SLinus Torvalds fd = port_connection(port->fd, socket, &pid); 871da177e4SLinus Torvalds if(fd < 0){ 881da177e4SLinus Torvalds if(fd != -EAGAIN) 891da177e4SLinus Torvalds printk(KERN_ERR "port_accept : port_connection " 901da177e4SLinus Torvalds "returned %d\n", -fd); 911da177e4SLinus Torvalds goto out; 921da177e4SLinus Torvalds } 931da177e4SLinus Torvalds 941da177e4SLinus Torvalds conn = kmalloc(sizeof(*conn), GFP_ATOMIC); 951da177e4SLinus Torvalds if(conn == NULL){ 961da177e4SLinus Torvalds printk(KERN_ERR "port_accept : failed to allocate " 971da177e4SLinus Torvalds "connection\n"); 981da177e4SLinus Torvalds goto out_close; 991da177e4SLinus Torvalds } 1001da177e4SLinus Torvalds *conn = ((struct connection) 1011da177e4SLinus Torvalds { .list = LIST_HEAD_INIT(conn->list), 1021da177e4SLinus Torvalds .fd = fd, 1031da177e4SLinus Torvalds .socket = { socket[0], socket[1] }, 1041da177e4SLinus Torvalds .telnetd_pid = pid, 1051da177e4SLinus Torvalds .port = port }); 1061da177e4SLinus Torvalds 1071da177e4SLinus Torvalds if(um_request_irq(TELNETD_IRQ, socket[0], IRQ_READ, pipe_interrupt, 108bd6aa650SThomas Gleixner IRQF_DISABLED | IRQF_SHARED | IRQF_SAMPLE_RANDOM, 1091da177e4SLinus Torvalds "telnetd", conn)){ 1101da177e4SLinus Torvalds printk(KERN_ERR "port_accept : failed to get IRQ for " 1111da177e4SLinus Torvalds "telnetd\n"); 1121da177e4SLinus Torvalds goto out_free; 1131da177e4SLinus Torvalds } 1141da177e4SLinus Torvalds 1151da177e4SLinus Torvalds if(atomic_read(&port->wait_count) == 0){ 116a6ea4cceSJeff Dike os_write_file(fd, NO_WAITER_MSG, sizeof(NO_WAITER_MSG)); 1171da177e4SLinus Torvalds printk("No one waiting for port\n"); 1181da177e4SLinus Torvalds } 1191da177e4SLinus Torvalds list_add(&conn->list, &port->pending); 12067608e0cSJeff Dike return 1; 1211da177e4SLinus Torvalds 1221da177e4SLinus Torvalds out_free: 1231da177e4SLinus Torvalds kfree(conn); 1241da177e4SLinus Torvalds out_close: 1251da177e4SLinus Torvalds os_close_file(fd); 1261da177e4SLinus Torvalds if(pid != -1) 1271da177e4SLinus Torvalds os_kill_process(pid, 1); 1281da177e4SLinus Torvalds out: 12967608e0cSJeff Dike return ret; 1301da177e4SLinus Torvalds } 1311da177e4SLinus Torvalds 132d832fc60SJeff Dike static DECLARE_MUTEX(ports_sem); 133c59bce62SJeff Dike static LIST_HEAD(ports); 1341da177e4SLinus Torvalds 1356d5aefb8SDavid Howells void port_work_proc(struct work_struct *unused) 1361da177e4SLinus Torvalds { 1371da177e4SLinus Torvalds struct port_list *port; 1381da177e4SLinus Torvalds struct list_head *ele; 1391da177e4SLinus Torvalds unsigned long flags; 1401da177e4SLinus Torvalds 1411da177e4SLinus Torvalds local_irq_save(flags); 1421da177e4SLinus Torvalds list_for_each(ele, &ports){ 1431da177e4SLinus Torvalds port = list_entry(ele, struct port_list, list); 1441da177e4SLinus Torvalds if(!port->has_connection) 1451da177e4SLinus Torvalds continue; 1461da177e4SLinus Torvalds reactivate_fd(port->fd, ACCEPT_IRQ); 1471da177e4SLinus Torvalds while(port_accept(port)) ; 1481da177e4SLinus Torvalds port->has_connection = 0; 1491da177e4SLinus Torvalds } 1501da177e4SLinus Torvalds local_irq_restore(flags); 1511da177e4SLinus Torvalds } 1521da177e4SLinus Torvalds 1536d5aefb8SDavid Howells DECLARE_WORK(port_work, port_work_proc); 1541da177e4SLinus Torvalds 1557bea96fdSAl Viro static irqreturn_t port_interrupt(int irq, void *data) 1561da177e4SLinus Torvalds { 1571da177e4SLinus Torvalds struct port_list *port = data; 1581da177e4SLinus Torvalds 1591da177e4SLinus Torvalds port->has_connection = 1; 1601da177e4SLinus Torvalds schedule_work(&port_work); 16167608e0cSJeff Dike return IRQ_HANDLED; 1621da177e4SLinus Torvalds } 1631da177e4SLinus Torvalds 1641da177e4SLinus Torvalds void *port_data(int port_num) 1651da177e4SLinus Torvalds { 1661da177e4SLinus Torvalds struct list_head *ele; 1671da177e4SLinus Torvalds struct port_list *port; 1681da177e4SLinus Torvalds struct port_dev *dev = NULL; 1691da177e4SLinus Torvalds int fd; 1701da177e4SLinus Torvalds 1711da177e4SLinus Torvalds down(&ports_sem); 1721da177e4SLinus Torvalds list_for_each(ele, &ports){ 1731da177e4SLinus Torvalds port = list_entry(ele, struct port_list, list); 1741da177e4SLinus Torvalds if(port->port == port_num) goto found; 1751da177e4SLinus Torvalds } 1761da177e4SLinus Torvalds port = kmalloc(sizeof(struct port_list), GFP_KERNEL); 1771da177e4SLinus Torvalds if(port == NULL){ 1781da177e4SLinus Torvalds printk(KERN_ERR "Allocation of port list failed\n"); 1791da177e4SLinus Torvalds goto out; 1801da177e4SLinus Torvalds } 1811da177e4SLinus Torvalds 1821da177e4SLinus Torvalds fd = port_listen_fd(port_num); 1831da177e4SLinus Torvalds if(fd < 0){ 1841da177e4SLinus Torvalds printk(KERN_ERR "binding to port %d failed, errno = %d\n", 1851da177e4SLinus Torvalds port_num, -fd); 1861da177e4SLinus Torvalds goto out_free; 1871da177e4SLinus Torvalds } 1881da177e4SLinus Torvalds if(um_request_irq(ACCEPT_IRQ, fd, IRQ_READ, port_interrupt, 18967608e0cSJeff Dike IRQF_DISABLED | IRQF_SHARED | IRQF_SAMPLE_RANDOM, 19067608e0cSJeff Dike "port", port)){ 1911da177e4SLinus Torvalds printk(KERN_ERR "Failed to get IRQ for port %d\n", port_num); 1921da177e4SLinus Torvalds goto out_close; 1931da177e4SLinus Torvalds } 1941da177e4SLinus Torvalds 1951da177e4SLinus Torvalds *port = ((struct port_list) 1961da177e4SLinus Torvalds { .list = LIST_HEAD_INIT(port->list), 1971da177e4SLinus Torvalds .wait_count = ATOMIC_INIT(0), 1981da177e4SLinus Torvalds .has_connection = 0, 1991da177e4SLinus Torvalds .port = port_num, 2001da177e4SLinus Torvalds .fd = fd, 2011da177e4SLinus Torvalds .pending = LIST_HEAD_INIT(port->pending), 2021da177e4SLinus Torvalds .connections = LIST_HEAD_INIT(port->connections) }); 2031da177e4SLinus Torvalds spin_lock_init(&port->lock); 2041da177e4SLinus Torvalds init_completion(&port->done); 2051da177e4SLinus Torvalds list_add(&port->list, &ports); 2061da177e4SLinus Torvalds 2071da177e4SLinus Torvalds found: 2081da177e4SLinus Torvalds dev = kmalloc(sizeof(struct port_dev), GFP_KERNEL); 2091da177e4SLinus Torvalds if(dev == NULL){ 2101da177e4SLinus Torvalds printk(KERN_ERR "Allocation of port device entry failed\n"); 2111da177e4SLinus Torvalds goto out; 2121da177e4SLinus Torvalds } 2131da177e4SLinus Torvalds 2141da177e4SLinus Torvalds *dev = ((struct port_dev) { .port = port, 2151da177e4SLinus Torvalds .helper_pid = -1, 2161da177e4SLinus Torvalds .telnetd_pid = -1 }); 2171da177e4SLinus Torvalds goto out; 2181da177e4SLinus Torvalds 2191da177e4SLinus Torvalds out_close: 2201da177e4SLinus Torvalds os_close_file(fd); 22179f66233SJeff Dike out_free: 22279f66233SJeff Dike kfree(port); 2231da177e4SLinus Torvalds out: 2241da177e4SLinus Torvalds up(&ports_sem); 22567608e0cSJeff Dike return dev; 2261da177e4SLinus Torvalds } 2271da177e4SLinus Torvalds 2281da177e4SLinus Torvalds int port_wait(void *data) 2291da177e4SLinus Torvalds { 2301da177e4SLinus Torvalds struct port_dev *dev = data; 2311da177e4SLinus Torvalds struct connection *conn; 2321da177e4SLinus Torvalds struct port_list *port = dev->port; 2331da177e4SLinus Torvalds int fd; 2341da177e4SLinus Torvalds 2351da177e4SLinus Torvalds atomic_inc(&port->wait_count); 2361da177e4SLinus Torvalds while(1){ 2371da177e4SLinus Torvalds fd = -ERESTARTSYS; 2381da177e4SLinus Torvalds if(wait_for_completion_interruptible(&port->done)) 2391da177e4SLinus Torvalds goto out; 2401da177e4SLinus Torvalds 2411da177e4SLinus Torvalds spin_lock(&port->lock); 2421da177e4SLinus Torvalds 2431da177e4SLinus Torvalds conn = list_entry(port->connections.next, struct connection, 2441da177e4SLinus Torvalds list); 2451da177e4SLinus Torvalds list_del(&conn->list); 2461da177e4SLinus Torvalds spin_unlock(&port->lock); 2471da177e4SLinus Torvalds 2481da177e4SLinus Torvalds os_shutdown_socket(conn->socket[0], 1, 1); 2491da177e4SLinus Torvalds os_close_file(conn->socket[0]); 2501da177e4SLinus Torvalds os_shutdown_socket(conn->socket[1], 1, 1); 2511da177e4SLinus Torvalds os_close_file(conn->socket[1]); 2521da177e4SLinus Torvalds 2531da177e4SLinus Torvalds /* This is done here because freeing an IRQ can't be done 2541da177e4SLinus Torvalds * within the IRQ handler. So, pipe_interrupt always ups 2551da177e4SLinus Torvalds * the semaphore regardless of whether it got a successful 2561da177e4SLinus Torvalds * connection. Then we loop here throwing out failed 2571da177e4SLinus Torvalds * connections until a good one is found. 2581da177e4SLinus Torvalds */ 2591da177e4SLinus Torvalds free_irq(TELNETD_IRQ, conn); 2601da177e4SLinus Torvalds 2611da177e4SLinus Torvalds if(conn->fd >= 0) break; 2621da177e4SLinus Torvalds os_close_file(conn->fd); 2631da177e4SLinus Torvalds kfree(conn); 2641da177e4SLinus Torvalds } 2651da177e4SLinus Torvalds 2661da177e4SLinus Torvalds fd = conn->fd; 2671da177e4SLinus Torvalds dev->helper_pid = conn->helper_pid; 2681da177e4SLinus Torvalds dev->telnetd_pid = conn->telnetd_pid; 2691da177e4SLinus Torvalds kfree(conn); 2701da177e4SLinus Torvalds out: 2711da177e4SLinus Torvalds atomic_dec(&port->wait_count); 2721da177e4SLinus Torvalds return fd; 2731da177e4SLinus Torvalds } 2741da177e4SLinus Torvalds 2751da177e4SLinus Torvalds void port_remove_dev(void *d) 2761da177e4SLinus Torvalds { 2771da177e4SLinus Torvalds struct port_dev *dev = d; 2781da177e4SLinus Torvalds 2791da177e4SLinus Torvalds if(dev->helper_pid != -1) 2801da177e4SLinus Torvalds os_kill_process(dev->helper_pid, 0); 2811da177e4SLinus Torvalds if(dev->telnetd_pid != -1) 2821da177e4SLinus Torvalds os_kill_process(dev->telnetd_pid, 1); 2831da177e4SLinus Torvalds dev->helper_pid = -1; 2841da177e4SLinus Torvalds dev->telnetd_pid = -1; 2851da177e4SLinus Torvalds } 2861da177e4SLinus Torvalds 2871da177e4SLinus Torvalds void port_kern_free(void *d) 2881da177e4SLinus Torvalds { 2891da177e4SLinus Torvalds struct port_dev *dev = d; 2901da177e4SLinus Torvalds 2911da177e4SLinus Torvalds port_remove_dev(dev); 2921da177e4SLinus Torvalds kfree(dev); 2931da177e4SLinus Torvalds } 2941da177e4SLinus Torvalds 2951da177e4SLinus Torvalds static void free_port(void) 2961da177e4SLinus Torvalds { 2971da177e4SLinus Torvalds struct list_head *ele; 2981da177e4SLinus Torvalds struct port_list *port; 2991da177e4SLinus Torvalds 3001da177e4SLinus Torvalds list_for_each(ele, &ports){ 3011da177e4SLinus Torvalds port = list_entry(ele, struct port_list, list); 3021da177e4SLinus Torvalds free_irq_by_fd(port->fd); 3031da177e4SLinus Torvalds os_close_file(port->fd); 3041da177e4SLinus Torvalds } 3051da177e4SLinus Torvalds } 3061da177e4SLinus Torvalds 3071da177e4SLinus Torvalds __uml_exitcall(free_port); 308