Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 213507
b: refs/heads/master
c: 47725ac
h: refs/heads/master
i:
  213505: 8ad1906
  213503: 7e40893
v: v3
  • Loading branch information
Nicolas Pitre authored and Greg Kroah-Hartman committed Oct 22, 2010
1 parent e6321b4 commit c0cadad
Show file tree
Hide file tree
Showing 2 changed files with 134 additions and 1 deletion.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: f4a3e0bceb57466c31757f25e4e0ed108d1299ec
refs/heads/master: 47725ac76f51328d467b1430dfd027aba8706a11
133 changes: 133 additions & 0 deletions trunk/drivers/char/vc_screen.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@
#include <linux/console.h>
#include <linux/device.h>
#include <linux/smp_lock.h>
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/poll.h>
#include <linux/signal.h>
#include <linux/slab.h>
#include <linux/notifier.h>

#include <asm/uaccess.h>
#include <asm/byteorder.h>
Expand All @@ -45,6 +51,86 @@
#undef addr
#define HEADER_SIZE 4

struct vcs_poll_data {
struct notifier_block notifier;
unsigned int cons_num;
bool seen_last_update;
wait_queue_head_t waitq;
struct fasync_struct *fasync;
};

static int
vcs_notifier(struct notifier_block *nb, unsigned long code, void *_param)
{
struct vt_notifier_param *param = _param;
struct vc_data *vc = param->vc;
struct vcs_poll_data *poll =
container_of(nb, struct vcs_poll_data, notifier);
int currcons = poll->cons_num;

if (code != VT_UPDATE)
return NOTIFY_DONE;

if (currcons == 0)
currcons = fg_console;
else
currcons--;
if (currcons != vc->vc_num)
return NOTIFY_DONE;

poll->seen_last_update = false;
wake_up_interruptible(&poll->waitq);
kill_fasync(&poll->fasync, SIGIO, POLL_IN);
return NOTIFY_OK;
}

static void
vcs_poll_data_free(struct vcs_poll_data *poll)
{
unregister_vt_notifier(&poll->notifier);
kfree(poll);
}

static struct vcs_poll_data *
vcs_poll_data_get(struct file *file)
{
struct vcs_poll_data *poll = file->private_data;

if (poll)
return poll;

poll = kzalloc(sizeof(*poll), GFP_KERNEL);
if (!poll)
return NULL;
poll->cons_num = iminor(file->f_path.dentry->d_inode) & 127;
init_waitqueue_head(&poll->waitq);
poll->notifier.notifier_call = vcs_notifier;
if (register_vt_notifier(&poll->notifier) != 0) {
kfree(poll);
return NULL;
}

/*
* This code may be called either through ->poll() or ->fasync().
* If we have two threads using the same file descriptor, they could
* both enter this function, both notice that the structure hasn't
* been allocated yet and go ahead allocating it in parallel, but
* only one of them must survive and be shared otherwise we'd leak
* memory with a dangling notifier callback.
*/
spin_lock(&file->f_lock);
if (!file->private_data) {
file->private_data = poll;
} else {
/* someone else raced ahead of us */
vcs_poll_data_free(poll);
poll = file->private_data;
}
spin_unlock(&file->f_lock);

return poll;
}

static int
vcs_size(struct inode *inode)
{
Expand Down Expand Up @@ -102,6 +188,7 @@ vcs_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
struct inode *inode = file->f_path.dentry->d_inode;
unsigned int currcons = iminor(inode);
struct vc_data *vc;
struct vcs_poll_data *poll;
long pos;
long viewed, attr, read;
int col, maxcol;
Expand Down Expand Up @@ -134,6 +221,9 @@ vcs_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
ret = -EINVAL;
if (pos < 0)
goto unlock_out;
poll = file->private_data;
if (count && poll)
poll->seen_last_update = true;
read = 0;
ret = 0;
while (count) {
Expand Down Expand Up @@ -457,6 +547,37 @@ vcs_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
return ret;
}

static unsigned int
vcs_poll(struct file *file, poll_table *wait)
{
struct vcs_poll_data *poll = vcs_poll_data_get(file);
int ret = 0;

if (poll) {
poll_wait(file, &poll->waitq, wait);
if (!poll->seen_last_update)
ret = POLLIN | POLLRDNORM;
}
return ret;
}

static int
vcs_fasync(int fd, struct file *file, int on)
{
struct vcs_poll_data *poll = file->private_data;

if (!poll) {
/* don't allocate anything if all we want is disable fasync */
if (!on)
return 0;
poll = vcs_poll_data_get(file);
if (!poll)
return -ENOMEM;
}

return fasync_helper(fd, file, on, &poll->fasync);
}

static int
vcs_open(struct inode *inode, struct file *filp)
{
Expand All @@ -470,11 +591,23 @@ vcs_open(struct inode *inode, struct file *filp)
return ret;
}

static int vcs_release(struct inode *inode, struct file *file)
{
struct vcs_poll_data *poll = file->private_data;

if (poll)
vcs_poll_data_free(poll);
return 0;
}

static const struct file_operations vcs_fops = {
.llseek = vcs_lseek,
.read = vcs_read,
.write = vcs_write,
.poll = vcs_poll,
.fasync = vcs_fasync,
.open = vcs_open,
.release = vcs_release,
};

static struct class *vc_class;
Expand Down

0 comments on commit c0cadad

Please sign in to comment.