diff --git a/drivers/media/common/siano/smsdvb-debugfs.c b/drivers/media/common/siano/smsdvb-debugfs.c index 59c7323f98d459b3dabd5571a98f7d65e0d7249f..0219be36c289ccdd2ba146e9c5bec270a2ed4c64 100644 --- a/drivers/media/common/siano/smsdvb-debugfs.c +++ b/drivers/media/common/siano/smsdvb-debugfs.c @@ -352,6 +352,14 @@ static int smsdvb_stats_open(struct inode *inode, struct file *file) return 0; } +static void smsdvb_debugfs_data_release(struct kref *ref) +{ + struct smsdvb_debugfs *debug_data; + + debug_data = container_of(ref, struct smsdvb_debugfs, refcount); + kfree(debug_data); +} + static int smsdvb_stats_wait_read(struct smsdvb_debugfs *debug_data) { int rc = 1; @@ -368,33 +376,65 @@ static int smsdvb_stats_wait_read(struct smsdvb_debugfs *debug_data) return rc; } -static ssize_t smsdvb_stats_read(struct file *file, char __user *user_buf, - size_t nbytes, loff_t *ppos) +static unsigned int smsdvb_stats_poll(struct file *file, poll_table *wait) { - int rc = 0; struct smsdvb_debugfs *debug_data = file->private_data; + int rc; - rc = wait_event_interruptible(debug_data->stats_queue, - smsdvb_stats_wait_read(debug_data)); - if (rc < 0) - return rc; + kref_get(&debug_data->refcount); - rc = simple_read_from_buffer(user_buf, nbytes, ppos, - debug_data->stats_data, - debug_data->stats_count); - spin_lock(&debug_data->lock); - debug_data->stats_was_read = true; - spin_unlock(&debug_data->lock); + poll_wait(file, &debug_data->stats_queue, wait); + + rc = smsdvb_stats_wait_read(debug_data); + if (rc > 0) + rc = POLLIN | POLLRDNORM; + + kref_put(&debug_data->refcount, smsdvb_debugfs_data_release); return rc; } -static void smsdvb_debugfs_data_release(struct kref *ref) +static ssize_t smsdvb_stats_read(struct file *file, char __user *user_buf, + size_t nbytes, loff_t *ppos) { - struct smsdvb_debugfs *debug_data; + int rc = 0, len; + struct smsdvb_debugfs *debug_data = file->private_data; - debug_data = container_of(ref, struct smsdvb_debugfs, refcount); - kfree(debug_data); + kref_get(&debug_data->refcount); + + if (file->f_flags & O_NONBLOCK) { + rc = smsdvb_stats_wait_read(debug_data); + if (!rc) { + rc = -EWOULDBLOCK; + goto ret; + } + } else { + rc = wait_event_interruptible(debug_data->stats_queue, + smsdvb_stats_wait_read(debug_data)); + if (rc < 0) + goto ret; + } + + if (debug_data->stats_was_read) { + rc = 0; /* EOF */ + goto ret; + } + + len = debug_data->stats_count - *ppos; + if (len >= 0) + rc = simple_read_from_buffer(user_buf, nbytes, ppos, + debug_data->stats_data, len); + else + rc = 0; + + if (*ppos >= debug_data->stats_count) { + spin_lock(&debug_data->lock); + debug_data->stats_was_read = true; + spin_unlock(&debug_data->lock); + } +ret: + kref_put(&debug_data->refcount, smsdvb_debugfs_data_release); + return rc; } static int smsdvb_stats_release(struct inode *inode, struct file *file) @@ -402,7 +442,7 @@ static int smsdvb_stats_release(struct inode *inode, struct file *file) struct smsdvb_debugfs *debug_data = file->private_data; spin_lock(&debug_data->lock); - debug_data->stats_was_read = true; + debug_data->stats_was_read = true; /* return EOF to read() */ spin_unlock(&debug_data->lock); wake_up_interruptible_sync(&debug_data->stats_queue); @@ -414,6 +454,7 @@ static int smsdvb_stats_release(struct inode *inode, struct file *file) static const struct file_operations debugfs_stats_ops = { .open = smsdvb_stats_open, + .poll = smsdvb_stats_poll, .read = smsdvb_stats_read, .release = smsdvb_stats_release, .llseek = generic_file_llseek,