Blame view
arch/um/drivers/port_kern.c
6.45 KB
67608e0c8 [PATCH] uml: port... |
1 |
/* |
e99525f97 uml: console subs... |
2 |
* Copyright (C) 2001 - 2007 Jeff Dike (jdike@{linux.intel,addtoit}.com) |
1da177e4c Linux-2.6.12-rc2 |
3 4 |
* Licensed under the GPL */ |
37185b332 um: get rid of po... |
5 6 7 8 9 10 11 12 13 14 |
#include <linux/completion.h> #include <linux/interrupt.h> #include <linux/list.h> #include <linux/mutex.h> #include <linux/slab.h> #include <linux/workqueue.h> #include <asm/atomic.h> #include <init.h> #include <irq_kern.h> #include <os.h> |
e99525f97 uml: console subs... |
15 |
#include "port.h" |
1da177e4c Linux-2.6.12-rc2 |
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
struct port_list { struct list_head list; atomic_t wait_count; int has_connection; struct completion done; int port; int fd; spinlock_t lock; struct list_head pending; struct list_head connections; }; struct port_dev { struct port_list *port; int helper_pid; int telnetd_pid; }; struct connection { struct list_head list; int fd; int helper_pid; int socket[2]; int telnetd_pid; struct port_list *port; }; |
7bea96fd2 [PATCH] uml pt_re... |
43 |
static irqreturn_t pipe_interrupt(int irq, void *data) |
1da177e4c Linux-2.6.12-rc2 |
44 45 46 47 48 |
{ struct connection *conn = data; int fd; fd = os_rcv_fd(conn->socket[0], &conn->helper_pid); |
e99525f97 uml: console subs... |
49 50 |
if (fd < 0) { if (fd == -EAGAIN) |
67608e0c8 [PATCH] uml: port... |
51 |
return IRQ_NONE; |
1da177e4c Linux-2.6.12-rc2 |
52 |
|
67608e0c8 [PATCH] uml: port... |
53 54 |
printk(KERN_ERR "pipe_interrupt : os_rcv_fd returned %d ", |
1da177e4c Linux-2.6.12-rc2 |
55 56 57 58 59 60 61 62 63 64 |
-fd); os_close_file(conn->fd); } list_del(&conn->list); conn->fd = fd; list_add(&conn->list, &conn->port->connections); complete(&conn->port->done); |
67608e0c8 [PATCH] uml: port... |
65 |
return IRQ_HANDLED; |
1da177e4c Linux-2.6.12-rc2 |
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
} #define NO_WAITER_MSG \ "**** " \ "There are currently no UML consoles waiting for port connections. " \ "Either disconnect from one to make it available or activate some more " \ "by enabling more consoles in the UML /etc/inittab. " \ "**** " static int port_accept(struct port_list *port) { struct connection *conn; |
e99525f97 uml: console subs... |
83 |
int fd, socket[2], pid; |
1da177e4c Linux-2.6.12-rc2 |
84 85 |
fd = port_connection(port->fd, socket, &pid); |
e99525f97 uml: console subs... |
86 87 |
if (fd < 0) { if (fd != -EAGAIN) |
1da177e4c Linux-2.6.12-rc2 |
88 89 90 91 92 93 94 |
printk(KERN_ERR "port_accept : port_connection " "returned %d ", -fd); goto out; } conn = kmalloc(sizeof(*conn), GFP_ATOMIC); |
e99525f97 uml: console subs... |
95 |
if (conn == NULL) { |
1da177e4c Linux-2.6.12-rc2 |
96 97 98 99 100 |
printk(KERN_ERR "port_accept : failed to allocate " "connection "); goto out_close; } |
67608e0c8 [PATCH] uml: port... |
101 |
*conn = ((struct connection) |
1da177e4c Linux-2.6.12-rc2 |
102 103 104 105 106 |
{ .list = LIST_HEAD_INIT(conn->list), .fd = fd, .socket = { socket[0], socket[1] }, .telnetd_pid = pid, .port = port }); |
e99525f97 uml: console subs... |
107 |
if (um_request_irq(TELNETD_IRQ, socket[0], IRQ_READ, pipe_interrupt, |
aab944606 um: remove IRQF_S... |
108 |
IRQF_SHARED, "telnetd", conn)) { |
1da177e4c Linux-2.6.12-rc2 |
109 110 111 112 113 |
printk(KERN_ERR "port_accept : failed to get IRQ for " "telnetd "); goto out_free; } |
e99525f97 uml: console subs... |
114 |
if (atomic_read(&port->wait_count) == 0) { |
a6ea4ccee uml: rename os_{r... |
115 |
os_write_file(fd, NO_WAITER_MSG, sizeof(NO_WAITER_MSG)); |
e99525f97 uml: console subs... |
116 117 |
printk(KERN_ERR "No one waiting for port "); |
1da177e4c Linux-2.6.12-rc2 |
118 119 |
} list_add(&conn->list, &port->pending); |
67608e0c8 [PATCH] uml: port... |
120 |
return 1; |
1da177e4c Linux-2.6.12-rc2 |
121 122 123 124 125 |
out_free: kfree(conn); out_close: os_close_file(fd); |
e99525f97 uml: console subs... |
126 |
os_kill_process(pid, 1); |
1da177e4c Linux-2.6.12-rc2 |
127 |
out: |
e99525f97 uml: console subs... |
128 |
return 0; |
67608e0c8 [PATCH] uml: port... |
129 |
} |
1da177e4c Linux-2.6.12-rc2 |
130 |
|
2aa9c5db8 uml: port mutex c... |
131 |
static DEFINE_MUTEX(ports_mutex); |
c59bce626 [PATCH] uml: use ... |
132 |
static LIST_HEAD(ports); |
1da177e4c Linux-2.6.12-rc2 |
133 |
|
e99525f97 uml: console subs... |
134 |
static void port_work_proc(struct work_struct *unused) |
1da177e4c Linux-2.6.12-rc2 |
135 136 137 138 139 140 |
{ struct port_list *port; struct list_head *ele; unsigned long flags; local_irq_save(flags); |
e99525f97 uml: console subs... |
141 |
list_for_each(ele, &ports) { |
1da177e4c Linux-2.6.12-rc2 |
142 |
port = list_entry(ele, struct port_list, list); |
e99525f97 uml: console subs... |
143 |
if (!port->has_connection) |
1da177e4c Linux-2.6.12-rc2 |
144 |
continue; |
e99525f97 uml: console subs... |
145 |
|
1da177e4c Linux-2.6.12-rc2 |
146 |
reactivate_fd(port->fd, ACCEPT_IRQ); |
e99525f97 uml: console subs... |
147 148 |
while (port_accept(port)) ; |
1da177e4c Linux-2.6.12-rc2 |
149 150 151 152 |
port->has_connection = 0; } local_irq_restore(flags); } |
6d5aefb8e WorkQueue: Fix up... |
153 |
DECLARE_WORK(port_work, port_work_proc); |
1da177e4c Linux-2.6.12-rc2 |
154 |
|
7bea96fd2 [PATCH] uml pt_re... |
155 |
static irqreturn_t port_interrupt(int irq, void *data) |
1da177e4c Linux-2.6.12-rc2 |
156 157 158 159 160 |
{ struct port_list *port = data; port->has_connection = 1; schedule_work(&port_work); |
67608e0c8 [PATCH] uml: port... |
161 162 |
return IRQ_HANDLED; } |
1da177e4c Linux-2.6.12-rc2 |
163 164 165 166 167 168 169 |
void *port_data(int port_num) { struct list_head *ele; struct port_list *port; struct port_dev *dev = NULL; int fd; |
2aa9c5db8 uml: port mutex c... |
170 |
mutex_lock(&ports_mutex); |
e99525f97 uml: console subs... |
171 |
list_for_each(ele, &ports) { |
1da177e4c Linux-2.6.12-rc2 |
172 |
port = list_entry(ele, struct port_list, list); |
e99525f97 uml: console subs... |
173 174 |
if (port->port == port_num) goto found; |
1da177e4c Linux-2.6.12-rc2 |
175 176 |
} port = kmalloc(sizeof(struct port_list), GFP_KERNEL); |
e99525f97 uml: console subs... |
177 |
if (port == NULL) { |
1da177e4c Linux-2.6.12-rc2 |
178 179 180 181 182 183 |
printk(KERN_ERR "Allocation of port list failed "); goto out; } fd = port_listen_fd(port_num); |
e99525f97 uml: console subs... |
184 |
if (fd < 0) { |
1da177e4c Linux-2.6.12-rc2 |
185 186 187 188 189 |
printk(KERN_ERR "binding to port %d failed, errno = %d ", port_num, -fd); goto out_free; } |
e99525f97 uml: console subs... |
190 191 |
if (um_request_irq(ACCEPT_IRQ, fd, IRQ_READ, port_interrupt, |
aab944606 um: remove IRQF_S... |
192 |
IRQF_SHARED, "port", port)) { |
1da177e4c Linux-2.6.12-rc2 |
193 194 195 196 |
printk(KERN_ERR "Failed to get IRQ for port %d ", port_num); goto out_close; } |
67608e0c8 [PATCH] uml: port... |
197 |
*port = ((struct port_list) |
1da177e4c Linux-2.6.12-rc2 |
198 199 200 201 202 203 204 205 206 207 208 209 210 |
{ .list = LIST_HEAD_INIT(port->list), .wait_count = ATOMIC_INIT(0), .has_connection = 0, .port = port_num, .fd = fd, .pending = LIST_HEAD_INIT(port->pending), .connections = LIST_HEAD_INIT(port->connections) }); spin_lock_init(&port->lock); init_completion(&port->done); list_add(&port->list, &ports); found: dev = kmalloc(sizeof(struct port_dev), GFP_KERNEL); |
e99525f97 uml: console subs... |
211 |
if (dev == NULL) { |
1da177e4c Linux-2.6.12-rc2 |
212 213 214 215 216 217 218 219 220 |
printk(KERN_ERR "Allocation of port device entry failed "); goto out; } *dev = ((struct port_dev) { .port = port, .helper_pid = -1, .telnetd_pid = -1 }); goto out; |
1da177e4c Linux-2.6.12-rc2 |
221 222 |
out_close: os_close_file(fd); |
79f662334 uml: fix error cl... |
223 224 |
out_free: kfree(port); |
1da177e4c Linux-2.6.12-rc2 |
225 |
out: |
2aa9c5db8 uml: port mutex c... |
226 |
mutex_unlock(&ports_mutex); |
67608e0c8 [PATCH] uml: port... |
227 |
return dev; |
1da177e4c Linux-2.6.12-rc2 |
228 229 230 231 232 233 234 235 |
} int port_wait(void *data) { struct port_dev *dev = data; struct connection *conn; struct port_list *port = dev->port; int fd; |
67608e0c8 [PATCH] uml: port... |
236 |
atomic_inc(&port->wait_count); |
e99525f97 uml: console subs... |
237 |
while (1) { |
1da177e4c Linux-2.6.12-rc2 |
238 |
fd = -ERESTARTSYS; |
e99525f97 uml: console subs... |
239 |
if (wait_for_completion_interruptible(&port->done)) |
67608e0c8 [PATCH] uml: port... |
240 |
goto out; |
1da177e4c Linux-2.6.12-rc2 |
241 242 |
spin_lock(&port->lock); |
67608e0c8 [PATCH] uml: port... |
243 |
conn = list_entry(port->connections.next, struct connection, |
1da177e4c Linux-2.6.12-rc2 |
244 245 246 247 248 249 250 |
list); list_del(&conn->list); spin_unlock(&port->lock); os_shutdown_socket(conn->socket[0], 1, 1); os_close_file(conn->socket[0]); os_shutdown_socket(conn->socket[1], 1, 1); |
67608e0c8 [PATCH] uml: port... |
251 |
os_close_file(conn->socket[1]); |
1da177e4c Linux-2.6.12-rc2 |
252 253 254 255 |
/* This is done here because freeing an IRQ can't be done * within the IRQ handler. So, pipe_interrupt always ups * the semaphore regardless of whether it got a successful |
67608e0c8 [PATCH] uml: port... |
256 |
* connection. Then we loop here throwing out failed |
1da177e4c Linux-2.6.12-rc2 |
257 258 |
* connections until a good one is found. */ |
fa7a0449e um: Implement um_... |
259 |
um_free_irq(TELNETD_IRQ, conn); |
1da177e4c Linux-2.6.12-rc2 |
260 |
|
e99525f97 uml: console subs... |
261 262 |
if (conn->fd >= 0) break; |
1da177e4c Linux-2.6.12-rc2 |
263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 |
os_close_file(conn->fd); kfree(conn); } fd = conn->fd; dev->helper_pid = conn->helper_pid; dev->telnetd_pid = conn->telnetd_pid; kfree(conn); out: atomic_dec(&port->wait_count); return fd; } void port_remove_dev(void *d) { struct port_dev *dev = d; |
e99525f97 uml: console subs... |
279 |
if (dev->helper_pid != -1) |
1da177e4c Linux-2.6.12-rc2 |
280 |
os_kill_process(dev->helper_pid, 0); |
e99525f97 uml: console subs... |
281 |
if (dev->telnetd_pid != -1) |
1da177e4c Linux-2.6.12-rc2 |
282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 |
os_kill_process(dev->telnetd_pid, 1); dev->helper_pid = -1; dev->telnetd_pid = -1; } void port_kern_free(void *d) { struct port_dev *dev = d; port_remove_dev(dev); kfree(dev); } static void free_port(void) { struct list_head *ele; struct port_list *port; |
e99525f97 uml: console subs... |
299 |
list_for_each(ele, &ports) { |
1da177e4c Linux-2.6.12-rc2 |
300 301 302 303 304 305 306 |
port = list_entry(ele, struct port_list, list); free_irq_by_fd(port->fd); os_close_file(port->fd); } } __uml_exitcall(free_port); |