0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024 #include <core/notify.h>
0025 #include <core/event.h>
0026
0027 static inline void
0028 nvkm_notify_put_locked(struct nvkm_notify *notify)
0029 {
0030 if (notify->block++ == 0)
0031 nvkm_event_put(notify->event, notify->types, notify->index);
0032 }
0033
0034 void
0035 nvkm_notify_put(struct nvkm_notify *notify)
0036 {
0037 struct nvkm_event *event = notify->event;
0038 unsigned long flags;
0039 if (likely(event) &&
0040 test_and_clear_bit(NVKM_NOTIFY_USER, ¬ify->flags)) {
0041 spin_lock_irqsave(&event->refs_lock, flags);
0042 nvkm_notify_put_locked(notify);
0043 spin_unlock_irqrestore(&event->refs_lock, flags);
0044 if (test_bit(NVKM_NOTIFY_WORK, ¬ify->flags))
0045 flush_work(¬ify->work);
0046 }
0047 }
0048
0049 static inline void
0050 nvkm_notify_get_locked(struct nvkm_notify *notify)
0051 {
0052 if (--notify->block == 0)
0053 nvkm_event_get(notify->event, notify->types, notify->index);
0054 }
0055
0056 void
0057 nvkm_notify_get(struct nvkm_notify *notify)
0058 {
0059 struct nvkm_event *event = notify->event;
0060 unsigned long flags;
0061 if (likely(event) &&
0062 !test_and_set_bit(NVKM_NOTIFY_USER, ¬ify->flags)) {
0063 spin_lock_irqsave(&event->refs_lock, flags);
0064 nvkm_notify_get_locked(notify);
0065 spin_unlock_irqrestore(&event->refs_lock, flags);
0066 }
0067 }
0068
0069 static inline void
0070 nvkm_notify_func(struct nvkm_notify *notify)
0071 {
0072 struct nvkm_event *event = notify->event;
0073 int ret = notify->func(notify);
0074 unsigned long flags;
0075 if ((ret == NVKM_NOTIFY_KEEP) ||
0076 !test_and_clear_bit(NVKM_NOTIFY_USER, ¬ify->flags)) {
0077 spin_lock_irqsave(&event->refs_lock, flags);
0078 nvkm_notify_get_locked(notify);
0079 spin_unlock_irqrestore(&event->refs_lock, flags);
0080 }
0081 }
0082
0083 static void
0084 nvkm_notify_work(struct work_struct *work)
0085 {
0086 struct nvkm_notify *notify = container_of(work, typeof(*notify), work);
0087 nvkm_notify_func(notify);
0088 }
0089
0090 void
0091 nvkm_notify_send(struct nvkm_notify *notify, void *data, u32 size)
0092 {
0093 struct nvkm_event *event = notify->event;
0094 unsigned long flags;
0095
0096 assert_spin_locked(&event->list_lock);
0097 BUG_ON(size != notify->size);
0098
0099 spin_lock_irqsave(&event->refs_lock, flags);
0100 if (notify->block) {
0101 spin_unlock_irqrestore(&event->refs_lock, flags);
0102 return;
0103 }
0104 nvkm_notify_put_locked(notify);
0105 spin_unlock_irqrestore(&event->refs_lock, flags);
0106
0107 if (test_bit(NVKM_NOTIFY_WORK, ¬ify->flags)) {
0108 memcpy((void *)notify->data, data, size);
0109 schedule_work(¬ify->work);
0110 } else {
0111 notify->data = data;
0112 nvkm_notify_func(notify);
0113 notify->data = NULL;
0114 }
0115 }
0116
0117 void
0118 nvkm_notify_fini(struct nvkm_notify *notify)
0119 {
0120 unsigned long flags;
0121 if (notify->event) {
0122 nvkm_notify_put(notify);
0123 spin_lock_irqsave(¬ify->event->list_lock, flags);
0124 list_del(¬ify->head);
0125 spin_unlock_irqrestore(¬ify->event->list_lock, flags);
0126 kfree((void *)notify->data);
0127 notify->event = NULL;
0128 }
0129 }
0130
0131 int
0132 nvkm_notify_init(struct nvkm_object *object, struct nvkm_event *event,
0133 int (*func)(struct nvkm_notify *), bool work,
0134 void *data, u32 size, u32 reply,
0135 struct nvkm_notify *notify)
0136 {
0137 unsigned long flags;
0138 int ret = -ENODEV;
0139 if ((notify->event = event), event->refs) {
0140 ret = event->func->ctor(object, data, size, notify);
0141 if (ret == 0 && (ret = -EINVAL, notify->size == reply)) {
0142 notify->flags = 0;
0143 notify->block = 1;
0144 notify->func = func;
0145 notify->data = NULL;
0146 if (ret = 0, work) {
0147 INIT_WORK(¬ify->work, nvkm_notify_work);
0148 set_bit(NVKM_NOTIFY_WORK, ¬ify->flags);
0149 notify->data = kmalloc(reply, GFP_KERNEL);
0150 if (!notify->data)
0151 ret = -ENOMEM;
0152 }
0153 }
0154 if (ret == 0) {
0155 spin_lock_irqsave(&event->list_lock, flags);
0156 list_add_tail(¬ify->head, &event->list);
0157 spin_unlock_irqrestore(&event->list_lock, flags);
0158 }
0159 }
0160 if (ret)
0161 notify->event = NULL;
0162 return ret;
0163 }