Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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: Mikko Perttunen <mperttunen@nvidia.com>
Signed-off-by: Thierry Reding <treding@nvidia.com>

authored by

Mikko Perttunen and committed by
Thierry Reding
625d4ffb c24973ed

+166 -497
+4 -3
drivers/gpu/host1x/debug.c
··· 77 77 78 78 static void show_syncpts(struct host1x *m, struct output *o, bool show_all) 79 79 { 80 + unsigned long irqflags; 80 81 struct list_head *pos; 81 82 unsigned int i; 82 83 int err; ··· 93 92 u32 min = host1x_syncpt_load(m->syncpt + i); 94 93 unsigned int waiters = 0; 95 94 96 - spin_lock(&m->syncpt[i].intr.lock); 97 - list_for_each(pos, &m->syncpt[i].intr.wait_head) 95 + spin_lock_irqsave(&m->syncpt[i].fences.lock, irqflags); 96 + list_for_each(pos, &m->syncpt[i].fences.list) 98 97 waiters++; 99 - spin_unlock(&m->syncpt[i].intr.lock); 98 + spin_unlock_irqrestore(&m->syncpt[i].fences.lock, irqflags); 100 99 101 100 if (!kref_read(&m->syncpt[i].ref)) 102 101 continue;
+2 -2
drivers/gpu/host1x/dev.c
··· 516 516 return PTR_ERR(host->regs); 517 517 } 518 518 519 - syncpt_irq = platform_get_irq(pdev, 0); 519 + host->syncpt_irq = platform_get_irq(pdev, 0); 520 520 if (syncpt_irq < 0) 521 521 return syncpt_irq; 522 522 ··· 578 578 goto free_contexts; 579 579 } 580 580 581 - err = host1x_intr_init(host, syncpt_irq); 581 + err = host1x_intr_init(host); 582 582 if (err) { 583 583 dev_err(&pdev->dev, "failed to initialize interrupts\n"); 584 584 goto deinit_syncpt;
+4 -6
drivers/gpu/host1x/dev.h
··· 74 74 }; 75 75 76 76 struct host1x_intr_ops { 77 - int (*init_host_sync)(struct host1x *host, u32 cpm, 78 - void (*syncpt_thresh_work)(struct work_struct *work)); 77 + int (*init_host_sync)(struct host1x *host, u32 cpm); 79 78 void (*set_syncpt_threshold)( 80 79 struct host1x *host, unsigned int id, u32 thresh); 81 80 void (*enable_syncpt_intr)(struct host1x *host, unsigned int id); ··· 124 125 void __iomem *regs; 125 126 void __iomem *hv_regs; /* hypervisor region */ 126 127 void __iomem *common_regs; 128 + int syncpt_irq; 127 129 struct host1x_syncpt *syncpt; 128 130 struct host1x_syncpt_base *bases; 129 131 struct device *dev; ··· 138 138 dma_addr_t iova_end; 139 139 140 140 struct mutex intr_mutex; 141 - int intr_syncpt_irq; 142 141 143 142 const struct host1x_syncpt_ops *syncpt_op; 144 143 const struct host1x_intr_ops *intr_op; ··· 215 216 return host->syncpt_op->enable_protection(host); 216 217 } 217 218 218 - static inline int host1x_hw_intr_init_host_sync(struct host1x *host, u32 cpm, 219 - void (*syncpt_thresh_work)(struct work_struct *)) 219 + static inline int host1x_hw_intr_init_host_sync(struct host1x *host, u32 cpm) 220 220 { 221 - return host->intr_op->init_host_sync(host, cpm, syncpt_thresh_work); 221 + return host->intr_op->init_host_sync(host, cpm); 222 222 } 223 223 224 224 static inline void host1x_hw_intr_set_syncpt_threshold(struct host1x *host,
+33 -63
drivers/gpu/host1x/fence.c
··· 15 15 #include "intr.h" 16 16 #include "syncpt.h" 17 17 18 - static DEFINE_SPINLOCK(lock); 19 - 20 - struct host1x_syncpt_fence { 21 - struct dma_fence base; 22 - 23 - atomic_t signaling; 24 - 25 - struct host1x_syncpt *sp; 26 - u32 threshold; 27 - 28 - struct host1x_waitlist *waiter; 29 - void *waiter_ref; 30 - 31 - struct delayed_work timeout_work; 32 - }; 33 - 34 18 static const char *host1x_syncpt_fence_get_driver_name(struct dma_fence *f) 35 19 { 36 20 return "host1x"; ··· 33 49 static bool host1x_syncpt_fence_enable_signaling(struct dma_fence *f) 34 50 { 35 51 struct host1x_syncpt_fence *sf = to_host1x_fence(f); 36 - int err; 37 52 38 53 if (host1x_syncpt_is_expired(sf->sp, sf->threshold)) 39 54 return false; 40 55 56 + /* One reference for interrupt path, one for timeout path. */ 57 + dma_fence_get(f); 41 58 dma_fence_get(f); 42 59 43 60 /* ··· 46 61 * reference to any fences for which 'enable_signaling' has been 47 62 * called (and that have not been signalled). 48 63 * 49 - * We provide a userspace API to create arbitrary syncpoint fences, 50 - * so we cannot normally guarantee that all fences get signalled. 64 + * We cannot (for now) normally guarantee that all fences get signalled. 51 65 * As such, setup a timeout, so that long-lasting fences will get 52 66 * reaped eventually. 53 67 */ 54 68 schedule_delayed_work(&sf->timeout_work, msecs_to_jiffies(30000)); 55 69 56 - err = host1x_intr_add_action(sf->sp->host, sf->sp, sf->threshold, 57 - HOST1X_INTR_ACTION_SIGNAL_FENCE, f, 58 - sf->waiter, &sf->waiter_ref); 59 - if (err) { 60 - cancel_delayed_work_sync(&sf->timeout_work); 61 - dma_fence_put(f); 62 - return false; 63 - } 64 - 65 - /* intr framework takes ownership of waiter */ 66 - sf->waiter = NULL; 70 + host1x_intr_add_fence_locked(sf->sp->host, sf); 67 71 68 72 /* 69 73 * The fence may get signalled at any time after the above call, ··· 63 89 return true; 64 90 } 65 91 66 - static void host1x_syncpt_fence_release(struct dma_fence *f) 67 - { 68 - struct host1x_syncpt_fence *sf = to_host1x_fence(f); 69 - 70 - if (sf->waiter) 71 - kfree(sf->waiter); 72 - 73 - dma_fence_free(f); 74 - } 75 - 76 92 static const struct dma_fence_ops host1x_syncpt_fence_ops = { 77 93 .get_driver_name = host1x_syncpt_fence_get_driver_name, 78 94 .get_timeline_name = host1x_syncpt_fence_get_timeline_name, 79 95 .enable_signaling = host1x_syncpt_fence_enable_signaling, 80 - .release = host1x_syncpt_fence_release, 81 96 }; 82 97 83 98 void host1x_fence_signal(struct host1x_syncpt_fence *f) 84 99 { 85 - if (atomic_xchg(&f->signaling, 1)) 100 + if (atomic_xchg(&f->signaling, 1)) { 101 + /* 102 + * Already on timeout path, but we removed the fence before 103 + * timeout path could, so drop interrupt path reference. 104 + */ 105 + dma_fence_put(&f->base); 86 106 return; 107 + } 87 108 88 - /* 89 - * Cancel pending timeout work - if it races, it will 90 - * not get 'f->signaling' and return. 91 - */ 92 - cancel_delayed_work_sync(&f->timeout_work); 109 + if (cancel_delayed_work(&f->timeout_work)) { 110 + /* 111 + * We know that the timeout path will not be entered. 112 + * Safe to drop the timeout path's reference now. 113 + */ 114 + dma_fence_put(&f->base); 115 + } 93 116 94 - host1x_intr_put_ref(f->sp->host, f->sp->id, f->waiter_ref, false); 95 - 96 - dma_fence_signal(&f->base); 117 + dma_fence_signal_locked(&f->base); 97 118 dma_fence_put(&f->base); 98 119 } 99 120 ··· 98 129 struct host1x_syncpt_fence *f = 99 130 container_of(dwork, struct host1x_syncpt_fence, timeout_work); 100 131 101 - if (atomic_xchg(&f->signaling, 1)) 132 + if (atomic_xchg(&f->signaling, 1)) { 133 + /* Already on interrupt path, drop timeout path reference. */ 134 + dma_fence_put(&f->base); 102 135 return; 136 + } 103 137 104 - /* 105 - * Cancel pending timeout work - if it races, it will 106 - * not get 'f->signaling' and return. 107 - */ 108 - host1x_intr_put_ref(f->sp->host, f->sp->id, f->waiter_ref, true); 138 + if (host1x_intr_remove_fence(f->sp->host, f)) { 139 + /* 140 + * Managed to remove fence from queue, so it's safe to drop 141 + * the interrupt path's reference. 142 + */ 143 + dma_fence_put(&f->base); 144 + } 109 145 110 146 dma_fence_set_error(&f->base, -ETIMEDOUT); 111 147 dma_fence_signal(&f->base); 148 + 149 + /* Drop timeout path reference. */ 112 150 dma_fence_put(&f->base); 113 151 } 114 152 ··· 127 151 if (!fence) 128 152 return ERR_PTR(-ENOMEM); 129 153 130 - fence->waiter = kzalloc(sizeof(*fence->waiter), GFP_KERNEL); 131 - if (!fence->waiter) { 132 - kfree(fence); 133 - return ERR_PTR(-ENOMEM); 134 - } 135 - 136 154 fence->sp = sp; 137 155 fence->threshold = threshold; 138 156 139 - dma_fence_init(&fence->base, &host1x_syncpt_fence_ops, &lock, 157 + dma_fence_init(&fence->base, &host1x_syncpt_fence_ops, &sp->fences.lock, 140 158 dma_fence_context_alloc(1), 0); 141 159 142 160 INIT_DELAYED_WORK(&fence->timeout_work, do_fence_timeout);
+17 -1
drivers/gpu/host1x/fence.h
··· 6 6 #ifndef HOST1X_FENCE_H 7 7 #define HOST1X_FENCE_H 8 8 9 - struct host1x_syncpt_fence; 9 + struct host1x_syncpt_fence { 10 + struct dma_fence base; 11 + 12 + atomic_t signaling; 13 + 14 + struct host1x_syncpt *sp; 15 + u32 threshold; 16 + 17 + struct delayed_work timeout_work; 18 + 19 + struct list_head list; 20 + }; 21 + 22 + struct host1x_fence_list { 23 + spinlock_t lock; 24 + struct list_head list; 25 + }; 10 26 11 27 void host1x_fence_signal(struct host1x_syncpt_fence *fence); 12 28
+20 -54
drivers/gpu/host1x/hw/intr_hw.c
··· 13 13 #include "../intr.h" 14 14 #include "../dev.h" 15 15 16 - /* 17 - * Sync point threshold interrupt service function 18 - * Handles sync point threshold triggers, in interrupt context 19 - */ 20 - static void host1x_intr_syncpt_handle(struct host1x_syncpt *syncpt) 21 - { 22 - unsigned int id = syncpt->id; 23 - struct host1x *host = syncpt->host; 24 - 25 - host1x_sync_writel(host, BIT(id % 32), 26 - HOST1X_SYNC_SYNCPT_THRESH_INT_DISABLE(id / 32)); 27 - host1x_sync_writel(host, BIT(id % 32), 28 - HOST1X_SYNC_SYNCPT_THRESH_CPU0_INT_STATUS(id / 32)); 29 - 30 - schedule_work(&syncpt->intr.work); 31 - } 32 - 33 16 static irqreturn_t syncpt_thresh_isr(int irq, void *dev_id) 34 17 { 35 18 struct host1x *host = dev_id; ··· 22 39 for (i = 0; i < DIV_ROUND_UP(host->info->nb_pts, 32); i++) { 23 40 reg = host1x_sync_readl(host, 24 41 HOST1X_SYNC_SYNCPT_THRESH_CPU0_INT_STATUS(i)); 25 - for_each_set_bit(id, &reg, 32) { 26 - struct host1x_syncpt *syncpt = 27 - host->syncpt + (i * 32 + id); 28 - host1x_intr_syncpt_handle(syncpt); 29 - } 42 + 43 + host1x_sync_writel(host, reg, 44 + HOST1X_SYNC_SYNCPT_THRESH_INT_DISABLE(i)); 45 + host1x_sync_writel(host, reg, 46 + HOST1X_SYNC_SYNCPT_THRESH_CPU0_INT_STATUS(i)); 47 + 48 + for_each_set_bit(id, &reg, 32) 49 + host1x_intr_handle_interrupt(host, i * 32 + id); 30 50 } 31 51 32 52 return IRQ_HANDLED; 33 53 } 34 54 35 - static void _host1x_intr_disable_all_syncpt_intrs(struct host1x *host) 55 + static void host1x_intr_disable_all_syncpt_intrs(struct host1x *host) 36 56 { 37 57 unsigned int i; 38 58 ··· 76 90 } 77 91 78 92 static int 79 - _host1x_intr_init_host_sync(struct host1x *host, u32 cpm, 80 - void (*syncpt_thresh_work)(struct work_struct *)) 93 + host1x_intr_init_host_sync(struct host1x *host, u32 cpm) 81 94 { 82 - unsigned int i; 83 95 int err; 84 96 85 97 host1x_hw_intr_disable_all_syncpt_intrs(host); 86 98 87 - for (i = 0; i < host->info->nb_pts; i++) 88 - INIT_WORK(&host->syncpt[i].intr.work, syncpt_thresh_work); 89 - 90 - err = devm_request_irq(host->dev, host->intr_syncpt_irq, 99 + err = devm_request_irq(host->dev, host->syncpt_irq, 91 100 syncpt_thresh_isr, IRQF_SHARED, 92 101 "host1x_syncpt", host); 93 - if (err < 0) { 94 - WARN_ON(1); 102 + if (err < 0) 95 103 return err; 96 - } 97 104 98 105 intr_hw_init(host, cpm); 99 106 100 107 return 0; 101 108 } 102 109 103 - static void _host1x_intr_set_syncpt_threshold(struct host1x *host, 110 + static void host1x_intr_set_syncpt_threshold(struct host1x *host, 104 111 unsigned int id, 105 112 u32 thresh) 106 113 { 107 114 host1x_sync_writel(host, thresh, HOST1X_SYNC_SYNCPT_INT_THRESH(id)); 108 115 } 109 116 110 - static void _host1x_intr_enable_syncpt_intr(struct host1x *host, 117 + static void host1x_intr_enable_syncpt_intr(struct host1x *host, 111 118 unsigned int id) 112 119 { 113 120 host1x_sync_writel(host, BIT(id % 32), 114 121 HOST1X_SYNC_SYNCPT_THRESH_INT_ENABLE_CPU0(id / 32)); 115 122 } 116 123 117 - static void _host1x_intr_disable_syncpt_intr(struct host1x *host, 124 + static void host1x_intr_disable_syncpt_intr(struct host1x *host, 118 125 unsigned int id) 119 126 { 120 127 host1x_sync_writel(host, BIT(id % 32), ··· 116 137 HOST1X_SYNC_SYNCPT_THRESH_CPU0_INT_STATUS(id / 32)); 117 138 } 118 139 119 - static int _host1x_free_syncpt_irq(struct host1x *host) 120 - { 121 - unsigned int i; 122 - 123 - devm_free_irq(host->dev, host->intr_syncpt_irq, host); 124 - 125 - for (i = 0; i < host->info->nb_pts; i++) 126 - cancel_work_sync(&host->syncpt[i].intr.work); 127 - 128 - return 0; 129 - } 130 - 131 140 static const struct host1x_intr_ops host1x_intr_ops = { 132 - .init_host_sync = _host1x_intr_init_host_sync, 133 - .set_syncpt_threshold = _host1x_intr_set_syncpt_threshold, 134 - .enable_syncpt_intr = _host1x_intr_enable_syncpt_intr, 135 - .disable_syncpt_intr = _host1x_intr_disable_syncpt_intr, 136 - .disable_all_syncpt_intrs = _host1x_intr_disable_all_syncpt_intrs, 137 - .free_syncpt_irq = _host1x_free_syncpt_irq, 141 + .init_host_sync = host1x_intr_init_host_sync, 142 + .set_syncpt_threshold = host1x_intr_set_syncpt_threshold, 143 + .enable_syncpt_intr = host1x_intr_enable_syncpt_intr, 144 + .disable_syncpt_intr = host1x_intr_disable_syncpt_intr, 145 + .disable_all_syncpt_intrs = host1x_intr_disable_all_syncpt_intrs, 138 146 };
+75 -293
drivers/gpu/host1x/intr.c
··· 2 2 /* 3 3 * Tegra host1x Interrupt Management 4 4 * 5 - * Copyright (c) 2010-2013, NVIDIA Corporation. 5 + * Copyright (c) 2010-2021, NVIDIA Corporation. 6 6 */ 7 7 8 8 #include <linux/clk.h> 9 - #include <linux/interrupt.h> 10 - #include <linux/slab.h> 11 - #include <linux/irq.h> 12 9 13 - #include <trace/events/host1x.h> 14 - #include "channel.h" 15 10 #include "dev.h" 16 11 #include "fence.h" 17 12 #include "intr.h" 18 13 19 - /* Wait list management */ 20 - 21 - enum waitlist_state { 22 - WLS_PENDING, 23 - WLS_REMOVED, 24 - WLS_CANCELLED, 25 - WLS_HANDLED 26 - }; 27 - 28 - static void waiter_release(struct kref *kref) 14 + static void host1x_intr_add_fence_to_list(struct host1x_fence_list *list, 15 + struct host1x_syncpt_fence *fence) 29 16 { 30 - kfree(container_of(kref, struct host1x_waitlist, refcount)); 17 + struct host1x_syncpt_fence *fence_in_list; 18 + 19 + list_for_each_entry_reverse(fence_in_list, &list->list, list) { 20 + if ((s32)(fence_in_list->threshold - fence->threshold) <= 0) { 21 + /* Fence in list is before us, we can insert here */ 22 + list_add(&fence->list, &fence_in_list->list); 23 + return; 24 + } 25 + } 26 + 27 + /* Add as first in list */ 28 + list_add(&fence->list, &list->list); 31 29 } 32 30 33 - /* 34 - * add a waiter to a waiter queue, sorted by threshold 35 - * returns true if it was added at the head of the queue 36 - */ 37 - static bool add_waiter_to_queue(struct host1x_waitlist *waiter, 38 - struct list_head *queue) 31 + static void host1x_intr_update_hw_state(struct host1x *host, struct host1x_syncpt *sp) 39 32 { 40 - struct host1x_waitlist *pos; 41 - u32 thresh = waiter->thresh; 33 + struct host1x_syncpt_fence *fence; 42 34 43 - list_for_each_entry_reverse(pos, queue, list) 44 - if ((s32)(pos->thresh - thresh) <= 0) { 45 - list_add(&waiter->list, &pos->list); 46 - return false; 47 - } 35 + if (!list_empty(&sp->fences.list)) { 36 + fence = list_first_entry(&sp->fences.list, struct host1x_syncpt_fence, list); 48 37 49 - list_add(&waiter->list, queue); 38 + host1x_hw_intr_set_syncpt_threshold(host, sp->id, fence->threshold); 39 + host1x_hw_intr_enable_syncpt_intr(host, sp->id); 40 + } else { 41 + host1x_hw_intr_disable_syncpt_intr(host, sp->id); 42 + } 43 + } 44 + 45 + void host1x_intr_add_fence_locked(struct host1x *host, struct host1x_syncpt_fence *fence) 46 + { 47 + struct host1x_fence_list *fence_list = &fence->sp->fences; 48 + 49 + INIT_LIST_HEAD(&fence->list); 50 + 51 + host1x_intr_add_fence_to_list(fence_list, fence); 52 + host1x_intr_update_hw_state(host, fence->sp); 53 + } 54 + 55 + bool host1x_intr_remove_fence(struct host1x *host, struct host1x_syncpt_fence *fence) 56 + { 57 + struct host1x_fence_list *fence_list = &fence->sp->fences; 58 + unsigned long irqflags; 59 + 60 + spin_lock_irqsave(&fence_list->lock, irqflags); 61 + 62 + if (list_empty(&fence->list)) { 63 + spin_unlock_irqrestore(&fence_list->lock, irqflags); 64 + return false; 65 + } 66 + 67 + list_del_init(&fence->list); 68 + host1x_intr_update_hw_state(host, fence->sp); 69 + 70 + spin_unlock_irqrestore(&fence_list->lock, irqflags); 71 + 50 72 return true; 51 73 } 52 74 53 - /* 54 - * run through a waiter queue for a single sync point ID 55 - * and gather all completed waiters into lists by actions 56 - */ 57 - static void remove_completed_waiters(struct list_head *head, u32 sync, 58 - struct list_head completed[HOST1X_INTR_ACTION_COUNT]) 75 + void host1x_intr_handle_interrupt(struct host1x *host, unsigned int id) 59 76 { 60 - struct list_head *dest; 61 - struct host1x_waitlist *waiter, *next, *prev; 77 + struct host1x_syncpt *sp = &host->syncpt[id]; 78 + struct host1x_syncpt_fence *fence, *tmp; 79 + unsigned int value; 62 80 63 - list_for_each_entry_safe(waiter, next, head, list) { 64 - if ((s32)(waiter->thresh - sync) > 0) 81 + value = host1x_syncpt_load(sp); 82 + 83 + spin_lock(&sp->fences.lock); 84 + 85 + list_for_each_entry_safe(fence, tmp, &sp->fences.list, list) { 86 + if (((value - fence->threshold) & 0x80000000U) != 0U) { 87 + /* Fence is not yet expired, we are done */ 65 88 break; 66 - 67 - dest = completed + waiter->action; 68 - 69 - /* consolidate submit cleanups */ 70 - if (waiter->action == HOST1X_INTR_ACTION_SUBMIT_COMPLETE && 71 - !list_empty(dest)) { 72 - prev = list_entry(dest->prev, 73 - struct host1x_waitlist, list); 74 - if (prev->data == waiter->data) { 75 - prev->count++; 76 - dest = NULL; 77 - } 78 89 } 79 90 80 - /* PENDING->REMOVED or CANCELLED->HANDLED */ 81 - if (atomic_inc_return(&waiter->state) == WLS_HANDLED || !dest) { 82 - list_del(&waiter->list); 83 - kref_put(&waiter->refcount, waiter_release); 84 - } else 85 - list_move_tail(&waiter->list, dest); 86 - } 87 - } 88 - 89 - static void reset_threshold_interrupt(struct host1x *host, 90 - struct list_head *head, 91 - unsigned int id) 92 - { 93 - u32 thresh = 94 - list_first_entry(head, struct host1x_waitlist, list)->thresh; 95 - 96 - host1x_hw_intr_set_syncpt_threshold(host, id, thresh); 97 - host1x_hw_intr_enable_syncpt_intr(host, id); 98 - } 99 - 100 - static void action_submit_complete(struct host1x_waitlist *waiter) 101 - { 102 - struct host1x_channel *channel = waiter->data; 103 - 104 - host1x_cdma_update(&channel->cdma); 105 - 106 - /* Add nr_completed to trace */ 107 - trace_host1x_channel_submit_complete(dev_name(channel->dev), 108 - waiter->count, waiter->thresh); 109 - } 110 - 111 - static void action_wakeup(struct host1x_waitlist *waiter) 112 - { 113 - wait_queue_head_t *wq = waiter->data; 114 - 115 - wake_up(wq); 116 - } 117 - 118 - static void action_wakeup_interruptible(struct host1x_waitlist *waiter) 119 - { 120 - wait_queue_head_t *wq = waiter->data; 121 - 122 - wake_up_interruptible(wq); 123 - } 124 - 125 - static void action_signal_fence(struct host1x_waitlist *waiter) 126 - { 127 - struct host1x_syncpt_fence *f = waiter->data; 128 - 129 - host1x_fence_signal(f); 130 - } 131 - 132 - typedef void (*action_handler)(struct host1x_waitlist *waiter); 133 - 134 - static const action_handler action_handlers[HOST1X_INTR_ACTION_COUNT] = { 135 - action_submit_complete, 136 - action_wakeup, 137 - action_wakeup_interruptible, 138 - action_signal_fence, 139 - }; 140 - 141 - static void run_handlers(struct list_head completed[HOST1X_INTR_ACTION_COUNT]) 142 - { 143 - struct list_head *head = completed; 144 - unsigned int i; 145 - 146 - for (i = 0; i < HOST1X_INTR_ACTION_COUNT; ++i, ++head) { 147 - action_handler handler = action_handlers[i]; 148 - struct host1x_waitlist *waiter, *next; 149 - 150 - list_for_each_entry_safe(waiter, next, head, list) { 151 - list_del(&waiter->list); 152 - handler(waiter); 153 - WARN_ON(atomic_xchg(&waiter->state, WLS_HANDLED) != 154 - WLS_REMOVED); 155 - kref_put(&waiter->refcount, waiter_release); 156 - } 157 - } 158 - } 159 - 160 - /* 161 - * Remove & handle all waiters that have completed for the given syncpt 162 - */ 163 - static int process_wait_list(struct host1x *host, 164 - struct host1x_syncpt *syncpt, 165 - u32 threshold) 166 - { 167 - struct list_head completed[HOST1X_INTR_ACTION_COUNT]; 168 - unsigned int i; 169 - int empty; 170 - 171 - for (i = 0; i < HOST1X_INTR_ACTION_COUNT; ++i) 172 - INIT_LIST_HEAD(completed + i); 173 - 174 - spin_lock(&syncpt->intr.lock); 175 - 176 - remove_completed_waiters(&syncpt->intr.wait_head, threshold, 177 - completed); 178 - 179 - empty = list_empty(&syncpt->intr.wait_head); 180 - if (empty) 181 - host1x_hw_intr_disable_syncpt_intr(host, syncpt->id); 182 - else 183 - reset_threshold_interrupt(host, &syncpt->intr.wait_head, 184 - syncpt->id); 185 - 186 - spin_unlock(&syncpt->intr.lock); 187 - 188 - run_handlers(completed); 189 - 190 - return empty; 191 - } 192 - 193 - /* 194 - * Sync point threshold interrupt service thread function 195 - * Handles sync point threshold triggers, in thread context 196 - */ 197 - 198 - static void syncpt_thresh_work(struct work_struct *work) 199 - { 200 - struct host1x_syncpt_intr *syncpt_intr = 201 - container_of(work, struct host1x_syncpt_intr, work); 202 - struct host1x_syncpt *syncpt = 203 - container_of(syncpt_intr, struct host1x_syncpt, intr); 204 - unsigned int id = syncpt->id; 205 - struct host1x *host = syncpt->host; 206 - 207 - (void)process_wait_list(host, syncpt, 208 - host1x_syncpt_load(host->syncpt + id)); 209 - } 210 - 211 - int host1x_intr_add_action(struct host1x *host, struct host1x_syncpt *syncpt, 212 - u32 thresh, enum host1x_intr_action action, 213 - void *data, struct host1x_waitlist *waiter, 214 - void **ref) 215 - { 216 - int queue_was_empty; 217 - 218 - if (waiter == NULL) { 219 - pr_warn("%s: NULL waiter\n", __func__); 220 - return -EINVAL; 91 + list_del_init(&fence->list); 92 + host1x_fence_signal(fence); 221 93 } 222 94 223 - /* initialize a new waiter */ 224 - INIT_LIST_HEAD(&waiter->list); 225 - kref_init(&waiter->refcount); 226 - if (ref) 227 - kref_get(&waiter->refcount); 228 - waiter->thresh = thresh; 229 - waiter->action = action; 230 - atomic_set(&waiter->state, WLS_PENDING); 231 - waiter->data = data; 232 - waiter->count = 1; 95 + /* Re-enable interrupt if necessary */ 96 + host1x_intr_update_hw_state(host, sp); 233 97 234 - spin_lock(&syncpt->intr.lock); 235 - 236 - queue_was_empty = list_empty(&syncpt->intr.wait_head); 237 - 238 - if (add_waiter_to_queue(waiter, &syncpt->intr.wait_head)) { 239 - /* added at head of list - new threshold value */ 240 - host1x_hw_intr_set_syncpt_threshold(host, syncpt->id, thresh); 241 - 242 - /* added as first waiter - enable interrupt */ 243 - if (queue_was_empty) 244 - host1x_hw_intr_enable_syncpt_intr(host, syncpt->id); 245 - } 246 - 247 - if (ref) 248 - *ref = waiter; 249 - 250 - spin_unlock(&syncpt->intr.lock); 251 - 252 - return 0; 98 + spin_unlock(&sp->fences.lock); 253 99 } 254 100 255 - void host1x_intr_put_ref(struct host1x *host, unsigned int id, void *ref, 256 - bool flush) 257 - { 258 - struct host1x_waitlist *waiter = ref; 259 - struct host1x_syncpt *syncpt; 260 - 261 - atomic_cmpxchg(&waiter->state, WLS_PENDING, WLS_CANCELLED); 262 - 263 - syncpt = host->syncpt + id; 264 - 265 - spin_lock(&syncpt->intr.lock); 266 - if (atomic_cmpxchg(&waiter->state, WLS_CANCELLED, WLS_HANDLED) == 267 - WLS_CANCELLED) { 268 - list_del(&waiter->list); 269 - kref_put(&waiter->refcount, waiter_release); 270 - } 271 - spin_unlock(&syncpt->intr.lock); 272 - 273 - if (flush) { 274 - /* Wait until any concurrently executing handler has finished. */ 275 - while (atomic_read(&waiter->state) != WLS_HANDLED) 276 - schedule(); 277 - } 278 - 279 - kref_put(&waiter->refcount, waiter_release); 280 - } 281 - 282 - int host1x_intr_init(struct host1x *host, unsigned int irq_sync) 101 + int host1x_intr_init(struct host1x *host) 283 102 { 284 103 unsigned int id; 285 - u32 nb_pts = host1x_syncpt_nb_pts(host); 286 104 287 105 mutex_init(&host->intr_mutex); 288 - host->intr_syncpt_irq = irq_sync; 289 106 290 - for (id = 0; id < nb_pts; ++id) { 291 - struct host1x_syncpt *syncpt = host->syncpt + id; 107 + for (id = 0; id < host1x_syncpt_nb_pts(host); ++id) { 108 + struct host1x_syncpt *syncpt = &host->syncpt[id]; 292 109 293 - spin_lock_init(&syncpt->intr.lock); 294 - INIT_LIST_HEAD(&syncpt->intr.wait_head); 295 - snprintf(syncpt->intr.thresh_irq_name, 296 - sizeof(syncpt->intr.thresh_irq_name), 297 - "host1x_sp_%02u", id); 110 + spin_lock_init(&syncpt->fences.lock); 111 + INIT_LIST_HEAD(&syncpt->fences.list); 298 112 } 299 113 300 114 return 0; ··· 124 310 int err; 125 311 126 312 mutex_lock(&host->intr_mutex); 127 - err = host1x_hw_intr_init_host_sync(host, DIV_ROUND_UP(hz, 1000000), 128 - syncpt_thresh_work); 313 + err = host1x_hw_intr_init_host_sync(host, DIV_ROUND_UP(hz, 1000000)); 129 314 if (err) { 130 315 mutex_unlock(&host->intr_mutex); 131 316 return; ··· 134 321 135 322 void host1x_intr_stop(struct host1x *host) 136 323 { 137 - unsigned int id; 138 - struct host1x_syncpt *syncpt = host->syncpt; 139 - u32 nb_pts = host1x_syncpt_nb_pts(host); 140 - 141 - mutex_lock(&host->intr_mutex); 142 - 143 324 host1x_hw_intr_disable_all_syncpt_intrs(host); 144 - 145 - for (id = 0; id < nb_pts; ++id) { 146 - struct host1x_waitlist *waiter, *next; 147 - 148 - list_for_each_entry_safe(waiter, next, 149 - &syncpt[id].intr.wait_head, list) { 150 - if (atomic_cmpxchg(&waiter->state, 151 - WLS_CANCELLED, WLS_HANDLED) == WLS_CANCELLED) { 152 - list_del(&waiter->list); 153 - kref_put(&waiter->refcount, waiter_release); 154 - } 155 - } 156 - 157 - if (!list_empty(&syncpt[id].intr.wait_head)) { 158 - /* output diagnostics */ 159 - mutex_unlock(&host->intr_mutex); 160 - pr_warn("%s cannot stop syncpt intr id=%u\n", 161 - __func__, id); 162 - return; 163 - } 164 - } 165 - 166 - host1x_hw_intr_free_syncpt_irq(host); 167 - 168 - mutex_unlock(&host->intr_mutex); 169 325 }
+9 -74
drivers/gpu/host1x/intr.h
··· 2 2 /* 3 3 * Tegra host1x Interrupt Management 4 4 * 5 - * Copyright (c) 2010-2013, NVIDIA Corporation. 5 + * Copyright (c) 2010-2021, NVIDIA Corporation. 6 6 */ 7 7 8 8 #ifndef __HOST1X_INTR_H 9 9 #define __HOST1X_INTR_H 10 10 11 - #include <linux/interrupt.h> 12 - #include <linux/workqueue.h> 13 - 14 - struct host1x_syncpt; 15 11 struct host1x; 16 - 17 - enum host1x_intr_action { 18 - /* 19 - * Perform cleanup after a submit has completed. 20 - * 'data' points to a channel 21 - */ 22 - HOST1X_INTR_ACTION_SUBMIT_COMPLETE = 0, 23 - 24 - /* 25 - * Wake up a task. 26 - * 'data' points to a wait_queue_head_t 27 - */ 28 - HOST1X_INTR_ACTION_WAKEUP, 29 - 30 - /* 31 - * Wake up a interruptible task. 32 - * 'data' points to a wait_queue_head_t 33 - */ 34 - HOST1X_INTR_ACTION_WAKEUP_INTERRUPTIBLE, 35 - 36 - HOST1X_INTR_ACTION_SIGNAL_FENCE, 37 - 38 - HOST1X_INTR_ACTION_COUNT 39 - }; 40 - 41 - struct host1x_syncpt_intr { 42 - spinlock_t lock; 43 - struct list_head wait_head; 44 - char thresh_irq_name[12]; 45 - struct work_struct work; 46 - }; 47 - 48 - struct host1x_waitlist { 49 - struct list_head list; 50 - struct kref refcount; 51 - u32 thresh; 52 - enum host1x_intr_action action; 53 - atomic_t state; 54 - void *data; 55 - int count; 56 - }; 57 - 58 - /* 59 - * Schedule an action to be taken when a sync point reaches the given threshold. 60 - * 61 - * @id the sync point 62 - * @thresh the threshold 63 - * @action the action to take 64 - * @data a pointer to extra data depending on action, see above 65 - * @waiter waiter structure - assumes ownership 66 - * @ref must be passed if cancellation is possible, else NULL 67 - * 68 - * This is a non-blocking api. 69 - */ 70 - int host1x_intr_add_action(struct host1x *host, struct host1x_syncpt *syncpt, 71 - u32 thresh, enum host1x_intr_action action, 72 - void *data, struct host1x_waitlist *waiter, 73 - void **ref); 74 - 75 - /* 76 - * Unreference an action submitted to host1x_intr_add_action(). 77 - * You must call this if you passed non-NULL as ref. 78 - * @ref the ref returned from host1x_intr_add_action() 79 - * @flush wait until any pending handlers have completed before returning. 80 - */ 81 - void host1x_intr_put_ref(struct host1x *host, unsigned int id, void *ref, 82 - bool flush); 12 + struct host1x_syncpt_fence; 83 13 84 14 /* Initialize host1x sync point interrupt */ 85 - int host1x_intr_init(struct host1x *host, unsigned int irq_sync); 15 + int host1x_intr_init(struct host1x *host); 86 16 87 17 /* Deinitialize host1x sync point interrupt */ 88 18 void host1x_intr_deinit(struct host1x *host); ··· 23 93 /* Disable host1x sync point interrupt */ 24 94 void host1x_intr_stop(struct host1x *host); 25 95 26 - irqreturn_t host1x_syncpt_thresh_fn(void *dev_id); 96 + void host1x_intr_handle_interrupt(struct host1x *host, unsigned int id); 97 + 98 + void host1x_intr_add_fence_locked(struct host1x *host, struct host1x_syncpt_fence *fence); 99 + 100 + bool host1x_intr_remove_fence(struct host1x *host, struct host1x_syncpt_fence *fence); 101 + 27 102 #endif
+2 -1
drivers/gpu/host1x/syncpt.h
··· 14 14 #include <linux/kref.h> 15 15 #include <linux/sched.h> 16 16 17 + #include "fence.h" 17 18 #include "intr.h" 18 19 19 20 struct host1x; ··· 40 39 struct host1x_syncpt_base *base; 41 40 42 41 /* interrupt data */ 43 - struct host1x_syncpt_intr intr; 42 + struct host1x_fence_list fences; 44 43 45 44 /* 46 45 * If a submission incrementing this syncpoint fails, lock it so that