Commit 625d4ffb authored by Mikko Perttunen's avatar Mikko Perttunen Committed by Thierry Reding
Browse files

gpu: host1x: Rewrite syncpoint interrupt handling



Move from the old, complex intr handling code to a new implementation
based on dma_fences. While there is a fair bit of churn to get there,
the new implementation is much simpler and likely faster as well due
to allowing signaling directly from interrupt context.

Signed-off-by: default avatarMikko Perttunen <mperttunen@nvidia.com>
Signed-off-by: default avatarThierry Reding <treding@nvidia.com>
parent c24973ed
Loading
Loading
Loading
Loading
+4 −3
Original line number Diff line number Diff line
@@ -77,6 +77,7 @@ static int show_channel(struct host1x_channel *ch, void *data, bool show_fifo)

static void show_syncpts(struct host1x *m, struct output *o, bool show_all)
{
	unsigned long irqflags;
	struct list_head *pos;
	unsigned int i;
	int err;
@@ -92,10 +93,10 @@ static void show_syncpts(struct host1x *m, struct output *o, bool show_all)
		u32 min = host1x_syncpt_load(m->syncpt + i);
		unsigned int waiters = 0;

		spin_lock(&m->syncpt[i].intr.lock);
		list_for_each(pos, &m->syncpt[i].intr.wait_head)
		spin_lock_irqsave(&m->syncpt[i].fences.lock, irqflags);
		list_for_each(pos, &m->syncpt[i].fences.list)
			waiters++;
		spin_unlock(&m->syncpt[i].intr.lock);
		spin_unlock_irqrestore(&m->syncpt[i].fences.lock, irqflags);

		if (!kref_read(&m->syncpt[i].ref))
			continue;
+2 −2
Original line number Diff line number Diff line
@@ -516,7 +516,7 @@ static int host1x_probe(struct platform_device *pdev)
			return PTR_ERR(host->regs);
	}

	syncpt_irq = platform_get_irq(pdev, 0);
	host->syncpt_irq = platform_get_irq(pdev, 0);
	if (syncpt_irq < 0)
		return syncpt_irq;

@@ -578,7 +578,7 @@ static int host1x_probe(struct platform_device *pdev)
		goto free_contexts;
	}

	err = host1x_intr_init(host, syncpt_irq);
	err = host1x_intr_init(host);
	if (err) {
		dev_err(&pdev->dev, "failed to initialize interrupts\n");
		goto deinit_syncpt;
+4 −6
Original line number Diff line number Diff line
@@ -74,8 +74,7 @@ struct host1x_syncpt_ops {
};

struct host1x_intr_ops {
	int (*init_host_sync)(struct host1x *host, u32 cpm,
		void (*syncpt_thresh_work)(struct work_struct *work));
	int (*init_host_sync)(struct host1x *host, u32 cpm);
	void (*set_syncpt_threshold)(
		struct host1x *host, unsigned int id, u32 thresh);
	void (*enable_syncpt_intr)(struct host1x *host, unsigned int id);
@@ -125,6 +124,7 @@ struct host1x {
	void __iomem *regs;
	void __iomem *hv_regs; /* hypervisor region */
	void __iomem *common_regs;
	int syncpt_irq;
	struct host1x_syncpt *syncpt;
	struct host1x_syncpt_base *bases;
	struct device *dev;
@@ -138,7 +138,6 @@ struct host1x {
	dma_addr_t iova_end;

	struct mutex intr_mutex;
	int intr_syncpt_irq;

	const struct host1x_syncpt_ops *syncpt_op;
	const struct host1x_intr_ops *intr_op;
@@ -216,10 +215,9 @@ static inline void host1x_hw_syncpt_enable_protection(struct host1x *host)
	return host->syncpt_op->enable_protection(host);
}

static inline int host1x_hw_intr_init_host_sync(struct host1x *host, u32 cpm,
			void (*syncpt_thresh_work)(struct work_struct *))
static inline int host1x_hw_intr_init_host_sync(struct host1x *host, u32 cpm)
{
	return host->intr_op->init_host_sync(host, cpm, syncpt_thresh_work);
	return host->intr_op->init_host_sync(host, cpm);
}

static inline void host1x_hw_intr_set_syncpt_threshold(struct host1x *host,
+33 −63
Original line number Diff line number Diff line
@@ -15,22 +15,6 @@
#include "intr.h"
#include "syncpt.h"

static DEFINE_SPINLOCK(lock);

struct host1x_syncpt_fence {
	struct dma_fence base;

	atomic_t signaling;

	struct host1x_syncpt *sp;
	u32 threshold;

	struct host1x_waitlist *waiter;
	void *waiter_ref;

	struct delayed_work timeout_work;
};

static const char *host1x_syncpt_fence_get_driver_name(struct dma_fence *f)
{
	return "host1x";
@@ -49,11 +33,12 @@ static struct host1x_syncpt_fence *to_host1x_fence(struct dma_fence *f)
static bool host1x_syncpt_fence_enable_signaling(struct dma_fence *f)
{
	struct host1x_syncpt_fence *sf = to_host1x_fence(f);
	int err;

	if (host1x_syncpt_is_expired(sf->sp, sf->threshold))
		return false;

	/* One reference for interrupt path, one for timeout path. */
	dma_fence_get(f);
	dma_fence_get(f);

	/*
@@ -61,24 +46,13 @@ static bool host1x_syncpt_fence_enable_signaling(struct dma_fence *f)
	 * reference to any fences for which 'enable_signaling' has been
	 * called (and that have not been signalled).
	 *
	 * We provide a userspace API to create arbitrary syncpoint fences,
	 * so we cannot normally guarantee that all fences get signalled.
	 * We cannot (for now) normally guarantee that all fences get signalled.
	 * As such, setup a timeout, so that long-lasting fences will get
	 * reaped eventually.
	 */
	schedule_delayed_work(&sf->timeout_work, msecs_to_jiffies(30000));

	err = host1x_intr_add_action(sf->sp->host, sf->sp, sf->threshold,
				     HOST1X_INTR_ACTION_SIGNAL_FENCE, f,
				     sf->waiter, &sf->waiter_ref);
	if (err) {
		cancel_delayed_work_sync(&sf->timeout_work);
		dma_fence_put(f);
		return false;
	}

	/* intr framework takes ownership of waiter */
	sf->waiter = NULL;
	host1x_intr_add_fence_locked(sf->sp->host, sf);

	/*
	 * The fence may get signalled at any time after the above call,
@@ -89,37 +63,32 @@ static bool host1x_syncpt_fence_enable_signaling(struct dma_fence *f)
	return true;
}

static void host1x_syncpt_fence_release(struct dma_fence *f)
{
	struct host1x_syncpt_fence *sf = to_host1x_fence(f);

	if (sf->waiter)
		kfree(sf->waiter);

	dma_fence_free(f);
}

static const struct dma_fence_ops host1x_syncpt_fence_ops = {
	.get_driver_name = host1x_syncpt_fence_get_driver_name,
	.get_timeline_name = host1x_syncpt_fence_get_timeline_name,
	.enable_signaling = host1x_syncpt_fence_enable_signaling,
	.release = host1x_syncpt_fence_release,
};

void host1x_fence_signal(struct host1x_syncpt_fence *f)
{
	if (atomic_xchg(&f->signaling, 1))
	if (atomic_xchg(&f->signaling, 1)) {
		/*
		 * Already on timeout path, but we removed the fence before
		 * timeout path could, so drop interrupt path reference.
		 */
		dma_fence_put(&f->base);
		return;
	}

	if (cancel_delayed_work(&f->timeout_work)) {
		/*
	 * Cancel pending timeout work - if it races, it will
	 * not get 'f->signaling' and return.
		 * We know that the timeout path will not be entered.
		 * Safe to drop the timeout path's reference now.
		 */
	cancel_delayed_work_sync(&f->timeout_work);

	host1x_intr_put_ref(f->sp->host, f->sp->id, f->waiter_ref, false);
		dma_fence_put(&f->base);
	}

	dma_fence_signal(&f->base);
	dma_fence_signal_locked(&f->base);
	dma_fence_put(&f->base);
}

@@ -129,17 +98,24 @@ static void do_fence_timeout(struct work_struct *work)
	struct host1x_syncpt_fence *f =
		container_of(dwork, struct host1x_syncpt_fence, timeout_work);

	if (atomic_xchg(&f->signaling, 1))
	if (atomic_xchg(&f->signaling, 1)) {
		/* Already on interrupt path, drop timeout path reference. */
		dma_fence_put(&f->base);
		return;
	}

	if (host1x_intr_remove_fence(f->sp->host, f)) {
		/*
	 * Cancel pending timeout work - if it races, it will
	 * not get 'f->signaling' and return.
		 * Managed to remove fence from queue, so it's safe to drop
		 * the interrupt path's reference.
		 */
	host1x_intr_put_ref(f->sp->host, f->sp->id, f->waiter_ref, true);
		dma_fence_put(&f->base);
	}

	dma_fence_set_error(&f->base, -ETIMEDOUT);
	dma_fence_signal(&f->base);

	/* Drop timeout path reference. */
	dma_fence_put(&f->base);
}

@@ -151,16 +127,10 @@ struct dma_fence *host1x_fence_create(struct host1x_syncpt *sp, u32 threshold)
	if (!fence)
		return ERR_PTR(-ENOMEM);

	fence->waiter = kzalloc(sizeof(*fence->waiter), GFP_KERNEL);
	if (!fence->waiter) {
		kfree(fence);
		return ERR_PTR(-ENOMEM);
	}

	fence->sp = sp;
	fence->threshold = threshold;

	dma_fence_init(&fence->base, &host1x_syncpt_fence_ops, &lock,
	dma_fence_init(&fence->base, &host1x_syncpt_fence_ops, &sp->fences.lock,
		       dma_fence_context_alloc(1), 0);

	INIT_DELAYED_WORK(&fence->timeout_work, do_fence_timeout);
+17 −1
Original line number Diff line number Diff line
@@ -6,7 +6,23 @@
#ifndef HOST1X_FENCE_H
#define HOST1X_FENCE_H

struct host1x_syncpt_fence;
struct host1x_syncpt_fence {
	struct dma_fence base;

	atomic_t signaling;

	struct host1x_syncpt *sp;
	u32 threshold;

	struct delayed_work timeout_work;

	struct list_head list;
};

struct host1x_fence_list {
	spinlock_t lock;
	struct list_head list;
};

void host1x_fence_signal(struct host1x_syncpt_fence *fence);

Loading