diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index e2340d8392ed..368f05662e3d 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -102,6 +102,15 @@ static void gb_connection_hd_cport_id_free(struct gb_connection *connection) connection->hd_cport_id = CPORT_ID_BAD; } +static void connection_timeout(struct work_struct *work) +{ + struct gb_connection *connection; + + connection = + container_of(work, struct gb_connection, timeout_work.work); + printk("timeout!\n"); +} + /* * Set up a Greybus connection, representing the bidirectional link * between a CPort on a (local) Greybus host device and a CPort on @@ -143,6 +152,7 @@ struct gb_connection *gb_connection_create(struct gb_interface *interface, INIT_LIST_HEAD(&connection->operations); connection->pending = RB_ROOT; atomic_set(&connection->op_cycle, 0); + INIT_DELAYED_WORK(&connection->timeout_work, connection_timeout); return connection; } diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index 685c1ffcb1ca..b5901a1b1a74 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -26,6 +26,7 @@ struct gb_connection { struct list_head operations; struct rb_root pending; /* awaiting reponse */ atomic_t op_cycle; + struct delayed_work timeout_work; void *private; }; diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index da455f019e33..e70e8a3faeae 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -19,6 +19,8 @@ */ #define GB_OPERATION_TYPE_RESPONSE 0x80 +#define CONNECTION_TIMEOUT_DEFAULT 1000 /* milliseconds */ + /* * XXX This needs to be coordinated with host driver parameters */ @@ -62,6 +64,8 @@ static void gb_operation_insert(struct gb_operation *operation) struct rb_node **link = &root->rb_node; struct rb_node *above = NULL; struct gb_operation_msg_hdr *header; + unsigned long timeout; + bool start_timer; __le16 wire_id; /* @@ -76,6 +80,16 @@ static void gb_operation_insert(struct gb_operation *operation) /* OK, insert the operation into its connection's tree */ spin_lock_irq(&gb_operations_lock); + /* + * We impose a time limit for requests to complete. If + * there are no requests pending there is no need for a + * timer. So if this will be the only one in flight we'll + * need to start the timer. Otherwise we just update the + * existing one to give this request a full timeout period + * to complete. + */ + start_timer = RB_EMPTY_ROOT(root); + while (*link) { struct gb_operation *other; @@ -89,15 +103,31 @@ static void gb_operation_insert(struct gb_operation *operation) } rb_link_node(node, above, link); rb_insert_color(node, root); - spin_unlock_irq(&gb_operations_lock); + + timeout = msecs_to_jiffies(CONNECTION_TIMEOUT_DEFAULT); + if (start_timer) + schedule_delayed_work(&connection->timeout_work, timeout); + else + mod_delayed_work(system_wq, &connection->timeout_work, timeout); } static void gb_operation_remove(struct gb_operation *operation) { + struct gb_connection *connection = operation->connection; + bool last_pending; + spin_lock_irq(&gb_operations_lock); - rb_erase(&operation->node, &operation->connection->pending); + rb_erase(&operation->node, &connection->pending); + last_pending = RB_EMPTY_ROOT(&connection->pending); spin_unlock_irq(&gb_operations_lock); + + /* + * If there are no more pending requests, we can stop the + * timeout timer. + */ + if (last_pending) + cancel_delayed_work(&connection->timeout_work); } static struct gb_operation *