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

Commit 51a402fd authored by Yong Ding's avatar Yong Ding
Browse files

soc: qcom: hab: use spin_lock/unlock() when local irqs are disabled



In habmm_socket_read/write(), spin_lock/unlock_bh() are naturally used
to handle the race between the tasklet, as bottom half, and process
context.

Sometimes and rarely, some kernel-space drivers(e.g., scm_qcpe.c, apr_vm.c)
could call habmm_socket_read/write() from the process context where local
irqs are disabled(e.g., spin_lock_irqsave is called).

In such case, spin_unlock_bh() will trigger such below warning log,
 warning: kernel/softirq.c:157 WARN_ON_ONCE(in_irq() || irqs_disabled()).

Actually in such case, no need for HAB to disable and enable bottom half
considering the fact that local irqs are already disabled from the caller.

Change-Id: I20c37b12b3ce7ad1ebd6f8245f9e55aa2fa215b6
Signed-off-by: default avatarYong Ding <yongding@codeaurora.org>
parent e97015c2
Loading
Loading
Loading
Loading
+9 −7
Original line number Diff line number Diff line
/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
/* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -43,19 +43,20 @@ int physical_channel_send(struct physical_channel *pchan,
	struct ghs_vdev *dev  = (struct ghs_vdev *)pchan->hyp_data;
	GIPC_Result result;
	uint8_t *msg;
	int irqs_disabled = irqs_disabled();

	spin_lock_bh(&dev->io_lock);
	hab_spin_lock(&dev->io_lock, irqs_disabled);

	result = GIPC_PrepareMessage(dev->endpoint, sizebytes+sizeof(*header),
		(void **)&msg);
	if (result == GIPC_Full) {
		spin_unlock_bh(&dev->io_lock);
		hab_spin_unlock(&dev->io_lock, irqs_disabled);
		/* need to wait for space! */
		pr_err("failed to reserve send msg for %zd bytes\n",
			sizebytes+sizeof(*header));
		return -EBUSY;
	} else if (result != GIPC_Success) {
		spin_unlock_bh(&dev->io_lock);
		hab_spin_unlock(&dev->io_lock, irqs_disabled);
		pr_err("failed to send due to error %d\n", result);
		return -ENOMEM;
	}
@@ -77,7 +78,7 @@ int physical_channel_send(struct physical_channel *pchan,

	result = GIPC_IssueMessage(dev->endpoint, sizebytes+sizeof(*header),
		header->id_type_size);
	spin_unlock_bh(&dev->io_lock);
	hab_spin_unlock(&dev->io_lock, irqs_disabled);
	if (result != GIPC_Success) {
		pr_err("send error %d, sz %zd, prot %x\n",
			result, sizebytes+sizeof(*header),
@@ -98,6 +99,7 @@ void physical_channel_rx_dispatch(unsigned long physical_channel)

	uint32_t events;
	unsigned long flags;
	int irqs_disabled = irqs_disabled();

	spin_lock_irqsave(&pchan->rxbuf_lock, flags);
	events = kgipc_dequeue_events(dev->endpoint);
@@ -111,7 +113,7 @@ void physical_channel_rx_dispatch(unsigned long physical_channel)
				dev->name, pchan->vmid_remote);

	if (events & (GIPC_EVENT_RECEIVEREADY)) {
		spin_lock_bh(&pchan->rxbuf_lock);
		hab_spin_lock(&pchan->rxbuf_lock, irqs_disabled);
		while (1) {
			dev->read_size = 0;
			dev->read_offset = 0;
@@ -133,7 +135,7 @@ void physical_channel_rx_dispatch(unsigned long physical_channel)
				result, dev->read_size);
			break;
		}
		spin_unlock_bh(&pchan->rxbuf_lock);
		hab_spin_unlock(&pchan->rxbuf_lock, irqs_disabled);
	}

	if (events & (GIPC_EVENT_SENDREADY))
+33 −1
Original line number Diff line number Diff line
/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
/* Copyright (c) 2016-2019, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -577,6 +577,38 @@ int hab_stat_show_expimp(struct hab_driver *drv, int pid, char *buf, int sz);
int hab_stat_init_sub(struct hab_driver *drv);
int hab_stat_deinit_sub(struct hab_driver *drv);

static inline void hab_spin_lock(spinlock_t *lock, int irqs_disabled)
{
	if (irqs_disabled)
		spin_lock(lock);
	else
		spin_lock_bh(lock);
}

static inline void hab_spin_unlock(spinlock_t *lock, int irqs_disabled)
{
	if (irqs_disabled)
		spin_unlock(lock);
	else
		spin_unlock_bh(lock);
}

static inline void hab_write_lock(rwlock_t *lock, int irqs_disabled)
{
	if (irqs_disabled)
		write_lock(lock);
	else
		write_lock_bh(lock);
}

static inline void hab_write_unlock(rwlock_t *lock, int irqs_disabled)
{
	if (irqs_disabled)
		write_unlock(lock);
	else
		write_unlock_bh(lock);
}

/* Global singleton HAB instance */
extern struct hab_driver hab_driver;

+18 −11
Original line number Diff line number Diff line
/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
/* Copyright (c) 2016-2019, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -15,10 +15,12 @@
static int hab_rx_queue_empty(struct virtual_channel *vchan)
{
	int ret;
	int irqs_disabled = irqs_disabled();

	spin_lock_bh(&vchan->rx_lock);
	hab_spin_lock(&vchan->rx_lock, irqs_disabled);
	ret = list_empty(&vchan->rx_list);
	spin_unlock_bh(&vchan->rx_lock);
	hab_spin_unlock(&vchan->rx_lock, irqs_disabled);

	return ret;
}

@@ -56,6 +58,7 @@ hab_msg_dequeue(struct virtual_channel *vchan, struct hab_message **msg,
	int ret = 0;
	int wait = !(flags & HABMM_SOCKET_RECV_FLAGS_NON_BLOCKING);
	int interruptible = !(flags & HABMM_SOCKET_RECV_FLAGS_UNINTERRUPTIBLE);
	int irqs_disabled = irqs_disabled();

	if (wait) {
		if (hab_rx_queue_empty(vchan)) {
@@ -75,7 +78,7 @@ hab_msg_dequeue(struct virtual_channel *vchan, struct hab_message **msg,
	 * and need empty check again in case the list is empty now due to
	 * dequeue by other threads
	 */
	spin_lock_bh(&vchan->rx_lock);
	hab_spin_lock(&vchan->rx_lock, irqs_disabled);

	if ((!ret || (ret == -ERESTARTSYS)) && !list_empty(&vchan->rx_list)) {
		message = list_first_entry(&vchan->rx_list,
@@ -99,7 +102,7 @@ hab_msg_dequeue(struct virtual_channel *vchan, struct hab_message **msg,
		/* no message received, retain the original status */
		*rsize = 0;

	spin_unlock_bh(&vchan->rx_lock);
	hab_spin_unlock(&vchan->rx_lock, irqs_disabled);

	*msg = message;
	return ret;
@@ -108,9 +111,11 @@ hab_msg_dequeue(struct virtual_channel *vchan, struct hab_message **msg,
static void hab_msg_queue(struct virtual_channel *vchan,
					struct hab_message *message)
{
	spin_lock_bh(&vchan->rx_lock);
	int irqs_disabled = irqs_disabled();

	hab_spin_lock(&vchan->rx_lock, irqs_disabled);
	list_add_tail(&message->node, &vchan->rx_list);
	spin_unlock_bh(&vchan->rx_lock);
	hab_spin_unlock(&vchan->rx_lock, irqs_disabled);

	wake_up(&vchan->rx_queue);
}
@@ -119,11 +124,12 @@ static int hab_export_enqueue(struct virtual_channel *vchan,
		struct export_desc *exp)
{
	struct uhab_context *ctx = vchan->ctx;
	int irqs_disabled = irqs_disabled();

	spin_lock_bh(&ctx->imp_lock);
	hab_spin_lock(&ctx->imp_lock, irqs_disabled);
	list_add_tail(&exp->node, &ctx->imp_whse);
	ctx->import_total++;
	spin_unlock_bh(&ctx->imp_lock);
	hab_spin_unlock(&ctx->imp_lock, irqs_disabled);

	return 0;
}
@@ -151,6 +157,7 @@ static int hab_receive_create_export_ack(struct physical_channel *pchan,
{
	struct hab_export_ack_recvd *ack_recvd =
		kzalloc(sizeof(*ack_recvd), GFP_ATOMIC);
	int irqs_disabled = irqs_disabled();

	if (!ack_recvd)
		return -ENOMEM;
@@ -170,9 +177,9 @@ static int hab_receive_create_export_ack(struct physical_channel *pchan,
		sizebytes) != sizebytes)
		return -EIO;

	spin_lock_bh(&ctx->expq_lock);
	hab_spin_lock(&ctx->expq_lock, irqs_disabled);
	list_add_tail(&ack_recvd->node, &ctx->exp_rxq);
	spin_unlock_bh(&ctx->expq_lock);
	hab_spin_unlock(&ctx->expq_lock, irqs_disabled);

	return 0;
}
+10 −7
Original line number Diff line number Diff line
/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
/* Copyright (c) 2016-2019, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -46,6 +46,7 @@ int hab_open_request_add(struct physical_channel *pchan,
	struct hab_device *dev = pchan->habdev;
	struct hab_open_request *request;
	struct timeval tv;
	int irqs_disabled = irqs_disabled();

	if (sizebytes > HAB_HEADER_SIZE_MASK) {
		pr_err("pchan %s request size too large %zd\n",
@@ -70,10 +71,11 @@ int hab_open_request_add(struct physical_channel *pchan,
		tv.tv_usec/1000000;
	hab_pchan_get(pchan);

	spin_lock_bh(&dev->openlock);
	hab_spin_lock(&dev->openlock, irqs_disabled);
	list_add_tail(&node->node, &dev->openq_list);
	dev->openq_cnt++;
	spin_unlock_bh(&dev->openlock);
	hab_spin_unlock(&dev->openlock, irqs_disabled);

	return 0;
}

@@ -192,6 +194,7 @@ int hab_open_receive_cancel(struct physical_channel *pchan,
	struct hab_open_node *node, *tmp;
	int bfound = 0;
	struct timeval tv;
	int irqs_disabled = irqs_disabled();

	if (sizebytes > HAB_HEADER_SIZE_MASK) {
		pr_err("pchan %s cancel size too large %zd\n",
@@ -202,7 +205,7 @@ int hab_open_receive_cancel(struct physical_channel *pchan,
	if (physical_channel_read(pchan, &data, sizebytes) != sizebytes)
		return -EIO;

	spin_lock_bh(&dev->openlock);
	hab_spin_lock(&dev->openlock, irqs_disabled);
	list_for_each_entry_safe(node, tmp, &dev->openq_list, node) {
		request = &node->request;
		/* check if open request has been serviced or not */
@@ -221,7 +224,7 @@ int hab_open_receive_cancel(struct physical_channel *pchan,
			break;
		}
	}
	spin_unlock_bh(&dev->openlock);
	hab_spin_unlock(&dev->openlock, irqs_disabled);

	if (!bfound) {
		pr_info("init waiting is in-flight. vcid %x sub %d open %d\n",
@@ -244,10 +247,10 @@ int hab_open_receive_cancel(struct physical_channel *pchan,
		/* put when this node is handled in open path */
		hab_pchan_get(pchan);

		spin_lock_bh(&dev->openlock);
		hab_spin_lock(&dev->openlock, irqs_disabled);
		list_add_tail(&node->node, &dev->openq_list);
		dev->openq_cnt++;
		spin_unlock_bh(&dev->openlock);
		hab_spin_unlock(&dev->openlock, irqs_disabled);

		wake_up_interruptible(&dev->openq);
	}
+11 −9
Original line number Diff line number Diff line
/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
/* Copyright (c) 2016-2019, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -77,16 +77,17 @@ hab_vchan_free(struct kref *ref)
	struct physical_channel *pchan = vchan->pchan;
	struct uhab_context *ctx = vchan->ctx;
	struct virtual_channel *vc, *vc_tmp;
	int irqs_disabled = irqs_disabled();

	spin_lock_bh(&vchan->rx_lock);
	hab_spin_lock(&vchan->rx_lock, irqs_disabled);
	list_for_each_entry_safe(message, msg_tmp, &vchan->rx_list, node) {
		list_del(&message->node);
		hab_msg_free(message);
	}
	spin_unlock_bh(&vchan->rx_lock);
	hab_spin_unlock(&vchan->rx_lock, irqs_disabled);

	/* release vchan from pchan. no more msg for this vchan */
	write_lock_bh(&pchan->vchans_lock);
	hab_write_lock(&pchan->vchans_lock, irqs_disabled);
	list_for_each_entry_safe(vc, vc_tmp, &pchan->vchannels, pnode) {
		if (vchan == vc) {
			list_del(&vc->pnode);
@@ -95,15 +96,15 @@ hab_vchan_free(struct kref *ref)
			break;
		}
	}
	write_unlock_bh(&pchan->vchans_lock);
	hab_write_unlock(&pchan->vchans_lock, irqs_disabled);

	/* the release vchan from ctx was done earlier in vchan close() */
	hab_ctx_put(ctx); /* now ctx is not needed from this vchan's view */

	/* release idr at the last so same idr will not be used early */
	spin_lock_bh(&pchan->vid_lock);
	hab_spin_lock(&pchan->vid_lock, irqs_disabled);
	idr_remove(&pchan->vchan_idr, HAB_VCID_GET_ID(vchan->id));
	spin_unlock_bh(&pchan->vid_lock);
	hab_spin_unlock(&pchan->vid_lock, irqs_disabled);

	hab_pchan_put(pchan); /* no more need for pchan from this vchan */

@@ -122,8 +123,9 @@ hab_vchan_get(struct physical_channel *pchan, struct hab_header *header)
	uint32_t session_id = HAB_HEADER_GET_SESSION_ID(*header);
	size_t sizebytes = HAB_HEADER_GET_SIZE(*header);
	uint32_t payload_type = HAB_HEADER_GET_TYPE(*header);
	int irqs_disabled = irqs_disabled();

	spin_lock_bh(&pchan->vid_lock);
	hab_spin_lock(&pchan->vid_lock, irqs_disabled);
	vchan = idr_find(&pchan->vchan_idr, HAB_VCID_GET_ID(vchan_id));
	if (vchan) {
		if (vchan->session_id != session_id)
@@ -162,7 +164,7 @@ hab_vchan_get(struct physical_channel *pchan, struct hab_header *header)
			vchan = NULL;
		}
	}
	spin_unlock_bh(&pchan->vid_lock);
	hab_spin_unlock(&pchan->vid_lock, irqs_disabled);

	return vchan;
}
Loading