/* * Process operation work. * * For incoming requests, call the protocol request handler. The operation * result should be -EINPROGRESS at this point. * * For outgoing requests, the operation result value should have * been set before queueing this. The operation callback function * allows the original requester to know the request has completed * and its result is available. */ static void gb_operation_work(struct work_struct *work) { struct gb_operation *operation; operation = container_of(work, struct gb_operation, work); if (gb_operation_is_incoming(operation)) gb_operation_request_handle(operation); else operation->callback(operation); gb_operation_put_active(operation); gb_operation_put(operation); }
/* * Cancel an outgoing operation synchronously, and record the given error to * indicate why. */ void gb_operation_cancel(struct gb_operation *operation, int errno) { if (WARN_ON(gb_operation_is_incoming(operation))) return; if (gb_operation_result_set(operation, errno)) { gb_message_cancel(operation->request); 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, !gb_operation_is_active(operation)); atomic_dec(&operation->waiters); }
/* * Looks up an outgoing operation on a connection and returns a refcounted * pointer if found, or NULL otherwise. */ static struct gb_operation * gb_operation_find_outgoing(struct gb_connection *connection, u16 operation_id) { struct gb_operation *operation; unsigned long flags; bool found = false; spin_lock_irqsave(&connection->lock, flags); list_for_each_entry(operation, &connection->operations, links) if (operation->id == operation_id && !gb_operation_is_incoming(operation)) { gb_operation_get(operation); found = true; break; } spin_unlock_irqrestore(&connection->lock, flags); return found ? operation : NULL; }
/* * Increment operation active count and add to connection list unless the * connection is going away. * * Caller holds operation reference. */ static int gb_operation_get_active(struct gb_operation *operation) { struct gb_connection *connection = operation->connection; unsigned long flags; spin_lock_irqsave(&connection->lock, flags); if (connection->state != GB_CONNECTION_STATE_ENABLED && connection->state != GB_CONNECTION_STATE_ENABLED_TX && !gb_operation_is_incoming(operation)) { spin_unlock_irqrestore(&connection->lock, flags); return -ENOTCONN; } if (operation->active++ == 0) list_add_tail(&operation->links, &connection->operations); spin_unlock_irqrestore(&connection->lock, flags); return 0; }
/* * Cancel an incoming operation synchronously. Called during connection tear * down. */ void gb_operation_cancel_incoming(struct gb_operation *operation, int errno) { if (WARN_ON(!gb_operation_is_incoming(operation))) return; if (!gb_operation_is_unidirectional(operation)) { /* * Make sure the request handler has submitted the response * before cancelling it. */ flush_work(&operation->work); 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, !gb_operation_is_active(operation)); atomic_dec(&operation->waiters); }
/* * Cancel all active operations on a connection. * * Should only be called during connection tear down. */ static void gb_connection_cancel_operations(struct gb_connection *connection, int errno) { struct gb_operation *operation; spin_lock_irq(&connection->lock); while (!list_empty(&connection->operations)) { operation = list_last_entry(&connection->operations, struct gb_operation, links); gb_operation_get(operation); spin_unlock_irq(&connection->lock); if (gb_operation_is_incoming(operation)) gb_operation_cancel_incoming(operation, errno); else gb_operation_cancel(operation, errno); gb_operation_put(operation); spin_lock_irq(&connection->lock); } spin_unlock_irq(&connection->lock); }