~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/arch/um/drivers/chan_kern.c

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 // SPDX-License-Identifier: GPL-2.0
  2 /*
  3  * Copyright (C) 2000 - 2007 Jeff Dike (jdike@{linux.intel,addtoit}.com)
  4  */
  5 
  6 #include <linux/slab.h>
  7 #include <linux/tty.h>
  8 #include <linux/tty_flip.h>
  9 #include "chan.h"
 10 #include <os.h>
 11 #include <irq_kern.h>
 12 
 13 #ifdef CONFIG_NOCONFIG_CHAN
 14 static void *not_configged_init(char *str, int device,
 15                                 const struct chan_opts *opts)
 16 {
 17         printk(KERN_ERR "Using a channel type which is configured out of "
 18                "UML\n");
 19         return NULL;
 20 }
 21 
 22 static int not_configged_open(int input, int output, int primary, void *data,
 23                               char **dev_out)
 24 {
 25         printk(KERN_ERR "Using a channel type which is configured out of "
 26                "UML\n");
 27         return -ENODEV;
 28 }
 29 
 30 static void not_configged_close(int fd, void *data)
 31 {
 32         printk(KERN_ERR "Using a channel type which is configured out of "
 33                "UML\n");
 34 }
 35 
 36 static int not_configged_read(int fd, u8 *c_out, void *data)
 37 {
 38         printk(KERN_ERR "Using a channel type which is configured out of "
 39                "UML\n");
 40         return -EIO;
 41 }
 42 
 43 static int not_configged_write(int fd, const u8 *buf, size_t len, void *data)
 44 {
 45         printk(KERN_ERR "Using a channel type which is configured out of "
 46                "UML\n");
 47         return -EIO;
 48 }
 49 
 50 static int not_configged_console_write(int fd, const char *buf, int len)
 51 {
 52         printk(KERN_ERR "Using a channel type which is configured out of "
 53                "UML\n");
 54         return -EIO;
 55 }
 56 
 57 static int not_configged_window_size(int fd, void *data, unsigned short *rows,
 58                                      unsigned short *cols)
 59 {
 60         printk(KERN_ERR "Using a channel type which is configured out of "
 61                "UML\n");
 62         return -ENODEV;
 63 }
 64 
 65 static void not_configged_free(void *data)
 66 {
 67         printk(KERN_ERR "Using a channel type which is configured out of "
 68                "UML\n");
 69 }
 70 
 71 static const struct chan_ops not_configged_ops = {
 72         .init           = not_configged_init,
 73         .open           = not_configged_open,
 74         .close          = not_configged_close,
 75         .read           = not_configged_read,
 76         .write          = not_configged_write,
 77         .console_write  = not_configged_console_write,
 78         .window_size    = not_configged_window_size,
 79         .free           = not_configged_free,
 80         .winch          = 0,
 81 };
 82 #endif /* CONFIG_NOCONFIG_CHAN */
 83 
 84 static inline bool need_output_blocking(void)
 85 {
 86         return time_travel_mode == TT_MODE_INFCPU ||
 87                time_travel_mode == TT_MODE_EXTERNAL;
 88 }
 89 
 90 static int open_one_chan(struct chan *chan)
 91 {
 92         int fd, err;
 93 
 94         if (chan->opened)
 95                 return 0;
 96 
 97         if (chan->ops->open == NULL)
 98                 fd = 0;
 99         else fd = (*chan->ops->open)(chan->input, chan->output, chan->primary,
100                                      chan->data, &chan->dev);
101         if (fd < 0)
102                 return fd;
103 
104         err = os_set_fd_block(fd, 0);
105         if (err)
106                 goto out_close;
107 
108         chan->fd_in = fd;
109         chan->fd_out = fd;
110 
111         /*
112          * In time-travel modes infinite-CPU and external we need to guarantee
113          * that any writes to the output succeed immdiately from the point of
114          * the VM. The best way to do this is to put the FD in blocking mode
115          * and simply wait/retry until everything is written.
116          * As every write is guaranteed to complete, we also do not need to
117          * request an IRQ for the output.
118          *
119          * Note that input cannot happen in a time synchronized way. We permit
120          * it, but time passes very quickly if anything waits for a read.
121          */
122         if (chan->output && need_output_blocking()) {
123                 err = os_dup_file(chan->fd_out);
124                 if (err < 0)
125                         goto out_close;
126 
127                 chan->fd_out = err;
128 
129                 err = os_set_fd_block(chan->fd_out, 1);
130                 if (err) {
131                         os_close_file(chan->fd_out);
132                         goto out_close;
133                 }
134         }
135 
136         chan->opened = 1;
137         return 0;
138 
139 out_close:
140         (*chan->ops->close)(fd, chan->data);
141         return err;
142 }
143 
144 static int open_chan(struct list_head *chans)
145 {
146         struct list_head *ele;
147         struct chan *chan;
148         int ret, err = 0;
149 
150         list_for_each(ele, chans) {
151                 chan = list_entry(ele, struct chan, list);
152                 ret = open_one_chan(chan);
153                 if (chan->primary)
154                         err = ret;
155         }
156         return err;
157 }
158 
159 void chan_enable_winch(struct chan *chan, struct tty_port *port)
160 {
161         if (chan && chan->primary && chan->ops->winch)
162                 register_winch(chan->fd_in, port);
163 }
164 
165 static void line_timer_cb(struct work_struct *work)
166 {
167         struct line *line = container_of(work, struct line, task.work);
168 
169         if (!line->throttled)
170                 chan_interrupt(line, line->read_irq);
171 }
172 
173 int enable_chan(struct line *line)
174 {
175         struct list_head *ele;
176         struct chan *chan;
177         int err;
178 
179         INIT_DELAYED_WORK(&line->task, line_timer_cb);
180 
181         list_for_each(ele, &line->chan_list) {
182                 chan = list_entry(ele, struct chan, list);
183                 err = open_one_chan(chan);
184                 if (err) {
185                         if (chan->primary)
186                                 goto out_close;
187 
188                         continue;
189                 }
190 
191                 if (chan->enabled)
192                         continue;
193                 err = line_setup_irq(chan->fd_in, chan->input,
194                                      chan->output && !need_output_blocking(),
195                                      line, chan);
196                 if (err)
197                         goto out_close;
198 
199                 chan->enabled = 1;
200         }
201 
202         return 0;
203 
204  out_close:
205         close_chan(line);
206         return err;
207 }
208 
209 /* Items are added in IRQ context, when free_irq can't be called, and
210  * removed in process context, when it can.
211  * This handles interrupt sources which disappear, and which need to
212  * be permanently disabled.  This is discovered in IRQ context, but
213  * the freeing of the IRQ must be done later.
214  */
215 static DEFINE_SPINLOCK(irqs_to_free_lock);
216 static LIST_HEAD(irqs_to_free);
217 
218 void free_irqs(void)
219 {
220         struct chan *chan;
221         LIST_HEAD(list);
222         struct list_head *ele;
223         unsigned long flags;
224 
225         spin_lock_irqsave(&irqs_to_free_lock, flags);
226         list_splice_init(&irqs_to_free, &list);
227         spin_unlock_irqrestore(&irqs_to_free_lock, flags);
228 
229         list_for_each(ele, &list) {
230                 chan = list_entry(ele, struct chan, free_list);
231 
232                 if (chan->input && chan->enabled)
233                         um_free_irq(chan->line->read_irq, chan);
234                 if (chan->output && chan->enabled &&
235                     !need_output_blocking())
236                         um_free_irq(chan->line->write_irq, chan);
237                 chan->enabled = 0;
238         }
239 }
240 
241 static void close_one_chan(struct chan *chan, int delay_free_irq)
242 {
243         unsigned long flags;
244 
245         if (!chan->opened)
246                 return;
247 
248         if (delay_free_irq) {
249                 spin_lock_irqsave(&irqs_to_free_lock, flags);
250                 list_add(&chan->free_list, &irqs_to_free);
251                 spin_unlock_irqrestore(&irqs_to_free_lock, flags);
252         } else {
253                 if (chan->input && chan->enabled)
254                         um_free_irq(chan->line->read_irq, chan);
255                 if (chan->output && chan->enabled &&
256                     !need_output_blocking())
257                         um_free_irq(chan->line->write_irq, chan);
258                 chan->enabled = 0;
259         }
260         if (chan->fd_out != chan->fd_in)
261                 os_close_file(chan->fd_out);
262         if (chan->ops->close != NULL)
263                 (*chan->ops->close)(chan->fd_in, chan->data);
264 
265         chan->opened = 0;
266         chan->fd_in = -1;
267         chan->fd_out = -1;
268 }
269 
270 void close_chan(struct line *line)
271 {
272         struct chan *chan;
273 
274         /* Close in reverse order as open in case more than one of them
275          * refers to the same device and they save and restore that device's
276          * state.  Then, the first one opened will have the original state,
277          * so it must be the last closed.
278          */
279         list_for_each_entry_reverse(chan, &line->chan_list, list) {
280                 close_one_chan(chan, 0);
281         }
282 }
283 
284 void deactivate_chan(struct chan *chan, int irq)
285 {
286         if (chan && chan->enabled)
287                 deactivate_fd(chan->fd_in, irq);
288 }
289 
290 int write_chan(struct chan *chan, const u8 *buf, size_t len, int write_irq)
291 {
292         int n, ret = 0;
293 
294         if (len == 0 || !chan || !chan->ops->write)
295                 return 0;
296 
297         n = chan->ops->write(chan->fd_out, buf, len, chan->data);
298         if (chan->primary) {
299                 ret = n;
300         }
301         return ret;
302 }
303 
304 int console_write_chan(struct chan *chan, const char *buf, int len)
305 {
306         int n, ret = 0;
307 
308         if (!chan || !chan->ops->console_write)
309                 return 0;
310 
311         n = chan->ops->console_write(chan->fd_out, buf, len);
312         if (chan->primary)
313                 ret = n;
314         return ret;
315 }
316 
317 int console_open_chan(struct line *line, struct console *co)
318 {
319         int err;
320 
321         err = open_chan(&line->chan_list);
322         if (err)
323                 return err;
324 
325         printk(KERN_INFO "Console initialized on /dev/%s%d\n", co->name,
326                co->index);
327         return 0;
328 }
329 
330 int chan_window_size(struct line *line, unsigned short *rows_out,
331                       unsigned short *cols_out)
332 {
333         struct chan *chan;
334 
335         chan = line->chan_in;
336         if (chan && chan->primary) {
337                 if (chan->ops->window_size == NULL)
338                         return 0;
339                 return chan->ops->window_size(chan->fd_in, chan->data,
340                                               rows_out, cols_out);
341         }
342         chan = line->chan_out;
343         if (chan && chan->primary) {
344                 if (chan->ops->window_size == NULL)
345                         return 0;
346                 return chan->ops->window_size(chan->fd_in, chan->data,
347                                               rows_out, cols_out);
348         }
349         return 0;
350 }
351 
352 static void free_one_chan(struct chan *chan)
353 {
354         list_del(&chan->list);
355 
356         close_one_chan(chan, 0);
357 
358         if (chan->ops->free != NULL)
359                 (*chan->ops->free)(chan->data);
360 
361         if (chan->primary && chan->output)
362                 ignore_sigio_fd(chan->fd_in);
363         kfree(chan);
364 }
365 
366 static void free_chan(struct list_head *chans)
367 {
368         struct list_head *ele, *next;
369         struct chan *chan;
370 
371         list_for_each_safe(ele, next, chans) {
372                 chan = list_entry(ele, struct chan, list);
373                 free_one_chan(chan);
374         }
375 }
376 
377 static int one_chan_config_string(struct chan *chan, char *str, int size,
378                                   char **error_out)
379 {
380         int n = 0;
381 
382         if (chan == NULL) {
383                 CONFIG_CHUNK(str, size, n, "none", 1);
384                 return n;
385         }
386 
387         CONFIG_CHUNK(str, size, n, chan->ops->type, 0);
388 
389         if (chan->dev == NULL) {
390                 CONFIG_CHUNK(str, size, n, "", 1);
391                 return n;
392         }
393 
394         CONFIG_CHUNK(str, size, n, ":", 0);
395         CONFIG_CHUNK(str, size, n, chan->dev, 0);
396 
397         return n;
398 }
399 
400 static int chan_pair_config_string(struct chan *in, struct chan *out,
401                                    char *str, int size, char **error_out)
402 {
403         int n;
404 
405         n = one_chan_config_string(in, str, size, error_out);
406         str += n;
407         size -= n;
408 
409         if (in == out) {
410                 CONFIG_CHUNK(str, size, n, "", 1);
411                 return n;
412         }
413 
414         CONFIG_CHUNK(str, size, n, ",", 1);
415         n = one_chan_config_string(out, str, size, error_out);
416         str += n;
417         size -= n;
418         CONFIG_CHUNK(str, size, n, "", 1);
419 
420         return n;
421 }
422 
423 int chan_config_string(struct line *line, char *str, int size,
424                        char **error_out)
425 {
426         struct chan *in = line->chan_in, *out = line->chan_out;
427 
428         if (in && !in->primary)
429                 in = NULL;
430         if (out && !out->primary)
431                 out = NULL;
432 
433         return chan_pair_config_string(in, out, str, size, error_out);
434 }
435 
436 struct chan_type {
437         char *key;
438         const struct chan_ops *ops;
439 };
440 
441 static const struct chan_type chan_table[] = {
442         { "fd", &fd_ops },
443 
444 #ifdef CONFIG_NULL_CHAN
445         { "null", &null_ops },
446 #else
447         { "null", &not_configged_ops },
448 #endif
449 
450 #ifdef CONFIG_PORT_CHAN
451         { "port", &port_ops },
452 #else
453         { "port", &not_configged_ops },
454 #endif
455 
456 #ifdef CONFIG_PTY_CHAN
457         { "pty", &pty_ops },
458         { "pts", &pts_ops },
459 #else
460         { "pty", &not_configged_ops },
461         { "pts", &not_configged_ops },
462 #endif
463 
464 #ifdef CONFIG_TTY_CHAN
465         { "tty", &tty_ops },
466 #else
467         { "tty", &not_configged_ops },
468 #endif
469 
470 #ifdef CONFIG_XTERM_CHAN
471         { "xterm", &xterm_ops },
472 #else
473         { "xterm", &not_configged_ops },
474 #endif
475 };
476 
477 static struct chan *parse_chan(struct line *line, char *str, int device,
478                                const struct chan_opts *opts, char **error_out)
479 {
480         const struct chan_type *entry;
481         const struct chan_ops *ops;
482         struct chan *chan;
483         void *data;
484         int i;
485 
486         ops = NULL;
487         data = NULL;
488         for(i = 0; i < ARRAY_SIZE(chan_table); i++) {
489                 entry = &chan_table[i];
490                 if (!strncmp(str, entry->key, strlen(entry->key))) {
491                         ops = entry->ops;
492                         str += strlen(entry->key);
493                         break;
494                 }
495         }
496         if (ops == NULL) {
497                 *error_out = "No match for configured backends";
498                 return NULL;
499         }
500 
501         data = (*ops->init)(str, device, opts);
502         if (data == NULL) {
503                 *error_out = "Configuration failed";
504                 return NULL;
505         }
506 
507         chan = kmalloc(sizeof(*chan), GFP_ATOMIC);
508         if (chan == NULL) {
509                 *error_out = "Memory allocation failed";
510                 return NULL;
511         }
512         *chan = ((struct chan) { .list          = LIST_HEAD_INIT(chan->list),
513                                  .free_list     =
514                                         LIST_HEAD_INIT(chan->free_list),
515                                  .line          = line,
516                                  .primary       = 1,
517                                  .input         = 0,
518                                  .output        = 0,
519                                  .opened        = 0,
520                                  .enabled       = 0,
521                                  .fd_in         = -1,
522                                  .fd_out        = -1,
523                                  .ops           = ops,
524                                  .data          = data });
525         return chan;
526 }
527 
528 int parse_chan_pair(char *str, struct line *line, int device,
529                     const struct chan_opts *opts, char **error_out)
530 {
531         struct list_head *chans = &line->chan_list;
532         struct chan *new;
533         char *in, *out;
534 
535         if (!list_empty(chans)) {
536                 line->chan_in = line->chan_out = NULL;
537                 free_chan(chans);
538                 INIT_LIST_HEAD(chans);
539         }
540 
541         if (!str)
542                 return 0;
543 
544         out = strchr(str, ',');
545         if (out != NULL) {
546                 in = str;
547                 *out = '\0';
548                 out++;
549                 new = parse_chan(line, in, device, opts, error_out);
550                 if (new == NULL)
551                         return -1;
552 
553                 new->input = 1;
554                 list_add(&new->list, chans);
555                 line->chan_in = new;
556 
557                 new = parse_chan(line, out, device, opts, error_out);
558                 if (new == NULL)
559                         return -1;
560 
561                 list_add(&new->list, chans);
562                 new->output = 1;
563                 line->chan_out = new;
564         }
565         else {
566                 new = parse_chan(line, str, device, opts, error_out);
567                 if (new == NULL)
568                         return -1;
569 
570                 list_add(&new->list, chans);
571                 new->input = 1;
572                 new->output = 1;
573                 line->chan_in = line->chan_out = new;
574         }
575         return 0;
576 }
577 
578 void chan_interrupt(struct line *line, int irq)
579 {
580         struct tty_port *port = &line->port;
581         struct chan *chan = line->chan_in;
582         int err;
583         u8 c;
584 
585         if (!chan || !chan->ops->read)
586                 goto out;
587 
588         do {
589                 if (!tty_buffer_request_room(port, 1)) {
590                         schedule_delayed_work(&line->task, 1);
591                         goto out;
592                 }
593                 err = chan->ops->read(chan->fd_in, &c, chan->data);
594                 if (err > 0)
595                         tty_insert_flip_char(port, c, TTY_NORMAL);
596         } while (err > 0);
597 
598         if (err == -EIO) {
599                 if (chan->primary) {
600                         tty_port_tty_hangup(&line->port, false);
601                         if (line->chan_out != chan)
602                                 close_one_chan(line->chan_out, 1);
603                 }
604                 close_one_chan(chan, 1);
605                 if (chan->primary)
606                         return;
607         }
608  out:
609         tty_flip_buffer_push(port);
610 }
611 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php