Blame view

arch/um/drivers/port_kern.c 6.53 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
   */
e99525f97   Jeff Dike   uml: console subs...
5
  #include "linux/completion.h"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
6
  #include "linux/interrupt.h"
e99525f97   Jeff Dike   uml: console subs...
7
  #include "linux/list.h"
2aa9c5db8   Daniel Walker   uml: port mutex c...
8
  #include "linux/mutex.h"
5a0e3ad6a   Tejun Heo   include cleanup: ...
9
  #include "linux/slab.h"
d43c36dc6   Alexey Dobriyan   headers: remove s...
10
  #include "linux/workqueue.h"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
11
  #include "asm/atomic.h"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
12
  #include "init.h"
e99525f97   Jeff Dike   uml: console subs...
13
  #include "irq_kern.h"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
14
  #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,
bd6aa6502   Thomas Gleixner   [PATCH] irq-flags...
108
  			  IRQF_DISABLED | IRQF_SHARED | IRQF_SAMPLE_RANDOM,
e99525f97   Jeff Dike   uml: console subs...
109
  			  "telnetd", conn)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
110
111
112
113
114
  		printk(KERN_ERR "port_accept : failed to get IRQ for "
  		       "telnetd
  ");
  		goto out_free;
  	}
e99525f97   Jeff Dike   uml: console subs...
115
  	if (atomic_read(&port->wait_count) == 0) {
a6ea4ccee   Jeff Dike   uml: rename os_{r...
116
  		os_write_file(fd, NO_WAITER_MSG, sizeof(NO_WAITER_MSG));
e99525f97   Jeff Dike   uml: console subs...
117
118
  		printk(KERN_ERR "No one waiting for port
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
119
120
  	}
  	list_add(&conn->list, &port->pending);
67608e0c8   Jeff Dike   [PATCH] uml: port...
121
  	return 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
122
123
124
125
126
  
   out_free:
  	kfree(conn);
   out_close:
  	os_close_file(fd);
e99525f97   Jeff Dike   uml: console subs...
127
  	os_kill_process(pid, 1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
128
   out:
e99525f97   Jeff Dike   uml: console subs...
129
  	return 0;
67608e0c8   Jeff Dike   [PATCH] uml: port...
130
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
131

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

e99525f97   Jeff Dike   uml: console subs...
135
  static void port_work_proc(struct work_struct *unused)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
136
137
138
139
140
141
  {
  	struct port_list *port;
  	struct list_head *ele;
  	unsigned long flags;
  
  	local_irq_save(flags);
e99525f97   Jeff Dike   uml: console subs...
142
  	list_for_each(ele, &ports) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
143
  		port = list_entry(ele, struct port_list, list);
e99525f97   Jeff Dike   uml: console subs...
144
  		if (!port->has_connection)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
145
  			continue;
e99525f97   Jeff Dike   uml: console subs...
146

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

7bea96fd2   Al Viro   [PATCH] uml pt_re...
156
  static irqreturn_t port_interrupt(int irq, void *data)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
157
158
159
160
161
  {
  	struct port_list *port = data;
  
  	port->has_connection = 1;
  	schedule_work(&port_work);
67608e0c8   Jeff Dike   [PATCH] uml: port...
162
163
  	return IRQ_HANDLED;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
164
165
166
167
168
169
170
  
  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...
171
  	mutex_lock(&ports_mutex);
e99525f97   Jeff Dike   uml: console subs...
172
  	list_for_each(ele, &ports) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
173
  		port = list_entry(ele, struct port_list, list);
e99525f97   Jeff Dike   uml: console subs...
174
175
  		if (port->port == port_num)
  			goto found;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
176
177
  	}
  	port = kmalloc(sizeof(struct port_list), GFP_KERNEL);
e99525f97   Jeff Dike   uml: console subs...
178
  	if (port == NULL) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
179
180
181
182
183
184
  		printk(KERN_ERR "Allocation of port list failed
  ");
  		goto out;
  	}
  
  	fd = port_listen_fd(port_num);
e99525f97   Jeff Dike   uml: console subs...
185
  	if (fd < 0) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
186
187
188
189
190
  		printk(KERN_ERR "binding to port %d failed, errno = %d
  ",
  		       port_num, -fd);
  		goto out_free;
  	}
e99525f97   Jeff Dike   uml: console subs...
191
192
  
  	if (um_request_irq(ACCEPT_IRQ, fd, IRQ_READ, port_interrupt,
67608e0c8   Jeff Dike   [PATCH] uml: port...
193
  			  IRQF_DISABLED | IRQF_SHARED | IRQF_SAMPLE_RANDOM,
e99525f97   Jeff Dike   uml: console subs...
194
  			  "port", port)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
195
196
197
198
  		printk(KERN_ERR "Failed to get IRQ for port %d
  ", port_num);
  		goto out_close;
  	}
67608e0c8   Jeff Dike   [PATCH] uml: port...
199
  	*port = ((struct port_list)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
200
201
202
203
204
205
206
207
208
209
210
211
212
  		{ .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...
213
  	if (dev == NULL) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
214
215
216
217
218
219
220
221
222
  		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
223
224
   out_close:
  	os_close_file(fd);
79f662334   Jeff Dike   uml: fix error cl...
225
226
   out_free:
  	kfree(port);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
227
   out:
2aa9c5db8   Daniel Walker   uml: port mutex c...
228
  	mutex_unlock(&ports_mutex);
67608e0c8   Jeff Dike   [PATCH] uml: port...
229
  	return dev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
230
231
232
233
234
235
236
237
  }
  
  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...
238
  	atomic_inc(&port->wait_count);
e99525f97   Jeff Dike   uml: console subs...
239
  	while (1) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
240
  		fd = -ERESTARTSYS;
e99525f97   Jeff Dike   uml: console subs...
241
  		if (wait_for_completion_interruptible(&port->done))
67608e0c8   Jeff Dike   [PATCH] uml: port...
242
  			goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
243
244
  
  		spin_lock(&port->lock);
67608e0c8   Jeff Dike   [PATCH] uml: port...
245
  		conn = list_entry(port->connections.next, struct connection,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
246
247
248
249
250
251
252
  				  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...
253
  		os_close_file(conn->socket[1]);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
254
255
256
257
  
  		/* 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...
258
  		 * connection.  Then we loop here throwing out failed
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
259
260
  		 * connections until a good one is found.
  		 */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
261
  		free_irq(TELNETD_IRQ, conn);
e99525f97   Jeff Dike   uml: console subs...
262
263
  		if (conn->fd >= 0)
  			break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
  		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...
280
  	if (dev->helper_pid != -1)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
281
  		os_kill_process(dev->helper_pid, 0);
e99525f97   Jeff Dike   uml: console subs...
282
  	if (dev->telnetd_pid != -1)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
  		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...
300
  	list_for_each(ele, &ports) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
301
302
303
304
305
306
307
  		port = list_entry(ele, struct port_list, list);
  		free_irq_by_fd(port->fd);
  		os_close_file(port->fd);
  	}
  }
  
  __uml_exitcall(free_port);