mirror of
https://github.com/torvalds/linux.git
synced 2024-11-26 14:12:06 +00:00
greybus: operation: add support for short responses
Add support for operations with short responses. So far we have assumed that the initiator of an operation always knows the exact size of the expected response. This is however not always the case and we've worked around this limitation in a couple of places by, for example, first requesting the size of a resource before fetching the actual data. To avoid such workarounds and simplify our protocols, add a short-response flag that can be set when allocating an operation. When this flag is set on an operation, core will accept a response that is shorter than the size of the (pre-allocated) response payload buffer. For now, we update the response-message payload_size field to reflect the actual length of the response received. Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Tested-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
This commit is contained in:
parent
34804efb0c
commit
7e43e337a5
@ -530,20 +530,25 @@ err_cache:
|
||||
* invalid operation type for all protocols, and this is enforced
|
||||
* here.
|
||||
*/
|
||||
struct gb_operation *gb_operation_create(struct gb_connection *connection,
|
||||
u8 type, size_t request_size,
|
||||
size_t response_size,
|
||||
gfp_t gfp)
|
||||
struct gb_operation *
|
||||
gb_operation_create_flags(struct gb_connection *connection,
|
||||
u8 type, size_t request_size,
|
||||
size_t response_size, unsigned long flags,
|
||||
gfp_t gfp)
|
||||
{
|
||||
if (WARN_ON_ONCE(type == GB_OPERATION_TYPE_INVALID))
|
||||
return NULL;
|
||||
if (WARN_ON_ONCE(type & GB_MESSAGE_TYPE_RESPONSE))
|
||||
type &= ~GB_MESSAGE_TYPE_RESPONSE;
|
||||
|
||||
if (WARN_ON_ONCE(flags & ~GB_OPERATION_FLAG_USER_MASK))
|
||||
flags &= GB_OPERATION_FLAG_USER_MASK;
|
||||
|
||||
return gb_operation_create_common(connection, type,
|
||||
request_size, response_size, 0, gfp);
|
||||
request_size, response_size,
|
||||
flags, gfp);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gb_operation_create);
|
||||
EXPORT_SYMBOL_GPL(gb_operation_create_flags);
|
||||
|
||||
size_t gb_operation_get_payload_size_max(struct gb_connection *connection)
|
||||
{
|
||||
@ -875,12 +880,22 @@ static void gb_connection_recv_response(struct gb_connection *connection,
|
||||
message = operation->response;
|
||||
header = message->header;
|
||||
message_size = sizeof(*header) + message->payload_size;
|
||||
if (!errno && size != message_size) {
|
||||
if (!errno && size > message_size) {
|
||||
dev_err(&connection->hd->dev,
|
||||
"%s: malformed response 0x%02x received (%zu != %zu)\n",
|
||||
connection->name, header->type, size,
|
||||
message_size);
|
||||
"%s: malformed response 0x%02x received (%zu > %zu)\n",
|
||||
connection->name, header->type,
|
||||
size, message_size);
|
||||
errno = -EMSGSIZE;
|
||||
} else if (!errno && size < message_size) {
|
||||
if (gb_operation_short_response_allowed(operation)) {
|
||||
message->payload_size = size - sizeof(*header);
|
||||
} else {
|
||||
dev_err(&connection->hd->dev,
|
||||
"%s: short response 0x%02x received (%zu < %zu)\n",
|
||||
connection->name, header->type,
|
||||
size, message_size);
|
||||
errno = -EMSGSIZE;
|
||||
}
|
||||
}
|
||||
trace_gb_message_recv_response(operation->response);
|
||||
|
||||
|
@ -65,6 +65,9 @@ struct gb_message {
|
||||
|
||||
#define GB_OPERATION_FLAG_INCOMING BIT(0)
|
||||
#define GB_OPERATION_FLAG_UNIDIRECTIONAL BIT(1)
|
||||
#define GB_OPERATION_FLAG_SHORT_RESPONSE BIT(2)
|
||||
|
||||
#define GB_OPERATION_FLAG_USER_MASK GB_OPERATION_FLAG_SHORT_RESPONSE
|
||||
|
||||
/*
|
||||
* A Greybus operation is a remote procedure call performed over a
|
||||
@ -119,16 +122,33 @@ gb_operation_is_unidirectional(struct gb_operation *operation)
|
||||
return operation->flags & GB_OPERATION_FLAG_UNIDIRECTIONAL;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
gb_operation_short_response_allowed(struct gb_operation *operation)
|
||||
{
|
||||
return operation->flags & GB_OPERATION_FLAG_SHORT_RESPONSE;
|
||||
}
|
||||
|
||||
void gb_connection_recv(struct gb_connection *connection,
|
||||
void *data, size_t size);
|
||||
|
||||
int gb_operation_result(struct gb_operation *operation);
|
||||
|
||||
size_t gb_operation_get_payload_size_max(struct gb_connection *connection);
|
||||
struct gb_operation *gb_operation_create(struct gb_connection *connection,
|
||||
u8 type, size_t request_size,
|
||||
size_t response_size,
|
||||
gfp_t gfp);
|
||||
struct gb_operation *
|
||||
gb_operation_create_flags(struct gb_connection *connection,
|
||||
u8 type, size_t request_size,
|
||||
size_t response_size, unsigned long flags,
|
||||
gfp_t gfp);
|
||||
|
||||
static inline struct gb_operation *
|
||||
gb_operation_create(struct gb_connection *connection,
|
||||
u8 type, size_t request_size,
|
||||
size_t response_size, gfp_t gfp)
|
||||
{
|
||||
return gb_operation_create_flags(connection, type, request_size,
|
||||
response_size, 0, gfp);
|
||||
}
|
||||
|
||||
void gb_operation_get(struct gb_operation *operation);
|
||||
void gb_operation_put(struct gb_operation *operation);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user