// SPDX-License-Identifier: GPL-2.0/* * Loopback bridge driver for the Greybus loopback module. * * Copyright 2014 Google Inc. * Copyright 2014 Linaro Ltd. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/kernel.h>#include <linux/module.h>#include <linux/mutex.h>#include <linux/slab.h>#include <linux/kthread.h>#include <linux/delay.h>#include <linux/random.h>#include <linux/sizes.h>#include <linux/cdev.h>#include <linux/fs.h>#include <linux/kfifo.h>#include <linux/debugfs.h>#include <linux/list_sort.h>#include <linux/spinlock.h>#include <linux/workqueue.h>#include <linux/atomic.h>#include <linux/pm_runtime.h>#include <linux/greybus.h>#include <asm/div64.h> #define NSEC_PER_DAY 86400000000000ULL struct gb_loopback_stats { u32 min; u32 max; u64 sum; u32 count;}; struct gb_loopback_device { struct dentry *root; u32 count; size_t size_max; /* We need to take a lock in atomic context */ spinlock_t lock; wait_queue_head_t wq;}; static struct gb_loopback_device gb_dev; struct gb_loopback_async_operation { struct gb_loopback *gb; struct gb_operation *operation; ktime_t ts; int (*completion)(struct gb_loopback_async_operation *op_async);}; struct gb_loopback { struct gb_connection *connection; struct dentry *file; struct kfifo kfifo_lat; struct mutex mutex; struct task_struct *task; struct device *dev; wait_queue_head_t wq; wait_queue_head_t wq_completion; atomic_t outstanding_operations; /* Per connection stats */ ktime_t ts; struct gb_loopback_stats latency; struct gb_loopback_stats throughput; struct gb_loopback_stats requests_per_second; struct gb_loopback_stats apbridge_unipro_latency; struct gb_loopback_stats gbphy_firmware_latency; int type; int async; int id; u32 size; u32 iteration_max; u32 iteration_count; int us_wait; u32 error; u32 requests_completed; u32 requests_timedout; u32 timeout; u32 jiffy_timeout; u32 timeout_min; u32 timeout_max; u32 outstanding_operations_max; u64 elapsed_nsecs; u32 apbridge_latency_ts; u32 gbphy_latency_ts; u32 send_count;}; static struct class loopback_class = { .name = "gb_loopback",}; static DEFINE_IDA(loopback_ida); /* Min/max values in jiffies */#define GB_LOOPBACK_TIMEOUT_MIN 1#define GB_LOOPBACK_TIMEOUT_MAX 10000 #define GB_LOOPBACK_FIFO_DEFAULT 8192 static unsigned int kfifo_depth = GB_LOOPBACK_FIFO_DEFAULT;module_param(kfifo_depth, uint, 0444); /* Maximum size of any one send data buffer we support */#define MAX_PACKET_SIZE (PAGE_SIZE * 2) #define GB_LOOPBACK_US_WAIT_MAX 1000000 /* interface sysfs attributes */#define gb_loopback_ro_attr(field) \static ssize_t field##_show(struct device *dev, \ struct device_attribute *attr, \ char *buf) \{ \ struct gb_loopback *gb = dev_get_drvdata(dev); \ return sysfs_emit(buf, "%u\n", gb->field); \} \static DEVICE_ATTR_RO(field) #define gb_loopback_ro_stats_attr(name, field, type) \static ssize_t name##_##field##_show(struct device *dev, \ struct device_attribute *attr, \ char *buf) \{ \ struct gb_loopback *gb = dev_get_drvdata(dev); \ /* Report 0 for min and max if no transfer succeeded */ \ if (!gb->requests_completed) \ return sysfs_emit(buf, "0\n"); \ return sysfs_emit(buf, "%" #type "\n", gb->name.field); \} \static DEVICE_ATTR_RO(name##_##field) #define gb_loopback_ro_avg_attr(name) \static ssize_t name##_avg_show(struct device *dev, \ struct device_attribute *attr, \ char *buf) \{ \ struct gb_loopback_stats *stats; \ struct gb_loopback *gb; \ u64 avg, rem; \ u32 count; \ gb = dev_get_drvdata(dev); \ stats = &gb->name; \ count = stats->count ? stats->count : 1; \ avg = stats->sum + count / 2000000; /* round closest */ \ rem = do_div(avg, count); \ rem *= 1000000; \ do_div(rem, count); \ return sysfs_emit(buf, "%llu.%06u\n", avg, (u32)rem); \} \static DEVICE_ATTR_RO(name##_avg) #define gb_loopback_stats_attrs(field) \ gb_loopback_ro_stats_attr(field, min, u); \ gb_loopback_ro_stats_attr(field, max, u); \ gb_loopback_ro_avg_attr(field) #define gb_dev_loopback_ro_attr(field) \static ssize_t field##_show(struct device *dev, \ struct device_attribute *attr, \ char *buf) \{ \ struct gb_loopback *gb = dev_get_drvdata(dev); \ return sysfs_emit(buf, "%u\n", gb->field); \} \static DEVICE_ATTR_RO(field) #define gb_dev_loopback_rw_attr(field, type) \static ssize_t field##_show(struct device *dev, \ struct device_attribute *attr, \ char *buf) \{ \ struct gb_loopback *gb = dev_get_drvdata(dev); \ return sysfs_emit(buf, "%" #type "\n", gb->field); \} \static ssize_t field##_store(struct device *dev, \ struct device_attribute *attr, \ const char *buf, \ size_t len) \{ \ int ret; \ struct gb_loopback *gb = dev_get_drvdata(dev); \ mutex_lock(&gb->mutex); \ ret = sscanf(buf, "%"#type, &gb->field); \ if (ret != 1) \ len = -EINVAL; \ else \ gb_loopback_check_attr(gb); \ mutex_unlock(&gb->mutex); \ return len; \} \static DEVICE_ATTR_RW(field) static void gb_loopback_reset_stats(struct gb_loopback *gb);static void gb_loopback_check_attr(struct gb_loopback *gb){ if (gb->us_wait > GB_LOOPBACK_US_WAIT_MAX) gb->us_wait = GB_LOOPBACK_US_WAIT_MAX; if (gb->size > gb_dev.size_max) gb->size = gb_dev.size_max; gb->requests_timedout = 0; gb->requests_completed = 0; gb->iteration_count = 0; gb->send_count = 0; gb->error = 0; if (kfifo_depth < gb->iteration_max) { dev_warn(gb->dev, "cannot log bytes %u kfifo_depth %u\n", gb->iteration_max, kfifo_depth); } kfifo_reset_out(&gb->kfifo_lat); switch (gb->type) { case GB_LOOPBACK_TYPE_PING: case GB_LOOPBACK_TYPE_TRANSFER: case GB_LOOPBACK_TYPE_SINK: gb->jiffy_timeout = usecs_to_jiffies(gb->timeout); if (!gb->jiffy_timeout) gb->jiffy_timeout = GB_LOOPBACK_TIMEOUT_MIN; else if (gb->jiffy_timeout > GB_LOOPBACK_TIMEOUT_MAX) gb->jiffy_timeout = GB_LOOPBACK_TIMEOUT_MAX; gb_loopback_reset_stats(gb); wake_up(&gb->wq); break; default: gb->type = 0; break; }} /* Time to send and receive one message */gb_loopback_stats_attrs(latency);/* Number of requests sent per second on this cport */gb_loopback_stats_attrs(requests_per_second);/* Quantity of data sent and received on this cport */gb_loopback_stats_attrs(throughput);/* Latency across the UniPro link from APBridge's perspective */gb_loopback_stats_attrs(apbridge_unipro_latency);/* Firmware induced overhead in the GPBridge */gb_loopback_stats_attrs(gbphy_firmware_latency); /* Number of errors encountered during loop */gb_loopback_ro_attr(error);/* Number of requests successfully completed async */gb_loopback_ro_attr(requests_completed);/* Number of requests timed out async */gb_loopback_ro_attr(requests_timedout);/* Timeout minimum in useconds */gb_loopback_ro_attr(timeout_min);/* Timeout minimum in useconds */gb_loopback_ro_attr(timeout_max); /* * Type of loopback message to send based on protocol type definitions * 0 => Don't send message * 2 => Send ping message continuously (message without payload) * 3 => Send transfer message continuously (message with payload, * payload returned in response) * 4 => Send a sink message (message with payload, no payload in response) */gb_dev_loopback_rw_attr(type, d);/* Size of transfer message payload: 0-4096 bytes */gb_dev_loopback_rw_attr(size, u);/* Time to wait between two messages: 0-1000 ms */gb_dev_loopback_rw_attr(us_wait, d);/* Maximum iterations for a given operation: 1-(2^32-1), 0 implies infinite */gb_dev_loopback_rw_attr(iteration_max, u);/* The current index of the for (i = 0; i < iteration_max; i++) loop */gb_dev_loopback_ro_attr(iteration_count);/* A flag to indicate synchronous or asynchronous operations */gb_dev_loopback_rw_attr(async, u);/* Timeout of an individual asynchronous request */gb_dev_loopback_rw_attr(timeout, u);/* Maximum number of in-flight operations before back-off */gb_dev_loopback_rw_attr(outstanding_operations_max, u); static struct attribute *loopback_attrs[] = { &dev_attr_latency_min.attr, &dev_attr_latency_max.attr, &dev_attr_latency_avg.attr, &dev_attr_requests_per_second_min.attr, &dev_attr_requests_per_second_max.attr, &dev_attr_requests_per_second_avg.attr, &dev_attr_throughput_min.attr, &dev_attr_throughput_max.attr, &dev_attr_throughput_avg.attr, &dev_attr_apbridge_unipro_latency_min.attr, &dev_attr_apbridge_unipro_latency_max.attr, &dev_attr_apbridge_unipro_latency_avg.attr, &dev_attr_gbphy_firmware_latency_min.attr, &dev_attr_gbphy_firmware_latency_max.attr, &dev_attr_gbphy_firmware_latency_avg.attr, &dev_attr_type.attr, &dev_attr_size.attr, &dev_attr_us_wait.attr, &dev_attr_iteration_count.attr, &dev_attr_iteration_max.attr, &dev_attr_async.attr, &dev_attr_error.attr, &dev_attr_requests_completed.attr, &dev_attr_requests_timedout.attr, &dev_attr_timeout.attr, &dev_attr_outstanding_operations_max.attr, &dev_attr_timeout_min.attr, &dev_attr_timeout_max.attr, NULL,};ATTRIBUTE_GROUPS(loopback); static void gb_loopback_calculate_stats(struct gb_loopback *gb, bool error); static u32 gb_loopback_nsec_to_usec_latency(u64 elapsed_nsecs){ do_div(elapsed_nsecs, NSEC_PER_USEC); return elapsed_nsecs;} static u64 __gb_loopback_calc_latency(u64 t1, u64 t2){ if (t2 > t1) return t2 - t1; else return NSEC_PER_DAY - t2 + t1;} static u64 gb_loopback_calc_latency(ktime_t ts, ktime_t te){ return __gb_loopback_calc_latency(ktime_to_ns(ts), ktime_to_ns(te));} static int gb_loopback_operation_sync(struct gb_loopback *gb, int type, void *request, int request_size, void *response, int response_size){ struct gb_operation *operation; ktime_t ts, te; int ret; ts = ktime_get(); operation = gb_operation_create(gb->connection, type, request_size, response_size, GFP_KERNEL); if (!operation) return -ENOMEM; if (request_size) memcpy(operation->request->payload, request, request_size); ret = gb_operation_request_send_sync(operation); if (ret) { dev_err(&gb->connection->bundle->dev, "synchronous operation failed: %d\n", ret); goto out_put_operation; } else { if (response_size == operation->response->payload_size) { memcpy(response, operation->response->payload, response_size); } else { dev_err(&gb->connection->bundle->dev, "response size %zu expected %d\n", operation->response->payload_size, response_size); ret = -EINVAL; goto out_put_operation; } } te = ktime_get(); /* Calculate the total time the message took */ gb->elapsed_nsecs = gb_loopback_calc_latency(ts, te); out_put_operation: gb_operation_put(operation); return ret;} static void gb_loopback_async_wait_all(struct gb_loopback *gb){ wait_event(gb->wq_completion, !atomic_read(&gb->outstanding_operations));} static void gb_loopback_async_operation_callback(struct gb_operation *operation){ struct gb_loopback_async_operation *op_async; struct gb_loopback *gb; ktime_t te; int result; te = ktime_get(); result = gb_operation_result(operation); op_async = gb_operation_get_data(operation); gb = op_async->gb; mutex_lock(&gb->mutex); if (!result && op_async->completion) result = op_async->completion(op_async); if (!result) { gb->elapsed_nsecs = gb_loopback_calc_latency(op_async->ts, te); } else { gb->error++; if (result == -ETIMEDOUT) gb->requests_timedout++; } gb->iteration_count++; gb_loopback_calculate_stats(gb, result); mutex_unlock(&gb->mutex); dev_dbg(&gb->connection->bundle->dev, "complete operation %d\n", operation->id); /* Wake up waiters */ atomic_dec(&op_async->gb->outstanding_operations); wake_up(&gb->wq_completion); /* Release resources */ gb_operation_put(operation); kfree(op_async);} static int gb_loopback_async_operation(struct gb_loopback *gb, int type, void *request, int request_size, int response_size, void *completion){ struct gb_loopback_async_operation *op_async; struct gb_operation *operation; int ret; op_async = kzalloc_obj(*op_async); if (!op_async) return -ENOMEM; operation = gb_operation_create(gb->connection, type, request_size, response_size, GFP_KERNEL); if (!operation) { kfree(op_async); return -ENOMEM; } if (request_size) memcpy(operation->request->payload, request, request_size); gb_operation_set_data(operation, op_async); op_async->gb = gb; op_async->operation = operation; op_async->completion = completion; op_async->ts = ktime_get(); atomic_inc(&gb->outstanding_operations); ret = gb_operation_request_send(operation, gb_loopback_async_operation_callback, jiffies_to_msecs(gb->jiffy_timeout), GFP_KERNEL); if (ret) { atomic_dec(&gb->outstanding_operations); gb_operation_put(operation); kfree(op_async); } return ret;} static int gb_loopback_sync_sink(struct gb_loopback *gb, u32 len){ struct gb_loopback_transfer_request *request; int retval; request = kmalloc(len + sizeof(*request), GFP_KERNEL); if (!request) return -ENOMEM; request->len = cpu_to_le32(len); retval = gb_loopback_operation_sync(gb, GB_LOOPBACK_TYPE_SINK, request, len + sizeof(*request), NULL, 0); kfree(request); return retval;} static int gb_loopback_sync_transfer(struct gb_loopback *gb, u32 len)Lines 1–500 of 1153. Load more
// SPDX-License-Identifier: GPL-2.0 /* * Loopback bridge driver for the Greybus loopback module. * * Copyright 2014 Google Inc. * Copyright 2014 Linaro Ltd. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/kernel.h> #include <linux/module.h> #include <linux/mutex.h> #include <linux/slab.h> #include <linux/kthread.h> #include <linux/delay.h> #include <linux/random.h> #include <linux/sizes.h> #include <linux/cdev.h> #include <linux/fs.h> #include <linux/kfifo.h> #include <linux/debugfs.h> #include <linux/list_sort.h> #include <linux/spinlock.h> #include <linux/workqueue.h> #include <linux/atomic.h> #include <linux/pm_runtime.h> #include <linux/greybus.h> #include <asm/div64.h> #define NSEC_PER_DAY 86400000000000ULL struct gb_loopback_stats { u32 min; u32 max; u64 sum; u32 count; }; struct gb_loopback_device { struct dentry *root; u32 count; size_t size_max; /* We need to take a lock in atomic context */ spinlock_t lock; wait_queue_head_t wq; }; static struct gb_loopback_device gb_dev; struct gb_loopback_async_operation { struct gb_loopback *gb; struct gb_operation *operation; ktime_t ts; int (*completion)(struct gb_loopback_async_operation *op_async); }; struct gb_loopback { struct gb_connection *connection; struct dentry *file; struct kfifo kfifo_lat; struct mutex mutex; struct task_struct *task; struct device *dev; wait_queue_head_t wq; wait_queue_head_t wq_completion; atomic_t outstanding_operations; /* Per connection stats */ ktime_t ts; struct gb_loopback_stats latency; struct gb_loopback_stats throughput; struct gb_loopback_stats requests_per_second; struct gb_loopback_stats apbridge_unipro_latency; struct gb_loopback_stats gbphy_firmware_latency; int type; int async; int id; u32 size; u32 iteration_max; u32 iteration_count; int us_wait; u32 error; u32 requests_completed; u32 requests_timedout; u32 timeout; u32 jiffy_timeout; u32 timeout_min; u32 timeout_max; u32 outstanding_operations_max; u64 elapsed_nsecs; u32 apbridge_latency_ts; u32 gbphy_latency_ts; u32 send_count; }; static struct class loopback_class = { .name = "gb_loopback", }; static DEFINE_IDA(loopback_ida); /* Min/max values in jiffies */ #define GB_LOOPBACK_TIMEOUT_MIN 1 #define GB_LOOPBACK_TIMEOUT_MAX 10000 #define GB_LOOPBACK_FIFO_DEFAULT 8192 static unsigned int kfifo_depth = GB_LOOPBACK_FIFO_DEFAULT; module_param(kfifo_depth, uint, 0444); /* Maximum size of any one send data buffer we support */ #define MAX_PACKET_SIZE (PAGE_SIZE * 2) #define GB_LOOPBACK_US_WAIT_MAX 1000000 /* interface sysfs attributes */ #define gb_loopback_ro_attr(field) \ static ssize_t field##_show(struct device *dev, \ struct device_attribute *attr, \ char *buf) \ { \ struct gb_loopback *gb = dev_get_drvdata(dev); \ return sysfs_emit(buf, "%u\n", gb->field); \ } \ static DEVICE_ATTR_RO(field) #define gb_loopback_ro_stats_attr(name, field, type) \ static ssize_t name##_##field##_show(struct device *dev, \ struct device_attribute *attr, \ char *buf) \ { \ struct gb_loopback *gb = dev_get_drvdata(dev); \ /* Report 0 for min and max if no transfer succeeded */ \ if (!gb->requests_completed) \ return sysfs_emit(buf, "0\n"); \ return sysfs_emit(buf, "%" #type "\n", gb->name.field); \ } \ static DEVICE_ATTR_RO(name##_##field) #define gb_loopback_ro_avg_attr(name) \ static ssize_t name##_avg_show(struct device *dev, \ struct device_attribute *attr, \ char *buf) \ { \ struct gb_loopback_stats *stats; \ struct gb_loopback *gb; \ u64 avg, rem; \ u32 count; \ gb = dev_get_drvdata(dev); \ stats = &gb->name; \ count = stats->count ? stats->count : 1; \ avg = stats->sum + count / 2000000; /* round closest */ \ rem = do_div(avg, count); \ rem *= 1000000; \ do_div(rem, count); \ return sysfs_emit(buf, "%llu.%06u\n", avg, (u32)rem); \ } \ static DEVICE_ATTR_RO(name##_avg) #define gb_loopback_stats_attrs(field) \ gb_loopback_ro_stats_attr(field, min, u); \ gb_loopback_ro_stats_attr(field, max, u); \ gb_loopback_ro_avg_attr(field) #define gb_dev_loopback_ro_attr(field) \ static ssize_t field##_show(struct device *dev, \ struct device_attribute *attr, \ char *buf) \ { \ struct gb_loopback *gb = dev_get_drvdata(dev); \ return sysfs_emit(buf, "%u\n", gb->field); \ } \ static DEVICE_ATTR_RO(field) #define gb_dev_loopback_rw_attr(field, type) \ static ssize_t field##_show(struct device *dev, \ struct device_attribute *attr, \ char *buf) \ { \ struct gb_loopback *gb = dev_get_drvdata(dev); \ return sysfs_emit(buf, "%" #type "\n", gb->field); \ } \ static ssize_t field##_store(struct device *dev, \ struct device_attribute *attr, \ const char *buf, \ size_t len) \ { \ int ret; \ struct gb_loopback *gb = dev_get_drvdata(dev); \ mutex_lock(&gb->mutex); \ ret = sscanf(buf, "%"#type, &gb->field); \ if (ret != 1) \ len = -EINVAL; \ else \ gb_loopback_check_attr(gb); \ mutex_unlock(&gb->mutex); \ return len; \ } \ static DEVICE_ATTR_RW(field) static void gb_loopback_reset_stats(struct gb_loopback *gb); static void gb_loopback_check_attr(struct gb_loopback *gb) { if (gb->us_wait > GB_LOOPBACK_US_WAIT_MAX) gb->us_wait = GB_LOOPBACK_US_WAIT_MAX; if (gb->size > gb_dev.size_max) gb->size = gb_dev.size_max; gb->requests_timedout = 0; gb->requests_completed = 0; gb->iteration_count = 0; gb->send_count = 0; gb->error = 0; if (kfifo_depth < gb->iteration_max) { dev_warn(gb->dev, "cannot log bytes %u kfifo_depth %u\n", gb->iteration_max, kfifo_depth); } kfifo_reset_out(&gb->kfifo_lat); switch (gb->type) { case GB_LOOPBACK_TYPE_PING: case GB_LOOPBACK_TYPE_TRANSFER: case GB_LOOPBACK_TYPE_SINK: gb->jiffy_timeout = usecs_to_jiffies(gb->timeout); if (!gb->jiffy_timeout) gb->jiffy_timeout = GB_LOOPBACK_TIMEOUT_MIN; else if (gb->jiffy_timeout > GB_LOOPBACK_TIMEOUT_MAX) gb->jiffy_timeout = GB_LOOPBACK_TIMEOUT_MAX; gb_loopback_reset_stats(gb); wake_up(&gb->wq); break; default: gb->type = 0; break; } } /* Time to send and receive one message */ gb_loopback_stats_attrs(latency); /* Number of requests sent per second on this cport */ gb_loopback_stats_attrs(requests_per_second); /* Quantity of data sent and received on this cport */ gb_loopback_stats_attrs(throughput); /* Latency across the UniPro link from APBridge's perspective */ gb_loopback_stats_attrs(apbridge_unipro_latency); /* Firmware induced overhead in the GPBridge */ gb_loopback_stats_attrs(gbphy_firmware_latency); /* Number of errors encountered during loop */ gb_loopback_ro_attr(error); /* Number of requests successfully completed async */ gb_loopback_ro_attr(requests_completed); /* Number of requests timed out async */ gb_loopback_ro_attr(requests_timedout); /* Timeout minimum in useconds */ gb_loopback_ro_attr(timeout_min); /* Timeout minimum in useconds */ gb_loopback_ro_attr(timeout_max); /* * Type of loopback message to send based on protocol type definitions * 0 => Don't send message * 2 => Send ping message continuously (message without payload) * 3 => Send transfer message continuously (message with payload, * payload returned in response) * 4 => Send a sink message (message with payload, no payload in response) */ gb_dev_loopback_rw_attr(type, d); /* Size of transfer message payload: 0-4096 bytes */ gb_dev_loopback_rw_attr(size, u); /* Time to wait between two messages: 0-1000 ms */ gb_dev_loopback_rw_attr(us_wait, d); /* Maximum iterations for a given operation: 1-(2^32-1), 0 implies infinite */ gb_dev_loopback_rw_attr(iteration_max, u); /* The current index of the for (i = 0; i < iteration_max; i++) loop */ gb_dev_loopback_ro_attr(iteration_count); /* A flag to indicate synchronous or asynchronous operations */ gb_dev_loopback_rw_attr(async, u); /* Timeout of an individual asynchronous request */ gb_dev_loopback_rw_attr(timeout, u); /* Maximum number of in-flight operations before back-off */ gb_dev_loopback_rw_attr(outstanding_operations_max, u); static struct attribute *loopback_attrs[] = { &dev_attr_latency_min.attr, &dev_attr_latency_max.attr, &dev_attr_latency_avg.attr, &dev_attr_requests_per_second_min.attr, &dev_attr_requests_per_second_max.attr, &dev_attr_requests_per_second_avg.attr, &dev_attr_throughput_min.attr, &dev_attr_throughput_max.attr, &dev_attr_throughput_avg.attr, &dev_attr_apbridge_unipro_latency_min.attr, &dev_attr_apbridge_unipro_latency_max.attr, &dev_attr_apbridge_unipro_latency_avg.attr, &dev_attr_gbphy_firmware_latency_min.attr, &dev_attr_gbphy_firmware_latency_max.attr, &dev_attr_gbphy_firmware_latency_avg.attr, &dev_attr_type.attr, &dev_attr_size.attr, &dev_attr_us_wait.attr, &dev_attr_iteration_count.attr, &dev_attr_iteration_max.attr, &dev_attr_async.attr, &dev_attr_error.attr, &dev_attr_requests_completed.attr, &dev_attr_requests_timedout.attr, &dev_attr_timeout.attr, &dev_attr_outstanding_operations_max.attr, &dev_attr_timeout_min.attr, &dev_attr_timeout_max.attr, NULL, }; ATTRIBUTE_GROUPS(loopback); static void gb_loopback_calculate_stats(struct gb_loopback *gb, bool error); static u32 gb_loopback_nsec_to_usec_latency(u64 elapsed_nsecs) { do_div(elapsed_nsecs, NSEC_PER_USEC); return elapsed_nsecs; } static u64 __gb_loopback_calc_latency(u64 t1, u64 t2) { if (t2 > t1) return t2 - t1; else return NSEC_PER_DAY - t2 + t1; } static u64 gb_loopback_calc_latency(ktime_t ts, ktime_t te) { return __gb_loopback_calc_latency(ktime_to_ns(ts), ktime_to_ns(te)); } static int gb_loopback_operation_sync(struct gb_loopback *gb, int type, void *request, int request_size, void *response, int response_size) { struct gb_operation *operation; ktime_t ts, te; int ret; ts = ktime_get(); operation = gb_operation_create(gb->connection, type, request_size, response_size, GFP_KERNEL); if (!operation) return -ENOMEM; if (request_size) memcpy(operation->request->payload, request, request_size); ret = gb_operation_request_send_sync(operation); if (ret) { dev_err(&gb->connection->bundle->dev, "synchronous operation failed: %d\n", ret); goto out_put_operation; } else { if (response_size == operation->response->payload_size) { memcpy(response, operation->response->payload, response_size); } else { dev_err(&gb->connection->bundle->dev, "response size %zu expected %d\n", operation->response->payload_size, response_size); ret = -EINVAL; goto out_put_operation; } } te = ktime_get(); /* Calculate the total time the message took */ gb->elapsed_nsecs = gb_loopback_calc_latency(ts, te); out_put_operation: gb_operation_put(operation); return ret; } static void gb_loopback_async_wait_all(struct gb_loopback *gb) { wait_event(gb->wq_completion, !atomic_read(&gb->outstanding_operations)); } static void gb_loopback_async_operation_callback(struct gb_operation *operation) { struct gb_loopback_async_operation *op_async; struct gb_loopback *gb; ktime_t te; int result; te = ktime_get(); result = gb_operation_result(operation); op_async = gb_operation_get_data(operation); gb = op_async->gb; mutex_lock(&gb->mutex); if (!result && op_async->completion) result = op_async->completion(op_async); if (!result) { gb->elapsed_nsecs = gb_loopback_calc_latency(op_async->ts, te); } else { gb->error++; if (result == -ETIMEDOUT) gb->requests_timedout++; } gb->iteration_count++; gb_loopback_calculate_stats(gb, result); mutex_unlock(&gb->mutex); dev_dbg(&gb->connection->bundle->dev, "complete operation %d\n", operation->id); /* Wake up waiters */ atomic_dec(&op_async->gb->outstanding_operations); wake_up(&gb->wq_completion); /* Release resources */ gb_operation_put(operation); kfree(op_async); } static int gb_loopback_async_operation(struct gb_loopback *gb, int type, void *request, int request_size, int response_size, void *completion) { struct gb_loopback_async_operation *op_async; struct gb_operation *operation; int ret; op_async = kzalloc_obj(*op_async); if (!op_async) return -ENOMEM; operation = gb_operation_create(gb->connection, type, request_size, response_size, GFP_KERNEL); if (!operation) { kfree(op_async); return -ENOMEM; } if (request_size) memcpy(operation->request->payload, request, request_size); gb_operation_set_data(operation, op_async); op_async->gb = gb; op_async->operation = operation; op_async->completion = completion; op_async->ts = ktime_get(); atomic_inc(&gb->outstanding_operations); ret = gb_operation_request_send(operation, gb_loopback_async_operation_callback, jiffies_to_msecs(gb->jiffy_timeout), GFP_KERNEL); if (ret) { atomic_dec(&gb->outstanding_operations); gb_operation_put(operation); kfree(op_async); } return ret; } static int gb_loopback_sync_sink(struct gb_loopback *gb, u32 len) { struct gb_loopback_transfer_request *request; int retval; request = kmalloc(len + sizeof(*request), GFP_KERNEL); if (!request) return -ENOMEM; request->len = cpu_to_le32(len); retval = gb_loopback_operation_sync(gb, GB_LOOPBACK_TYPE_SINK, request, len + sizeof(*request), NULL, 0); kfree(request); return retval; } static int gb_loopback_sync_transfer(struct gb_loopback *gb, u32 len)