#include <linux/workqueue.h>
#include "greybus.h"
+#include "greybus_trace.h"
static struct kmem_cache *gb_operation_cache;
static struct kmem_cache *gb_message_cache;
/* Workqueue to handle Greybus operation completions. */
-static struct workqueue_struct *gb_operation_workqueue;
+static struct workqueue_struct *gb_operation_completion_wq;
/* Wait queue for synchronous cancellations. */
static DECLARE_WAIT_QUEUE_HEAD(gb_operation_cancellation_queue);
{
struct gb_connection *connection = message->operation->connection;
+ trace_gb_message_send(message);
return connection->hd->driver->message_send(connection->hd,
connection->hd_cport_id,
message,
- GFP_KERNEL);
+ gfp);
}
/*
*/
static void gb_message_cancel(struct gb_message *message)
{
- struct greybus_host_device *hd = message->operation->connection->hd;
+ struct gb_host_device *hd = message->operation->connection->hd;
hd->driver->message_cancel(message);
}
if (protocol->request_recv) {
status = protocol->request_recv(operation->type, operation);
} else {
- dev_err(&operation->connection->dev,
+ dev_err(&operation->connection->bundle->dev,
"unexpected incoming request type 0x%02hhx\n",
operation->type);
ret = gb_operation_response_send(operation, status);
if (ret) {
- dev_err(&operation->connection->dev,
- "failed to send response %d: %d\n",
- status, ret);
+ dev_err(&operation->connection->bundle->dev,
+ "failed to send response %d for type 0x%02hhx: %d\n",
+ status, operation->type, ret);
return;
}
}
gb_operation_put(operation);
}
-static void gb_operation_message_init(struct greybus_host_device *hd,
+static void gb_operation_message_init(struct gb_host_device *hd,
struct gb_message *message, u16 operation_id,
size_t payload_size, u8 type)
{
* message payload / the message size
*/
static struct gb_message *
-gb_operation_message_alloc(struct greybus_host_device *hd, u8 type,
+gb_operation_message_alloc(struct gb_host_device *hd, u8 type,
size_t payload_size, gfp_t gfp_flags)
{
struct gb_message *message;
}
bool gb_operation_response_alloc(struct gb_operation *operation,
- size_t response_size)
+ size_t response_size, gfp_t gfp)
{
- struct greybus_host_device *hd = operation->connection->hd;
+ struct gb_host_device *hd = operation->connection->hd;
struct gb_operation_msg_hdr *request_header;
struct gb_message *response;
u8 type;
type = operation->type | GB_MESSAGE_TYPE_RESPONSE;
- response = gb_operation_message_alloc(hd, type, response_size,
- GFP_KERNEL);
+ response = gb_operation_message_alloc(hd, type, response_size, gfp);
if (!response)
return false;
response->operation = operation;
size_t request_size, size_t response_size,
unsigned long op_flags, gfp_t gfp_flags)
{
- struct greybus_host_device *hd = connection->hd;
+ struct gb_host_device *hd = connection->hd;
struct gb_operation *operation;
operation = kmem_cache_zalloc(gb_operation_cache, gfp_flags);
/* Allocate the response buffer for outgoing operations */
if (!(op_flags & GB_OPERATION_FLAG_INCOMING)) {
- if (!gb_operation_response_alloc(operation, response_size))
+ if (!gb_operation_response_alloc(operation, response_size,
+ gfp_flags)) {
goto err_request;
+ }
}
operation->flags = op_flags;
size_t gb_operation_get_payload_size_max(struct gb_connection *connection)
{
- struct greybus_host_device *hd = connection->hd;
+ struct gb_host_device *hd = connection->hd;
return hd->buffer_size_max - sizeof(struct gb_operation_msg_hdr);
}
if (!operation->response &&
!gb_operation_is_unidirectional(operation)) {
- if (!gb_operation_response_alloc(operation, 0))
+ if (!gb_operation_response_alloc(operation, 0, GFP_KERNEL))
return -ENOMEM;
}
/* Record the result */
if (!gb_operation_result_set(operation, errno)) {
- dev_err(&connection->dev, "request result already set\n");
+ dev_err(&connection->bundle->dev, "request result already set\n");
return -EIO; /* Shouldn't happen */
}
/*
* This function is called when a message send request has completed.
*/
-void greybus_message_sent(struct greybus_host_device *hd,
+void greybus_message_sent(struct gb_host_device *hd,
struct gb_message *message, int status)
{
- struct gb_operation *operation;
+ struct gb_operation *operation = message->operation;
+ struct gb_connection *connection = operation->connection;
/*
* If the message was a response, we just need to drop our
* attempting to send it, record that as the result of
* the operation and schedule its completion.
*/
- operation = message->operation;
if (message == operation->response) {
if (status) {
- dev_err(&operation->connection->dev,
- "error sending response: %d\n", status);
+ dev_err(&connection->bundle->dev,
+ "error sending response type 0x%02hhx: %d\n",
+ operation->type, status);
}
gb_operation_put_active(operation);
gb_operation_put(operation);
} else if (status) {
- if (gb_operation_result_set(operation, status))
- queue_work(gb_operation_workqueue, &operation->work);
+ if (gb_operation_result_set(operation, status)) {
+ queue_work(gb_operation_completion_wq,
+ &operation->work);
+ }
}
}
EXPORT_SYMBOL_GPL(greybus_message_sent);
operation = gb_operation_create_incoming(connection, operation_id,
type, data, size);
if (!operation) {
- dev_err(&connection->dev, "can't create operation\n");
- return; /* XXX Respond with pre-allocated ENOMEM */
+ dev_err(&connection->bundle->dev,
+ "can't create incoming operation\n");
+ return;
}
ret = gb_operation_get_active(operation);
gb_operation_put(operation);
return;
}
+ trace_gb_message_recv_request(operation->request);
/*
* The initial reference to the operation will be dropped when the
* request handler returns.
*/
if (gb_operation_result_set(operation, -EINPROGRESS))
- queue_work(gb_operation_workqueue, &operation->work);
+ queue_work(connection->wq, &operation->work);
}
/*
operation = gb_operation_find_outgoing(connection, operation_id);
if (!operation) {
- dev_err(&connection->dev, "operation not found\n");
+ dev_err(&connection->bundle->dev,
+ "unexpected response 0x%04hx received\n", operation_id);
return;
}
message = operation->response;
message_size = sizeof(*message->header) + message->payload_size;
if (!errno && size != message_size) {
- dev_err(&connection->dev, "bad message size (%zu != %zu)\n",
- size, message_size);
+ dev_err(&connection->bundle->dev,
+ "malformed response of type 0x%02hhx received (%zu != %zu)\n",
+ message->header->type, size, message_size);
errno = -EMSGSIZE;
}
+ trace_gb_message_recv_response(operation->response);
/* We must ignore the payload if a bad status is returned */
if (errno)
/* The rest will be handled in work queue context */
if (gb_operation_result_set(operation, errno)) {
memcpy(message->header, data, size);
- queue_work(gb_operation_workqueue, &operation->work);
+ queue_work(gb_operation_completion_wq, &operation->work);
}
gb_operation_put(operation);
void *data, size_t size)
{
struct gb_operation_msg_hdr header;
+ struct device *dev = &connection->bundle->dev;
size_t msg_size;
u16 operation_id;
if (connection->state != GB_CONNECTION_STATE_ENABLED) {
- dev_err(&connection->dev, "dropping %zu received bytes\n",
- size);
+ dev_warn(dev, "dropping %zu received bytes\n", size);
return;
}
if (size < sizeof(header)) {
- dev_err(&connection->dev, "message too small\n");
+ dev_err(dev, "short message received\n");
return;
}
memcpy(&header, data, sizeof(header));
msg_size = le16_to_cpu(header.size);
if (size < msg_size) {
- dev_err(&connection->dev,
- "incomplete message received: 0x%04x (%zu < %zu)\n",
- le16_to_cpu(header.operation_id), size, msg_size);
+ dev_err(dev,
+ "incomplete message 0x%04hx of type 0x%02hhx received (%zu < %zu)\n",
+ le16_to_cpu(header.operation_id), header.type, size,
+ msg_size);
return; /* XXX Should still complete operation */
}
if (gb_operation_result_set(operation, errno)) {
gb_message_cancel(operation->request);
- queue_work(gb_operation_workqueue, &operation->work);
+ queue_work(gb_operation_completion_wq, &operation->work);
}
+ trace_gb_message_cancel_outgoing(operation->request);
atomic_inc(&operation->waiters);
wait_event(gb_operation_cancellation_queue,
if (!gb_operation_result_set(operation, errno))
gb_message_cancel(operation->response);
}
+ trace_gb_message_cancel_incoming(operation->response);
atomic_inc(&operation->waiters);
wait_event(gb_operation_cancellation_queue,
* @request_size: size of @request
* @response: pointer to a memory buffer to copy the response to
* @response_size: the size of @response.
+ * @timeout: operation timeout in milliseconds
*
* This function implements a simple synchronous Greybus operation. It sends
* the provided operation request and waits (sleeps) until the corresponding
*
* If there is an error, the response buffer is left alone.
*/
-int gb_operation_sync(struct gb_connection *connection, int type,
- void *request, int request_size,
- void *response, int response_size)
+int gb_operation_sync_timeout(struct gb_connection *connection, int type,
+ void *request, int request_size,
+ void *response, int response_size,
+ unsigned int timeout)
{
struct gb_operation *operation;
int ret;
if (request_size)
memcpy(operation->request->payload, request, request_size);
- ret = gb_operation_request_send_sync(operation);
+ ret = gb_operation_request_send_sync_timeout(operation, timeout);
if (ret) {
- dev_err(&connection->dev, "synchronous operation failed: %d\n",
- ret);
+ dev_err(&connection->bundle->dev,
+ "synchronous operation of type 0x%02hhx failed: %d\n",
+ type, ret);
} else {
if (response_size) {
memcpy(response, operation->response->payload,
response_size);
}
}
- gb_operation_destroy(operation);
+
+ gb_operation_put(operation);
return ret;
}
-EXPORT_SYMBOL_GPL(gb_operation_sync);
+EXPORT_SYMBOL_GPL(gb_operation_sync_timeout);
int __init gb_operation_init(void)
{
if (!gb_operation_cache)
goto err_destroy_message_cache;
- gb_operation_workqueue = alloc_workqueue("greybus_operation", 0, 1);
- if (!gb_operation_workqueue)
- goto err_operation;
+ gb_operation_completion_wq = alloc_workqueue("greybus_completion",
+ 0, 0);
+ if (!gb_operation_completion_wq)
+ goto err_destroy_operation_cache;
return 0;
-err_operation:
+
+err_destroy_operation_cache:
kmem_cache_destroy(gb_operation_cache);
gb_operation_cache = NULL;
err_destroy_message_cache:
void gb_operation_exit(void)
{
- destroy_workqueue(gb_operation_workqueue);
- gb_operation_workqueue = NULL;
+ destroy_workqueue(gb_operation_completion_wq);
+ gb_operation_completion_wq = NULL;
kmem_cache_destroy(gb_operation_cache);
gb_operation_cache = NULL;
kmem_cache_destroy(gb_message_cache);