Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  * Copyright 2014 Red Hat Inc.
0003  *
0004  * Permission is hereby granted, free of charge, to any person obtaining a
0005  * copy of this software and associated documentation files (the "Software"),
0006  * to deal in the Software without restriction, including without limitation
0007  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
0008  * and/or sell copies of the Software, and to permit persons to whom the
0009  * Software is furnished to do so, subject to the following conditions:
0010  *
0011  * The above copyright notice and this permission notice shall be included in
0012  * all copies or substantial portions of the Software.
0013  *
0014  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
0015  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
0016  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
0017  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
0018  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
0019  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
0020  * OTHER DEALINGS IN THE SOFTWARE.
0021  *
0022  * Authors: Ben Skeggs <bskeggs@redhat.com>
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, &notify->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, &notify->flags))
0045             flush_work(&notify->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, &notify->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, &notify->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, &notify->flags)) {
0108         memcpy((void *)notify->data, data, size);
0109         schedule_work(&notify->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(&notify->event->list_lock, flags);
0124         list_del(&notify->head);
0125         spin_unlock_irqrestore(&notify->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(&notify->work, nvkm_notify_work);
0148                 set_bit(NVKM_NOTIFY_WORK, &notify->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(&notify->head, &event->list);
0157             spin_unlock_irqrestore(&event->list_lock, flags);
0158         }
0159     }
0160     if (ret)
0161         notify->event = NULL;
0162     return ret;
0163 }