diff options
| -rw-r--r-- | drivers/char/vc_screen.c | 133 |
1 files changed, 133 insertions, 0 deletions
diff --git a/drivers/char/vc_screen.c b/drivers/char/vc_screen.c index bcce46c96b88..6f7054e1a516 100644 --- a/drivers/char/vc_screen.c +++ b/drivers/char/vc_screen.c | |||
| @@ -35,6 +35,12 @@ | |||
| 35 | #include <linux/console.h> | 35 | #include <linux/console.h> |
| 36 | #include <linux/device.h> | 36 | #include <linux/device.h> |
| 37 | #include <linux/smp_lock.h> | 37 | #include <linux/smp_lock.h> |
| 38 | #include <linux/sched.h> | ||
| 39 | #include <linux/fs.h> | ||
| 40 | #include <linux/poll.h> | ||
| 41 | #include <linux/signal.h> | ||
| 42 | #include <linux/slab.h> | ||
| 43 | #include <linux/notifier.h> | ||
| 38 | 44 | ||
| 39 | #include <asm/uaccess.h> | 45 | #include <asm/uaccess.h> |
| 40 | #include <asm/byteorder.h> | 46 | #include <asm/byteorder.h> |
| @@ -45,6 +51,86 @@ | |||
| 45 | #undef addr | 51 | #undef addr |
| 46 | #define HEADER_SIZE 4 | 52 | #define HEADER_SIZE 4 |
| 47 | 53 | ||
| 54 | struct vcs_poll_data { | ||
| 55 | struct notifier_block notifier; | ||
| 56 | unsigned int cons_num; | ||
| 57 | bool seen_last_update; | ||
| 58 | wait_queue_head_t waitq; | ||
| 59 | struct fasync_struct *fasync; | ||
| 60 | }; | ||
| 61 | |||
| 62 | static int | ||
| 63 | vcs_notifier(struct notifier_block *nb, unsigned long code, void *_param) | ||
| 64 | { | ||
| 65 | struct vt_notifier_param *param = _param; | ||
| 66 | struct vc_data *vc = param->vc; | ||
| 67 | struct vcs_poll_data *poll = | ||
| 68 | container_of(nb, struct vcs_poll_data, notifier); | ||
| 69 | int currcons = poll->cons_num; | ||
| 70 | |||
| 71 | if (code != VT_UPDATE) | ||
| 72 | return NOTIFY_DONE; | ||
| 73 | |||
| 74 | if (currcons == 0) | ||
| 75 | currcons = fg_console; | ||
| 76 | else | ||
| 77 | currcons--; | ||
| 78 | if (currcons != vc->vc_num) | ||
| 79 | return NOTIFY_DONE; | ||
| 80 | |||
| 81 | poll->seen_last_update = false; | ||
| 82 | wake_up_interruptible(&poll->waitq); | ||
| 83 | kill_fasync(&poll->fasync, SIGIO, POLL_IN); | ||
| 84 | return NOTIFY_OK; | ||
| 85 | } | ||
| 86 | |||
| 87 | static void | ||
| 88 | vcs_poll_data_free(struct vcs_poll_data *poll) | ||
| 89 | { | ||
| 90 | unregister_vt_notifier(&poll->notifier); | ||
| 91 | kfree(poll); | ||
| 92 | } | ||
| 93 | |||
| 94 | static struct vcs_poll_data * | ||
| 95 | vcs_poll_data_get(struct file *file) | ||
| 96 | { | ||
| 97 | struct vcs_poll_data *poll = file->private_data; | ||
| 98 | |||
| 99 | if (poll) | ||
| 100 | return poll; | ||
| 101 | |||
| 102 | poll = kzalloc(sizeof(*poll), GFP_KERNEL); | ||
| 103 | if (!poll) | ||
| 104 | return NULL; | ||
| 105 | poll->cons_num = iminor(file->f_path.dentry->d_inode) & 127; | ||
| 106 | init_waitqueue_head(&poll->waitq); | ||
| 107 | poll->notifier.notifier_call = vcs_notifier; | ||
| 108 | if (register_vt_notifier(&poll->notifier) != 0) { | ||
| 109 | kfree(poll); | ||
| 110 | return NULL; | ||
| 111 | } | ||
| 112 | |||
| 113 | /* | ||
| 114 | * This code may be called either through ->poll() or ->fasync(). | ||
| 115 | * If we have two threads using the same file descriptor, they could | ||
| 116 | * both enter this function, both notice that the structure hasn't | ||
| 117 | * been allocated yet and go ahead allocating it in parallel, but | ||
| 118 | * only one of them must survive and be shared otherwise we'd leak | ||
| 119 | * memory with a dangling notifier callback. | ||
| 120 | */ | ||
| 121 | spin_lock(&file->f_lock); | ||
| 122 | if (!file->private_data) { | ||
| 123 | file->private_data = poll; | ||
| 124 | } else { | ||
| 125 | /* someone else raced ahead of us */ | ||
| 126 | vcs_poll_data_free(poll); | ||
| 127 | poll = file->private_data; | ||
| 128 | } | ||
| 129 | spin_unlock(&file->f_lock); | ||
| 130 | |||
| 131 | return poll; | ||
| 132 | } | ||
| 133 | |||
| 48 | static int | 134 | static int |
| 49 | vcs_size(struct inode *inode) | 135 | vcs_size(struct inode *inode) |
| 50 | { | 136 | { |
| @@ -102,6 +188,7 @@ vcs_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) | |||
| 102 | struct inode *inode = file->f_path.dentry->d_inode; | 188 | struct inode *inode = file->f_path.dentry->d_inode; |
| 103 | unsigned int currcons = iminor(inode); | 189 | unsigned int currcons = iminor(inode); |
| 104 | struct vc_data *vc; | 190 | struct vc_data *vc; |
| 191 | struct vcs_poll_data *poll; | ||
| 105 | long pos; | 192 | long pos; |
| 106 | long viewed, attr, read; | 193 | long viewed, attr, read; |
| 107 | int col, maxcol; | 194 | int col, maxcol; |
| @@ -134,6 +221,9 @@ vcs_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) | |||
| 134 | ret = -EINVAL; | 221 | ret = -EINVAL; |
| 135 | if (pos < 0) | 222 | if (pos < 0) |
| 136 | goto unlock_out; | 223 | goto unlock_out; |
| 224 | poll = file->private_data; | ||
| 225 | if (count && poll) | ||
| 226 | poll->seen_last_update = true; | ||
| 137 | read = 0; | 227 | read = 0; |
| 138 | ret = 0; | 228 | ret = 0; |
| 139 | while (count) { | 229 | while (count) { |
| @@ -457,6 +547,37 @@ unlock_out: | |||
| 457 | return ret; | 547 | return ret; |
| 458 | } | 548 | } |
| 459 | 549 | ||
| 550 | static unsigned int | ||
| 551 | vcs_poll(struct file *file, poll_table *wait) | ||
| 552 | { | ||
| 553 | struct vcs_poll_data *poll = vcs_poll_data_get(file); | ||
| 554 | int ret = 0; | ||
| 555 | |||
| 556 | if (poll) { | ||
| 557 | poll_wait(file, &poll->waitq, wait); | ||
| 558 | if (!poll->seen_last_update) | ||
| 559 | ret = POLLIN | POLLRDNORM; | ||
| 560 | } | ||
| 561 | return ret; | ||
| 562 | } | ||
| 563 | |||
| 564 | static int | ||
| 565 | vcs_fasync(int fd, struct file *file, int on) | ||
| 566 | { | ||
| 567 | struct vcs_poll_data *poll = file->private_data; | ||
| 568 | |||
| 569 | if (!poll) { | ||
| 570 | /* don't allocate anything if all we want is disable fasync */ | ||
| 571 | if (!on) | ||
| 572 | return 0; | ||
| 573 | poll = vcs_poll_data_get(file); | ||
| 574 | if (!poll) | ||
| 575 | return -ENOMEM; | ||
| 576 | } | ||
| 577 | |||
| 578 | return fasync_helper(fd, file, on, &poll->fasync); | ||
| 579 | } | ||
| 580 | |||
| 460 | static int | 581 | static int |
| 461 | vcs_open(struct inode *inode, struct file *filp) | 582 | vcs_open(struct inode *inode, struct file *filp) |
| 462 | { | 583 | { |
| @@ -470,11 +591,23 @@ vcs_open(struct inode *inode, struct file *filp) | |||
| 470 | return ret; | 591 | return ret; |
| 471 | } | 592 | } |
| 472 | 593 | ||
| 594 | static int vcs_release(struct inode *inode, struct file *file) | ||
| 595 | { | ||
| 596 | struct vcs_poll_data *poll = file->private_data; | ||
| 597 | |||
| 598 | if (poll) | ||
| 599 | vcs_poll_data_free(poll); | ||
| 600 | return 0; | ||
| 601 | } | ||
| 602 | |||
| 473 | static const struct file_operations vcs_fops = { | 603 | static const struct file_operations vcs_fops = { |
| 474 | .llseek = vcs_lseek, | 604 | .llseek = vcs_lseek, |
| 475 | .read = vcs_read, | 605 | .read = vcs_read, |
| 476 | .write = vcs_write, | 606 | .write = vcs_write, |
| 607 | .poll = vcs_poll, | ||
| 608 | .fasync = vcs_fasync, | ||
| 477 | .open = vcs_open, | 609 | .open = vcs_open, |
| 610 | .release = vcs_release, | ||
| 478 | }; | 611 | }; |
| 479 | 612 | ||
| 480 | static struct class *vc_class; | 613 | static struct class *vc_class; |
