Commit | Line | Data |
---|---|---|
6fa3eb70 S |
1 | /* |
2 | * This confidential and proprietary software may be used only as | |
3 | * authorised by a licensing agreement from ARM Limited | |
02af6beb | 4 | * (C) COPYRIGHT 2008-2015 ARM Limited |
6fa3eb70 S |
5 | * ALL RIGHTS RESERVED |
6 | * The entire notice above must be reproduced on all authorised | |
7 | * copies and copies may only be made to the extent permitted | |
8 | * by a licensing agreement from ARM Limited. | |
9 | */ | |
10 | ||
11 | /** | |
12 | * @file mali_osk_notification.c | |
13 | * Implementation of the OS abstraction layer for the kernel device driver | |
14 | */ | |
15 | ||
16 | #include "mali_osk.h" | |
17 | #include "mali_kernel_common.h" | |
18 | ||
19 | #include <linux/sched.h> | |
20 | #include <linux/slab.h> | |
21 | #include <linux/spinlock.h> | |
22 | ||
23 | /** | |
24 | * Declaration of the notification queue object type | |
25 | * Contains a linked list of notification pending delivery to user space. | |
26 | * It also contains a wait queue of exclusive waiters blocked in the ioctl | |
27 | * When a new notification is posted a single thread is resumed. | |
28 | */ | |
29 | struct _mali_osk_notification_queue_t_struct { | |
30 | spinlock_t mutex; /**< Mutex protecting the list */ | |
31 | wait_queue_head_t receive_queue; /**< Threads waiting for new entries to the queue */ | |
32 | struct list_head head; /**< List of notifications waiting to be picked up */ | |
33 | }; | |
34 | ||
35 | typedef struct _mali_osk_notification_wrapper_t_struct { | |
36 | struct list_head list; /**< Internal linked list variable */ | |
37 | _mali_osk_notification_t data; /**< Notification data */ | |
38 | } _mali_osk_notification_wrapper_t; | |
39 | ||
02af6beb | 40 | _mali_osk_notification_queue_t *_mali_osk_notification_queue_init(void) |
6fa3eb70 | 41 | { |
02af6beb | 42 | _mali_osk_notification_queue_t *result; |
6fa3eb70 S |
43 | |
44 | result = (_mali_osk_notification_queue_t *)kmalloc(sizeof(_mali_osk_notification_queue_t), GFP_KERNEL); | |
45 | if (NULL == result) return NULL; | |
46 | ||
47 | spin_lock_init(&result->mutex); | |
48 | init_waitqueue_head(&result->receive_queue); | |
49 | INIT_LIST_HEAD(&result->head); | |
50 | ||
51 | return result; | |
52 | } | |
53 | ||
02af6beb | 54 | _mali_osk_notification_t *_mali_osk_notification_create(u32 type, u32 size) |
6fa3eb70 S |
55 | { |
56 | /* OPT Recycling of notification objects */ | |
57 | _mali_osk_notification_wrapper_t *notification; | |
58 | ||
02af6beb S |
59 | notification = (_mali_osk_notification_wrapper_t *)kmalloc(sizeof(_mali_osk_notification_wrapper_t) + size, |
60 | GFP_KERNEL | __GFP_HIGH | __GFP_REPEAT); | |
6fa3eb70 S |
61 | if (NULL == notification) { |
62 | MALI_DEBUG_PRINT(1, ("Failed to create a notification object\n")); | |
63 | return NULL; | |
64 | } | |
65 | ||
66 | /* Init the list */ | |
67 | INIT_LIST_HEAD(¬ification->list); | |
68 | ||
69 | if (0 != size) { | |
02af6beb | 70 | notification->data.result_buffer = ((u8 *)notification) + sizeof(_mali_osk_notification_wrapper_t); |
6fa3eb70 S |
71 | } else { |
72 | notification->data.result_buffer = NULL; | |
73 | } | |
74 | ||
75 | /* set up the non-allocating fields */ | |
76 | notification->data.notification_type = type; | |
77 | notification->data.result_buffer_size = size; | |
78 | ||
79 | /* all ok */ | |
80 | return &(notification->data); | |
81 | } | |
82 | ||
02af6beb | 83 | void _mali_osk_notification_delete(_mali_osk_notification_t *object) |
6fa3eb70 S |
84 | { |
85 | _mali_osk_notification_wrapper_t *notification; | |
02af6beb | 86 | MALI_DEBUG_ASSERT_POINTER(object); |
6fa3eb70 | 87 | |
02af6beb | 88 | notification = container_of(object, _mali_osk_notification_wrapper_t, data); |
6fa3eb70 S |
89 | |
90 | /* Free the container */ | |
91 | kfree(notification); | |
92 | } | |
93 | ||
02af6beb | 94 | void _mali_osk_notification_queue_term(_mali_osk_notification_queue_t *queue) |
6fa3eb70 S |
95 | { |
96 | _mali_osk_notification_t *result; | |
02af6beb | 97 | MALI_DEBUG_ASSERT_POINTER(queue); |
6fa3eb70 S |
98 | |
99 | while (_MALI_OSK_ERR_OK == _mali_osk_notification_queue_dequeue(queue, &result)) { | |
02af6beb | 100 | _mali_osk_notification_delete(result); |
6fa3eb70 S |
101 | } |
102 | ||
103 | /* not much to do, just free the memory */ | |
104 | kfree(queue); | |
105 | } | |
02af6beb | 106 | void _mali_osk_notification_queue_send(_mali_osk_notification_queue_t *queue, _mali_osk_notification_t *object) |
6fa3eb70 S |
107 | { |
108 | #if defined(MALI_UPPER_HALF_SCHEDULING) | |
109 | unsigned long irq_flags; | |
110 | #endif | |
111 | ||
112 | _mali_osk_notification_wrapper_t *notification; | |
02af6beb S |
113 | MALI_DEBUG_ASSERT_POINTER(queue); |
114 | MALI_DEBUG_ASSERT_POINTER(object); | |
6fa3eb70 | 115 | |
02af6beb | 116 | notification = container_of(object, _mali_osk_notification_wrapper_t, data); |
6fa3eb70 S |
117 | |
118 | #if defined(MALI_UPPER_HALF_SCHEDULING) | |
119 | spin_lock_irqsave(&queue->mutex, irq_flags); | |
120 | #else | |
121 | spin_lock(&queue->mutex); | |
122 | #endif | |
123 | ||
124 | list_add_tail(¬ification->list, &queue->head); | |
125 | ||
126 | #if defined(MALI_UPPER_HALF_SCHEDULING) | |
127 | spin_unlock_irqrestore(&queue->mutex, irq_flags); | |
128 | #else | |
129 | spin_unlock(&queue->mutex); | |
130 | #endif | |
131 | ||
132 | /* and wake up one possible exclusive waiter */ | |
133 | wake_up(&queue->receive_queue); | |
134 | } | |
135 | ||
02af6beb | 136 | _mali_osk_errcode_t _mali_osk_notification_queue_dequeue(_mali_osk_notification_queue_t *queue, _mali_osk_notification_t **result) |
6fa3eb70 S |
137 | { |
138 | #if defined(MALI_UPPER_HALF_SCHEDULING) | |
139 | unsigned long irq_flags; | |
140 | #endif | |
141 | ||
142 | _mali_osk_errcode_t ret = _MALI_OSK_ERR_ITEM_NOT_FOUND; | |
143 | _mali_osk_notification_wrapper_t *wrapper_object; | |
144 | ||
145 | #if defined(MALI_UPPER_HALF_SCHEDULING) | |
146 | spin_lock_irqsave(&queue->mutex, irq_flags); | |
147 | #else | |
148 | spin_lock(&queue->mutex); | |
149 | #endif | |
150 | ||
151 | if (!list_empty(&queue->head)) { | |
152 | wrapper_object = list_entry(queue->head.next, _mali_osk_notification_wrapper_t, list); | |
153 | *result = &(wrapper_object->data); | |
154 | list_del_init(&wrapper_object->list); | |
155 | ret = _MALI_OSK_ERR_OK; | |
156 | } | |
157 | ||
158 | #if defined(MALI_UPPER_HALF_SCHEDULING) | |
159 | spin_unlock_irqrestore(&queue->mutex, irq_flags); | |
160 | #else | |
161 | spin_unlock(&queue->mutex); | |
162 | #endif | |
163 | ||
164 | return ret; | |
165 | } | |
166 | ||
02af6beb | 167 | _mali_osk_errcode_t _mali_osk_notification_queue_receive(_mali_osk_notification_queue_t *queue, _mali_osk_notification_t **result) |
6fa3eb70 S |
168 | { |
169 | /* check input */ | |
02af6beb S |
170 | MALI_DEBUG_ASSERT_POINTER(queue); |
171 | MALI_DEBUG_ASSERT_POINTER(result); | |
6fa3eb70 S |
172 | |
173 | /* default result */ | |
174 | *result = NULL; | |
175 | ||
176 | if (wait_event_interruptible(queue->receive_queue, | |
02af6beb | 177 | _MALI_OSK_ERR_OK == _mali_osk_notification_queue_dequeue(queue, result))) { |
6fa3eb70 S |
178 | return _MALI_OSK_ERR_RESTARTSYSCALL; |
179 | } | |
180 | ||
181 | return _MALI_OSK_ERR_OK; /* all ok */ | |
182 | } |