Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 23f7911b authored by Elliot Berman's avatar Elliot Berman
Browse files

haven: dbl: Make hh_dbl_ functions wait for capid availability



Client drivers may successfully register with a doorbell even before
doorbell capids are known to Linux. However, we do need to know capids
for send, read, reset, and mask functions. Here, we mirror the behavior
of message queues by placing the calling thread in a waitqueue until the
capids are populated by RM.

Change-Id: Ie1cf98d0fe344bb2a878bb655db78705d4292ff9
Signed-off-by: default avatarElliot Berman <eberman@codeaurora.org>
parent 90f8f682
Loading
Loading
Loading
Loading
+53 −24
Original line number Diff line number Diff line
@@ -34,6 +34,7 @@ struct hh_dbl_cap_table {
	const char *rx_irq_name;
	dbl_rx_cb_t rx_callback;
	void *rx_priv_data;
	wait_queue_head_t cap_wq;
};

static bool hh_dbl_initialized;
@@ -43,11 +44,11 @@ static struct hh_dbl_cap_table hh_dbl_cap_table[HH_DBL_LABEL_MAX];
 * hh_dbl_validate_params - Validate doorbell common parameters
 */
static int hh_dbl_validate_params(struct hh_dbl_desc *client_desc,
				  enum hh_dbl_dir dir)
			enum hh_dbl_dir dir, const unsigned long flags)
{
	struct hh_dbl_cap_table *cap_table_entry;

	if (!client_desc)
	if (IS_ERR_OR_NULL(client_desc))
		return -EINVAL;

	/* Check if the client has manipulated the label */
@@ -68,23 +69,35 @@ static int hh_dbl_validate_params(struct hh_dbl_desc *client_desc,
	 * There are no doorbell setup for Tx or Rx
	 */
	if (dir == HH_DBL_DIRECTION_RX) {
		if ((cap_table_entry->rx_cap_id == HH_CAPID_INVAL) ||
		    !cap_table_entry->rx_reg_done) {
			pr_err("%s: label: %d; rx_cap_id: %llu; dir: %d rx_done: %d\n",
				__func__, client_desc->label,
				cap_table_entry->rx_cap_id,
				dir, cap_table_entry->rx_reg_done);
		if (!cap_table_entry->rx_reg_done)
			return -EINVAL;

		if (cap_table_entry->rx_cap_id != HH_CAPID_INVAL)
			return 0;

		if (flags & HH_DBL_NONBLOCK)
			return -EAGAIN;
		}

		if (wait_event_interruptible(cap_table_entry->cap_wq,
				cap_table_entry->rx_cap_id != HH_CAPID_INVAL))
			return -ERESTARTSYS;

		return 0;
	} else {
		if ((cap_table_entry->tx_cap_id == HH_CAPID_INVAL) ||
		    !cap_table_entry->tx_reg_done) {
			pr_err("%s: label: %d; tx_cap_id: %llu; dir: %d tx_done: %d\n",
				__func__, client_desc->label,
				cap_table_entry->tx_cap_id,
				dir, cap_table_entry->tx_reg_done);
		if (!cap_table_entry->tx_reg_done)
			return -EINVAL;

		if (cap_table_entry->tx_cap_id != HH_CAPID_INVAL)
			return 0;

		if (flags & HH_DBL_NONBLOCK)
			return -EAGAIN;
		}

		if (wait_event_interruptible(cap_table_entry->cap_wq,
				cap_table_entry->tx_cap_id != HH_CAPID_INVAL))
			return -ERESTARTSYS;

		return 0;
	}

	return 0;
@@ -94,6 +107,8 @@ static int hh_dbl_validate_params(struct hh_dbl_desc *client_desc,
 * hh_dbl_read_and_clean - Automatically read and clear the flags in doorbell
 * @client_desc: client handle to indetify the doorbell object
 * @clear_flags: clear the bits mentioned in the clear_flags
 * @flags: Optional flags to pass to send the data. For the list of flags,
 *         see linux/haven/hh_dbl.h
 *
 * Reads and clears the flags of the Doorbell object. If there is a pending
 * bound virtual interrupt, it will be de-asserted
@@ -102,7 +117,8 @@ static int hh_dbl_validate_params(struct hh_dbl_desc *client_desc,
 * 0 on success, @clear_flags contains the doorbell’s previous unmasked flags
 * before the @clear_flags were removed.
 */
int hh_dbl_read_and_clean(void *dbl_client_desc, hh_dbl_flags_t *clear_flags)
int hh_dbl_read_and_clean(void *dbl_client_desc, hh_dbl_flags_t *clear_flags,
			  const unsigned long flags)
{
	struct hh_dbl_cap_table *cap_table_entry;
	struct hh_hcall_dbl_recv_resp recv_resp;
@@ -112,7 +128,7 @@ int hh_dbl_read_and_clean(void *dbl_client_desc, hh_dbl_flags_t *clear_flags)
	if (!clear_flags)
		return -EINVAL;

	ret = hh_dbl_validate_params(client_desc, HH_DBL_DIRECTION_RX);
	ret = hh_dbl_validate_params(client_desc, HH_DBL_DIRECTION_RX, flags);
	if (ret)
		return ret;

@@ -138,6 +154,8 @@ EXPORT_SYMBOL(hh_dbl_read_and_clean);
 *				 the doorbell's bound virtual interrupt
 * @ack_mask: Controls which flags should be automatically cleared
 *			  when the interrupt is asserted
 * @flags: Optional flags to pass to send the data. For the list of flags,
 *         see linux/haven/hh_dbl.h
 *
 * Sets the Doorbell object’s masks. A doorbell object has two masks
 * which are configured by the receiver to control which flags it is
@@ -147,13 +165,13 @@ EXPORT_SYMBOL(hh_dbl_read_and_clean);
 * 0 on success
 */
int hh_dbl_set_mask(void *dbl_client_desc, hh_dbl_flags_t enable_mask,
						hh_dbl_flags_t ack_mask)
		    hh_dbl_flags_t ack_mask, const unsigned long flags)
{
	struct hh_dbl_cap_table *cap_table_entry;
	struct hh_dbl_desc *client_desc = dbl_client_desc;
	int ret, hh_ret;

	ret = hh_dbl_validate_params(client_desc, HH_DBL_DIRECTION_RX);
	ret = hh_dbl_validate_params(client_desc, HH_DBL_DIRECTION_RX, flags);
	if (ret)
		return ret;

@@ -177,6 +195,8 @@ EXPORT_SYMBOL(hh_dbl_set_mask);
 * @client_desc: client handle to indetify the doorbell object
 * @newflags: flags to set in the doorbell. This flag along with enable_mask
 *			  in the doorbell decide whehter to raise vIRQ are not.
 * @flags: Optional flags to pass to send the data. For the list of flags,
 *         see linux/haven/hh_dbl.h
 *
 * Set flags in the doorbell. If following the send, the set of enabled flags
 * as defined by the bitwise-AND of the doorbell flags with the EnableMask,
@@ -186,7 +206,8 @@ EXPORT_SYMBOL(hh_dbl_set_mask);
 * 0 on success, @newflags contains the doorbell’s previous unmasked flags
 * before the @newflags were added.
 */
int hh_dbl_send(void *dbl_client_desc, hh_dbl_flags_t *newflags)
int hh_dbl_send(void *dbl_client_desc, hh_dbl_flags_t *newflags,
		unsigned long flags)
{
	struct hh_dbl_cap_table *cap_table_entry;
	struct hh_hcall_dbl_send_resp send_resp;
@@ -196,7 +217,7 @@ int hh_dbl_send(void *dbl_client_desc, hh_dbl_flags_t *newflags)
	if (!newflags)
		return -EINVAL;

	ret = hh_dbl_validate_params(client_desc, HH_DBL_DIRECTION_TX);
	ret = hh_dbl_validate_params(client_desc, HH_DBL_DIRECTION_TX, flags);
	if (ret)
		return ret;

@@ -219,6 +240,8 @@ EXPORT_SYMBOL(hh_dbl_send);
 * hh_dbl_reset - clear all the flags of the doorbell and sets all bits in
 *				  the Doorbell’s mask.
 * @client_desc: client handle to indetify the doorbell object
 * @flags: Optional flags to pass to send the data. For the list of flags,
 *         see linux/haven/hh_dbl.h
 *
 * Clears all the flags of the doorbell and sets all bits in the doorbell’s
 * mask. If there is a pending bound virtual interrupt, it will be de-asserted.
@@ -226,13 +249,13 @@ EXPORT_SYMBOL(hh_dbl_send);
 * Returns:
 * 0 on success
 */
int hh_dbl_reset(void *dbl_client_desc)
int hh_dbl_reset(void *dbl_client_desc, const unsigned long flags)
{
	struct hh_dbl_cap_table *cap_table_entry;
	struct hh_dbl_desc *client_desc = dbl_client_desc;
	int ret, hh_ret;

	ret = hh_dbl_validate_params(client_desc, HH_DBL_DIRECTION_RX);
	ret = hh_dbl_validate_params(client_desc, HH_DBL_DIRECTION_RX, flags);
	if (ret)
		return ret;

@@ -518,6 +541,9 @@ int hh_dbl_populate_cap_info(enum hh_dbl_label label, u64 cap_id,
			goto err;
		}
		cap_table_entry->tx_cap_id = cap_id;

		wake_up_interruptible(&cap_table_entry->cap_wq);

		pr_debug("%s: label: %d; tx_cap_id: %llu; dir: %d; rx_irq: %d\n",
			__func__, label, cap_id, direction, rx_irq);
		break;
@@ -543,6 +569,8 @@ int hh_dbl_populate_cap_info(enum hh_dbl_label label, u64 cap_id,
			goto err;
		}

		wake_up_interruptible(&cap_table_entry->cap_wq);

		pr_debug("%s: label: %d; rx_cap_id: %llu; dir: %d; rx_irq: %d\n",
			__func__, label, cap_id, direction, rx_irq);
		break;
@@ -581,6 +609,7 @@ static int __init hh_dbl_init(void)
	for (i = 0; i < HH_DBL_LABEL_MAX; i++) {
		entry = &hh_dbl_cap_table[i];
		mutex_init(&entry->cap_entry_lock);
		init_waitqueue_head(&entry->cap_wq);
		entry->tx_cap_id = HH_CAPID_INVAL;
		entry->rx_cap_id = HH_CAPID_INVAL;
		entry->rx_irq_name = kasprintf(GFP_KERNEL, "hh_dbl_rx_%d", i);
+16 −8
Original line number Diff line number Diff line
@@ -19,6 +19,9 @@ enum hh_dbl_label {
	HH_DBL_LABEL_MAX
};

/* Possible flags to pass for send, set_mask, read, reset */
#define HH_DBL_NONBLOCK		BIT(32)

#if IS_ENABLED(CONFIG_HH_DBL)
void *hh_dbl_tx_register(enum hh_dbl_label label);
void *hh_dbl_rx_register(enum hh_dbl_label label, dbl_rx_cb_t rx_cb,
@@ -27,11 +30,13 @@ void *hh_dbl_rx_register(enum hh_dbl_label label, dbl_rx_cb_t rx_cb,
int hh_dbl_tx_unregister(void *dbl_client_desc);
int hh_dbl_rx_unregister(void *dbl_client_desc);

int hh_dbl_send(void *dbl_client_desc, uint64_t *newflags);
int hh_dbl_send(void *dbl_client_desc, uint64_t *newflags,
		const unsigned long flags);
int hh_dbl_set_mask(void *dbl_client_desc, hh_dbl_flags_t enable_mask,
		    hh_dbl_flags_t ack_mask);
int hh_dbl_read_and_clean(void *dbl_client_desc, hh_dbl_flags_t *clear_flags);
int hh_dbl_reset(void *dbl_client_desc);
		    hh_dbl_flags_t ack_mask, const unsigned long flags);
int hh_dbl_read_and_clean(void *dbl_client_desc, hh_dbl_flags_t *clear_flags,
			  const unsigned long flags);
int hh_dbl_reset(void *dbl_client_desc, const unsigned long flags);
int hh_dbl_populate_cap_info(enum hh_dbl_label label, u64 cap_id,
						int direction, int rx_irq);
#else
@@ -57,25 +62,28 @@ static inline int hh_dbl_rx_unregister(void *dbl_client_desc)
	return ERR_PTR(-ENODEV);
}

static inline int hh_dbl_send(void *dbl_client_desc, uint64_t *newflags)
static inline int hh_dbl_send(void *dbl_client_desc, uint64_t *newflags,
			      const unsigned long flags)
{
	return -EINVAL;
}

static inline int hh_dbl_set_mask(void *dbl_client_desc,
		    hh_dbl_flags_t enable_mask,
		    hh_dbl_flags_t ack_mask)
		    hh_dbl_flags_t ack_mask,
		    const unsigned long flags)
{
	return -EINVAL;
}

static inline int hh_dbl_read_and_clean(void *dbl_client_desc,
					hh_dbl_flags_t *clear_flags)
					hh_dbl_flags_t *clear_flags,
					const unsigned long flags)
{
	return -EINVAL;
}

static inline int hh_dbl_reset(void *dbl_client_desc)
static inline int hh_dbl_reset(void *dbl_client_desc, const unsigned long flags)
{
	return -EINVAL;
}
+1 −1
Original line number Diff line number Diff line
@@ -94,7 +94,7 @@ static inline int channel_hh_kick(struct neuron_mq_data_priv *priv)
	hh_dbl_flags_t dbl_mask = CH_DBL_MASK;
	int ret;

	ret = hh_dbl_send(priv->tx_dbl, &dbl_mask);
	ret = hh_dbl_send(priv->tx_dbl, &dbl_mask, 0);
	if (ret)
		pr_err("failed to raise virq to the sender %d\n", ret);

+1 −1
Original line number Diff line number Diff line
@@ -68,7 +68,7 @@ static inline int channel_hh_kick(struct neuron_mq_data_priv *priv)
	hh_dbl_flags_t dbl_mask = CH_DBL_MASK;
	int ret;

	ret = hh_dbl_send(priv->tx_dbl, &dbl_mask);
	ret = hh_dbl_send(priv->tx_dbl, &dbl_mask, 0);
	if (ret)
		pr_err("failed to raise virq to the sender %d\n", ret);

+1 −1
Original line number Diff line number Diff line
@@ -68,7 +68,7 @@ static void qrtr_haven_kick(struct qrtr_haven_dev *qdev)
	hh_dbl_flags_t dbl_mask = QRTR_DBL_MASK;
	int ret;

	ret = hh_dbl_send(qdev->tx_dbl, &dbl_mask);
	ret = hh_dbl_send(qdev->tx_dbl, &dbl_mask, HH_DBL_NONBLOCK);
	if (ret)
		dev_err(qdev->dev, "failed to raise doorbell %d\n", ret);
}