aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/char/vc_screen.c133
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
54struct 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
62static int
63vcs_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
87static void
88vcs_poll_data_free(struct vcs_poll_data *poll)
89{
90 unregister_vt_notifier(&poll->notifier);
91 kfree(poll);
92}
93
94static struct vcs_poll_data *
95vcs_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
48static int 134static int
49vcs_size(struct inode *inode) 135vcs_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
550static unsigned int
551vcs_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
564static int
565vcs_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
460static int 581static int
461vcs_open(struct inode *inode, struct file *filp) 582vcs_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
594static 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
473static const struct file_operations vcs_fops = { 603static 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
480static struct class *vc_class; 613static struct class *vc_class;