Blame view

arch/um/drivers/port_kern.c 6.45 KB
67608e0c8   Jeff Dike   [PATCH] uml: port...
1
  /*
e99525f97   Jeff Dike   uml: console subs...
2
   * Copyright (C) 2001 - 2007 Jeff Dike (jdike@{linux.intel,addtoit}.com)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
3
4
   * Licensed under the GPL
   */
37185b332   Al Viro   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   Jeff Dike   uml: console subs...
15
  #include "port.h"
1da177e4c   Linus Torvalds   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   Al Viro   [PATCH] uml pt_re...
43
  static irqreturn_t pipe_interrupt(int irq, void *data)
1da177e4c   Linus Torvalds   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   Jeff Dike   uml: console subs...
49
50
  	if (fd < 0) {
  		if (fd == -EAGAIN)
67608e0c8   Jeff Dike   [PATCH] uml: port...
51
  			return IRQ_NONE;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
52

67608e0c8   Jeff Dike   [PATCH] uml: port...
53
54
  		printk(KERN_ERR "pipe_interrupt : os_rcv_fd returned %d
  ",
1da177e4c   Linus Torvalds   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   Jeff Dike   [PATCH] uml: port...
65
  	return IRQ_HANDLED;
1da177e4c   Linus Torvalds   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   Jeff Dike   uml: console subs...
83
  	int fd, socket[2], pid;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
84
85
  
  	fd = port_connection(port->fd, socket, &pid);
e99525f97   Jeff Dike   uml: console subs...
86
87
  	if (fd < 0) {
  		if (fd != -EAGAIN)
1da177e4c   Linus Torvalds   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   Jeff Dike   uml: console subs...
95
  	if (conn == NULL) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
96
97
98
99
100
  		printk(KERN_ERR "port_accept : failed to allocate "
  		       "connection
  ");
  		goto out_close;
  	}
67608e0c8   Jeff Dike   [PATCH] uml: port...
101
  	*conn = ((struct connection)
1da177e4c   Linus Torvalds   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   Jeff Dike   uml: console subs...
107
  	if (um_request_irq(TELNETD_IRQ, socket[0], IRQ_READ, pipe_interrupt,
aab944606   Theodore Ts'o   um: remove IRQF_S...
108
  			  IRQF_SHARED, "telnetd", conn)) {
1da177e4c   Linus Torvalds   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   Jeff Dike   uml: console subs...
114
  	if (atomic_read(&port->wait_count) == 0) {
a6ea4ccee   Jeff Dike   uml: rename os_{r...
115
  		os_write_file(fd, NO_WAITER_MSG, sizeof(NO_WAITER_MSG));
e99525f97   Jeff Dike   uml: console subs...
116
117
  		printk(KERN_ERR "No one waiting for port
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
118
119
  	}
  	list_add(&conn->list, &port->pending);
67608e0c8   Jeff Dike   [PATCH] uml: port...
120
  	return 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
121
122
123
124
125
  
   out_free:
  	kfree(conn);
   out_close:
  	os_close_file(fd);
e99525f97   Jeff Dike   uml: console subs...
126
  	os_kill_process(pid, 1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
127
   out:
e99525f97   Jeff Dike   uml: console subs...
128
  	return 0;
67608e0c8   Jeff Dike   [PATCH] uml: port...
129
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
130

2aa9c5db8   Daniel Walker   uml: port mutex c...
131
  static DEFINE_MUTEX(ports_mutex);
c59bce626   Jeff Dike   [PATCH] uml: use ...
132
  static LIST_HEAD(ports);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
133

e99525f97   Jeff Dike   uml: console subs...
134
  static void port_work_proc(struct work_struct *unused)
1da177e4c   Linus Torvalds   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   Jeff Dike   uml: console subs...
141
  	list_for_each(ele, &ports) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
142
  		port = list_entry(ele, struct port_list, list);
e99525f97   Jeff Dike   uml: console subs...
143
  		if (!port->has_connection)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
144
  			continue;
e99525f97   Jeff Dike   uml: console subs...
145

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
146
  		reactivate_fd(port->fd, ACCEPT_IRQ);
e99525f97   Jeff Dike   uml: console subs...
147
148
  		while (port_accept(port))
  			;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
149
150
151
152
  		port->has_connection = 0;
  	}
  	local_irq_restore(flags);
  }
6d5aefb8e   David Howells   WorkQueue: Fix up...
153
  DECLARE_WORK(port_work, port_work_proc);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
154

7bea96fd2   Al Viro   [PATCH] uml pt_re...
155
  static irqreturn_t port_interrupt(int irq, void *data)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
156
157
158
159
160
  {
  	struct port_list *port = data;
  
  	port->has_connection = 1;
  	schedule_work(&port_work);
67608e0c8   Jeff Dike   [PATCH] uml: port...
161
162
  	return IRQ_HANDLED;
  }
1da177e4c   Linus Torvalds   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   Daniel Walker   uml: port mutex c...
170
  	mutex_lock(&ports_mutex);
e99525f97   Jeff Dike   uml: console subs...
171
  	list_for_each(ele, &ports) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
172
  		port = list_entry(ele, struct port_list, list);
e99525f97   Jeff Dike   uml: console subs...
173
174
  		if (port->port == port_num)
  			goto found;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
175
176
  	}
  	port = kmalloc(sizeof(struct port_list), GFP_KERNEL);
e99525f97   Jeff Dike   uml: console subs...
177
  	if (port == NULL) {
1da177e4c   Linus Torvalds   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   Jeff Dike   uml: console subs...
184
  	if (fd < 0) {
1da177e4c   Linus Torvalds   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   Jeff Dike   uml: console subs...
190
191
  
  	if (um_request_irq(ACCEPT_IRQ, fd, IRQ_READ, port_interrupt,
aab944606   Theodore Ts'o   um: remove IRQF_S...
192
  			  IRQF_SHARED, "port", port)) {
1da177e4c   Linus Torvalds   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   Jeff Dike   [PATCH] uml: port...
197
  	*port = ((struct port_list)
1da177e4c   Linus Torvalds   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   Jeff Dike   uml: console subs...
211
  	if (dev == NULL) {
1da177e4c   Linus Torvalds   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   Linus Torvalds   Linux-2.6.12-rc2
221
222
   out_close:
  	os_close_file(fd);
79f662334   Jeff Dike   uml: fix error cl...
223
224
   out_free:
  	kfree(port);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
225
   out:
2aa9c5db8   Daniel Walker   uml: port mutex c...
226
  	mutex_unlock(&ports_mutex);
67608e0c8   Jeff Dike   [PATCH] uml: port...
227
  	return dev;
1da177e4c   Linus Torvalds   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   Jeff Dike   [PATCH] uml: port...
236
  	atomic_inc(&port->wait_count);
e99525f97   Jeff Dike   uml: console subs...
237
  	while (1) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
238
  		fd = -ERESTARTSYS;
e99525f97   Jeff Dike   uml: console subs...
239
  		if (wait_for_completion_interruptible(&port->done))
67608e0c8   Jeff Dike   [PATCH] uml: port...
240
  			goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
241
242
  
  		spin_lock(&port->lock);
67608e0c8   Jeff Dike   [PATCH] uml: port...
243
  		conn = list_entry(port->connections.next, struct connection,
1da177e4c   Linus Torvalds   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   Jeff Dike   [PATCH] uml: port...
251
  		os_close_file(conn->socket[1]);
1da177e4c   Linus Torvalds   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   Jeff Dike   [PATCH] uml: port...
256
  		 * connection.  Then we loop here throwing out failed
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
257
258
  		 * connections until a good one is found.
  		 */
fa7a0449e   Richard Weinberger   um: Implement um_...
259
  		um_free_irq(TELNETD_IRQ, conn);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
260

e99525f97   Jeff Dike   uml: console subs...
261
262
  		if (conn->fd >= 0)
  			break;
1da177e4c   Linus Torvalds   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   Jeff Dike   uml: console subs...
279
  	if (dev->helper_pid != -1)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
280
  		os_kill_process(dev->helper_pid, 0);
e99525f97   Jeff Dike   uml: console subs...
281
  	if (dev->telnetd_pid != -1)
1da177e4c   Linus Torvalds   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   Jeff Dike   uml: console subs...
299
  	list_for_each(ele, &ports) {
1da177e4c   Linus Torvalds   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);