Commit aa27bf82 authored by Bryan O'Donoghue's avatar Bryan O'Donoghue Committed by Greg Kroah-Hartman

greybus: loopback: convert sample report interface to debugfs

Greg previously suggested switching over to debugfs instead of a char
interface to report raw samples to user-space. At the time we agreed not
to go for that change. However later patches in this series are made
simpler if debugfs is used instead of /dev, so it makes sense to make the
conversion now for that reason. This patch removes the char interface and
replaces it with a debugfs interface. Raw samples will be acquired from
/sys/kernel/debug/gb_loopback/raw_latency_endo0:x:y:z:w where

x = <module-id>
y = <interface-id>
z = <bundle-id>
w = <cport-number>
Signed-off-by: default avatarBryan O'Donoghue <bryan.odonoghue@linaro.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@google.com>
parent 2f842304
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include <linux/cdev.h> #include <linux/cdev.h>
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/kfifo.h> #include <linux/kfifo.h>
#include <linux/debugfs.h>
#include <asm/div64.h> #include <asm/div64.h>
...@@ -31,16 +32,21 @@ struct gb_loopback_stats { ...@@ -31,16 +32,21 @@ struct gb_loopback_stats {
u32 count; u32 count;
}; };
struct gb_loopback_device {
struct dentry *root;
u32 count;
};
static struct gb_loopback_device gb_dev;
struct gb_loopback { struct gb_loopback {
struct gb_connection *connection; struct gb_connection *connection;
struct dentry *file;
struct kfifo kfifo; struct kfifo kfifo;
struct mutex mutex; struct mutex mutex;
struct task_struct *task; struct task_struct *task;
wait_queue_head_t wq; wait_queue_head_t wq;
dev_t dev;
struct cdev cdev;
struct device *device;
int type; int type;
u32 size; u32 size;
...@@ -60,16 +66,9 @@ struct gb_loopback { ...@@ -60,16 +66,9 @@ struct gb_loopback {
#define GB_LOOPBACK_FIFO_DEFAULT 8192 #define GB_LOOPBACK_FIFO_DEFAULT 8192
static struct class *loopback_class;
static int loopback_major;
static const struct file_operations loopback_fops;
static DEFINE_IDA(minors);
static unsigned kfifo_depth = GB_LOOPBACK_FIFO_DEFAULT; static unsigned kfifo_depth = GB_LOOPBACK_FIFO_DEFAULT;
module_param(kfifo_depth, uint, 0444); module_param(kfifo_depth, uint, 0444);
/* Number of minor devices this driver supports */
#define NUM_MINORS 256
/* Maximum size of any one send data buffer we support */ /* Maximum size of any one send data buffer we support */
#define MAX_PACKET_SIZE (PAGE_SIZE * 2) #define MAX_PACKET_SIZE (PAGE_SIZE * 2)
...@@ -515,35 +514,72 @@ static int gb_loopback_fn(void *data) ...@@ -515,35 +514,72 @@ static int gb_loopback_fn(void *data)
return 0; return 0;
} }
static int gb_loopback_dbgfs_latency_show(struct seq_file *s, void *unused)
{
struct gb_loopback *gb = s->private;
u32 latency;
int retval;
if (kfifo_len(&gb->kfifo) == 0) {
retval = -EAGAIN;
goto done;
}
mutex_lock(&gb->mutex);
retval = kfifo_out(&gb->kfifo, &latency, sizeof(latency));
if (retval > 0) {
seq_printf(s, "%u", latency);
retval = 0;
}
mutex_unlock(&gb->mutex);
done:
return retval;
}
static int gb_loopback_latency_open(struct inode *inode, struct file *file)
{
return single_open(file, gb_loopback_dbgfs_latency_show,
inode->i_private);
}
static const struct file_operations gb_loopback_debugfs_latency_ops = {
.open = gb_loopback_latency_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
#define DEBUGFS_NAMELEN 24
static int gb_loopback_connection_init(struct gb_connection *connection) static int gb_loopback_connection_init(struct gb_connection *connection)
{ {
struct gb_loopback *gb; struct gb_loopback *gb;
int retval; int retval;
int minor; char name[DEBUGFS_NAMELEN];
gb = kzalloc(sizeof(*gb), GFP_KERNEL); gb = kzalloc(sizeof(*gb), GFP_KERNEL);
if (!gb) if (!gb)
return -ENOMEM; return -ENOMEM;
gb_loopback_reset_stats(gb); gb_loopback_reset_stats(gb);
snprintf(name, sizeof(name), "raw_latency_endo0:%d:%d:%d:%d",
connection->bundle->intf->module->module_id,
connection->bundle->intf->interface_id,
connection->bundle->id,
connection->intf_cport_id);
gb->file = debugfs_create_file(name, S_IFREG | S_IRUGO, gb_dev.root, gb,
&gb_loopback_debugfs_latency_ops);
gb->connection = connection; gb->connection = connection;
connection->private = gb; connection->private = gb;
retval = sysfs_create_groups(&connection->dev.kobj, loopback_groups); retval = sysfs_create_groups(&connection->dev.kobj, loopback_groups);
if (retval) if (retval)
goto out_free; goto out_debugfs;
/* Get a minor number */
minor = ida_simple_get(&minors, 0, 0, GFP_KERNEL);
if (minor < 0) {
retval = minor;
goto out_sysfs;
}
/* Calculate maximum payload */ /* Calculate maximum payload */
gb->size_max = gb_operation_get_payload_size_max(connection); gb->size_max = gb_operation_get_payload_size_max(connection);
if (gb->size_max <= sizeof(struct gb_loopback_transfer_request)) { if (gb->size_max <= sizeof(struct gb_loopback_transfer_request)) {
retval = -EINVAL; retval = -EINVAL;
goto out_minor; goto out_sysfs;
} }
gb->size_max -= sizeof(struct gb_loopback_transfer_request); gb->size_max -= sizeof(struct gb_loopback_transfer_request);
...@@ -551,21 +587,7 @@ static int gb_loopback_connection_init(struct gb_connection *connection) ...@@ -551,21 +587,7 @@ static int gb_loopback_connection_init(struct gb_connection *connection)
if (kfifo_alloc(&gb->kfifo, kfifo_depth * sizeof(u32), if (kfifo_alloc(&gb->kfifo, kfifo_depth * sizeof(u32),
GFP_KERNEL)) { GFP_KERNEL)) {
retval = -ENOMEM; retval = -ENOMEM;
goto out_minor; goto out_sysfs;
}
/* Create device entry */
gb->dev = MKDEV(loopback_major, minor);
cdev_init(&gb->cdev, &loopback_fops);
retval = cdev_add(&gb->cdev, gb->dev, 1);
if (retval)
goto out_kfifo;
gb->device = device_create(loopback_class, &connection->dev, gb->dev,
gb, "gb!loopback%d", minor);
if (IS_ERR(gb->device)) {
retval = PTR_ERR(gb->device);
goto out_cdev;
} }
/* Fork worker thread */ /* Fork worker thread */
...@@ -574,22 +596,18 @@ static int gb_loopback_connection_init(struct gb_connection *connection) ...@@ -574,22 +596,18 @@ static int gb_loopback_connection_init(struct gb_connection *connection)
gb->task = kthread_run(gb_loopback_fn, gb, "gb_loopback"); gb->task = kthread_run(gb_loopback_fn, gb, "gb_loopback");
if (IS_ERR(gb->task)) { if (IS_ERR(gb->task)) {
retval = PTR_ERR(gb->task); retval = PTR_ERR(gb->task);
goto out_device; goto out_kfifo;
} }
gb_dev.count++;
return 0; return 0;
out_device:
device_del(gb->device);
out_cdev:
cdev_del(&gb->cdev);
out_kfifo: out_kfifo:
kfifo_free(&gb->kfifo); kfifo_free(&gb->kfifo);
out_minor:
ida_simple_remove(&minors, minor);
out_sysfs: out_sysfs:
sysfs_remove_groups(&connection->dev.kobj, loopback_groups); sysfs_remove_groups(&connection->dev.kobj, loopback_groups);
out_free: out_debugfs:
debugfs_remove(gb->file);
connection->private = NULL; connection->private = NULL;
kfree(gb); kfree(gb);
...@@ -600,15 +618,14 @@ static void gb_loopback_connection_exit(struct gb_connection *connection) ...@@ -600,15 +618,14 @@ static void gb_loopback_connection_exit(struct gb_connection *connection)
{ {
struct gb_loopback *gb = connection->private; struct gb_loopback *gb = connection->private;
gb_dev.count--;
connection->private = NULL; connection->private = NULL;
if (!IS_ERR_OR_NULL(gb->task)) if (!IS_ERR_OR_NULL(gb->task))
kthread_stop(gb->task); kthread_stop(gb->task);
device_del(gb->device);
cdev_del(&gb->cdev);
kfifo_free(&gb->kfifo); kfifo_free(&gb->kfifo);
ida_simple_remove(&minors, MINOR(gb->dev));
sysfs_remove_groups(&connection->dev.kobj, loopback_groups); sysfs_remove_groups(&connection->dev.kobj, loopback_groups);
debugfs_remove(gb->file);
kfree(gb); kfree(gb);
} }
...@@ -622,79 +639,17 @@ static struct gb_protocol loopback_protocol = { ...@@ -622,79 +639,17 @@ static struct gb_protocol loopback_protocol = {
.request_recv = gb_loopback_request_recv, .request_recv = gb_loopback_request_recv,
}; };
static int loopback_open(struct inode *inode, struct file *file)
{
struct cdev *cdev = inode->i_cdev;
struct gb_loopback *gb = container_of(cdev, struct gb_loopback, cdev);
file->private_data = gb;
return 0;
}
static ssize_t loopback_read(struct file *file, char __user *buf, size_t count,
loff_t *ppos)
{
struct gb_loopback *gb = file->private_data;
size_t fifo_len;
int retval;
if (!count || count % sizeof(u32))
return -EINVAL;
mutex_lock(&gb->mutex);
fifo_len = kfifo_len(&gb->kfifo);
if (kfifo_to_user(&gb->kfifo, buf, min(fifo_len, count), &retval) != 0)
retval = -EIO;
mutex_unlock(&gb->mutex);
return retval;
}
static const struct file_operations loopback_fops = {
.owner = THIS_MODULE,
.read = loopback_read,
.open = loopback_open,
.llseek = noop_llseek,
};
static int loopback_init(void) static int loopback_init(void)
{ {
dev_t dev; gb_dev.root = debugfs_create_dir("gb_loopback", NULL);
int retval; return gb_protocol_register(&loopback_protocol);
loopback_class = class_create(THIS_MODULE, "gb_loopback");
if (IS_ERR(loopback_class)) {
retval = PTR_ERR(loopback_class);
goto error_class;
}
retval = alloc_chrdev_region(&dev, 0, NUM_MINORS, "gb_loopback");
if (retval < 0)
goto error_chrdev;
loopback_major = MAJOR(dev);
retval = gb_protocol_register(&loopback_protocol);
if (retval)
goto error_gb;
return 0;
error_gb:
unregister_chrdev_region(dev, NUM_MINORS);
error_chrdev:
class_destroy(loopback_class);
error_class:
return retval;
} }
module_init(loopback_init); module_init(loopback_init);
static void __exit loopback_exit(void) static void __exit loopback_exit(void)
{ {
debugfs_remove_recursive(gb_dev.root);
gb_protocol_deregister(&loopback_protocol); gb_protocol_deregister(&loopback_protocol);
unregister_chrdev_region(MKDEV(loopback_major, 0), NUM_MINORS);
class_destroy(loopback_class);
ida_destroy(&minors);
} }
module_exit(loopback_exit); module_exit(loopback_exit);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment