xref: /OK3568_Linux_fs/kernel/drivers/gpu/arm/mali400/mali/linux/mali_osk_notification.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1 /*
2  * Copyright (C) 2010-2014, 2016-2017 ARM Limited. All rights reserved.
3  *
4  * This program is free software and is provided to you under the terms of the GNU General Public License version 2
5  * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence.
6  *
7  * A copy of the licence is included with the program, and can also be obtained from Free Software
8  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
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 
_mali_osk_notification_queue_init(void)40 _mali_osk_notification_queue_t *_mali_osk_notification_queue_init(void)
41 {
42 	_mali_osk_notification_queue_t         *result;
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 
_mali_osk_notification_create(u32 type,u32 size)54 _mali_osk_notification_t *_mali_osk_notification_create(u32 type, u32 size)
55 {
56 	/* OPT Recycling of notification objects */
57 	_mali_osk_notification_wrapper_t *notification;
58 
59 	notification = (_mali_osk_notification_wrapper_t *)kmalloc(sizeof(_mali_osk_notification_wrapper_t) + size,
60 			GFP_KERNEL | __GFP_HIGH | __GFP_RETRY_MAYFAIL);
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(&notification->list);
68 
69 	if (0 != size) {
70 		notification->data.result_buffer = ((u8 *)notification) + sizeof(_mali_osk_notification_wrapper_t);
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 
_mali_osk_notification_delete(_mali_osk_notification_t * object)83 void _mali_osk_notification_delete(_mali_osk_notification_t *object)
84 {
85 	_mali_osk_notification_wrapper_t *notification;
86 	MALI_DEBUG_ASSERT_POINTER(object);
87 
88 	notification = container_of(object, _mali_osk_notification_wrapper_t, data);
89 
90 	/* Free the container */
91 	kfree(notification);
92 }
93 
_mali_osk_notification_queue_term(_mali_osk_notification_queue_t * queue)94 void _mali_osk_notification_queue_term(_mali_osk_notification_queue_t *queue)
95 {
96 	_mali_osk_notification_t *result;
97 	MALI_DEBUG_ASSERT_POINTER(queue);
98 
99 	while (_MALI_OSK_ERR_OK == _mali_osk_notification_queue_dequeue(queue, &result)) {
100 		_mali_osk_notification_delete(result);
101 	}
102 
103 	/* not much to do, just free the memory */
104 	kfree(queue);
105 }
_mali_osk_notification_queue_send(_mali_osk_notification_queue_t * queue,_mali_osk_notification_t * object)106 void _mali_osk_notification_queue_send(_mali_osk_notification_queue_t *queue, _mali_osk_notification_t *object)
107 {
108 #if defined(MALI_UPPER_HALF_SCHEDULING)
109 	unsigned long irq_flags;
110 #endif
111 
112 	_mali_osk_notification_wrapper_t *notification;
113 	MALI_DEBUG_ASSERT_POINTER(queue);
114 	MALI_DEBUG_ASSERT_POINTER(object);
115 
116 	notification = container_of(object, _mali_osk_notification_wrapper_t, data);
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(&notification->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 
_mali_osk_notification_queue_dequeue(_mali_osk_notification_queue_t * queue,_mali_osk_notification_t ** result)136 _mali_osk_errcode_t _mali_osk_notification_queue_dequeue(_mali_osk_notification_queue_t *queue, _mali_osk_notification_t **result)
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 
_mali_osk_notification_queue_receive(_mali_osk_notification_queue_t * queue,_mali_osk_notification_t ** result)167 _mali_osk_errcode_t _mali_osk_notification_queue_receive(_mali_osk_notification_queue_t *queue, _mali_osk_notification_t **result)
168 {
169 	/* check input */
170 	MALI_DEBUG_ASSERT_POINTER(queue);
171 	MALI_DEBUG_ASSERT_POINTER(result);
172 
173 	/* default result */
174 	*result = NULL;
175 
176 	if (wait_event_interruptible(queue->receive_queue,
177 				     _MALI_OSK_ERR_OK == _mali_osk_notification_queue_dequeue(queue, result))) {
178 		return _MALI_OSK_ERR_RESTARTSYSCALL;
179 	}
180 
181 	return _MALI_OSK_ERR_OK; /* all ok */
182 }
183