Skip to content

Commit 8a4c7b9

Browse files
warped-rudijnettlet
authored andcommitted
MXC-CEC: Use a more efficient buffer allocation scheme
Don't vmalloc() a new buffer for each event. Instead, get memory in units of pages from the kernel and sub-allocate as needed. Signed-off-by: Rudi <[email protected]>
1 parent a2d6114 commit 8a4c7b9

File tree

1 file changed

+114
-57
lines changed

1 file changed

+114
-57
lines changed

drivers/mxc/hdmi-cec/mxc_hdmi-cec.c

Lines changed: 114 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ struct hdmi_cec_event {
9696
};
9797

9898

99+
static LIST_HEAD(ev_idle);
99100
static LIST_HEAD(ev_pending);
100101

101102
static int hdmi_cec_irq;
@@ -109,6 +110,60 @@ static u8 is_initialized;
109110
static wait_queue_head_t rx_queue;
110111
static wait_queue_head_t tx_queue;
111112

113+
static struct hdmi_cec_event *alloc_event(void)
114+
{
115+
int i;
116+
struct hdmi_cec_event *event;
117+
118+
if (list_empty(&ev_idle)) {
119+
event = (void *)__get_free_page(GFP_KERNEL);
120+
121+
if (!event) {
122+
pr_err("HDMI-CEC: Failed to allocate event buffer\n");
123+
return NULL;
124+
}
125+
126+
for (i = 1; i < PAGE_SIZE / sizeof(struct hdmi_cec_event); i++)
127+
list_add_tail(&event[i].list, &ev_idle);
128+
129+
pr_debug("HDMI-CEC: Allocated event buffer page: @%08lx (%d)\n",
130+
(unsigned long)event, i);
131+
} else {
132+
event = list_first_entry(&ev_idle, struct hdmi_cec_event, list);
133+
list_del(&event->list);
134+
}
135+
136+
memset(event, 0, sizeof(struct hdmi_cec_event));
137+
return event;
138+
}
139+
140+
static void free_events(void)
141+
{
142+
struct hdmi_cec_event *event, *tmp_event;
143+
144+
/* Flush events which have not been read by user space */
145+
list_splice_init(&ev_pending, &ev_idle);
146+
147+
/* Find item(s) starting on a page boundary */
148+
list_for_each_entry_safe(event, tmp_event, &ev_idle, list) {
149+
if (((unsigned long)event & ~PAGE_MASK) == 0)
150+
list_move_tail(&event->list, &ev_pending);
151+
}
152+
153+
/* Discard idle list */
154+
INIT_LIST_HEAD(&ev_idle);
155+
156+
/* Empty pending list and free page(s) */
157+
while (!list_empty(&ev_pending)) {
158+
event = list_first_entry(&ev_pending, struct hdmi_cec_event, list);
159+
list_del(&event->list);
160+
free_page((unsigned long)event);
161+
162+
pr_debug("HDMI-CEC: Freed event buffer page: @%08lx\n",
163+
(unsigned long)event);
164+
}
165+
}
166+
112167
static u32 get_hpd_stat(struct hdmi_cec_priv *hdmi_cec)
113168
{
114169
u32 cec_stat0 = 0;
@@ -156,12 +211,29 @@ static irqreturn_t mxc_hdmi_cec_isr(int irq, void *data)
156211

157212
static void mxc_hdmi_cec_handle(u32 cec_stat)
158213
{
159-
u8 i = 0;
160-
struct hdmi_cec_event *event = NULL;
214+
int i;
215+
struct hdmi_cec_event *event;
161216

162217
if (!hdmi_cec_data.open_count)
163218
return;
164219

220+
/*HDMI cable connected: handle first*/
221+
if (cec_stat & CEC_STAT0_EX_CONNECTED) {
222+
pr_info("HDMI-CEC: link connected\n");
223+
224+
mutex_lock(&hdmi_cec_data.lock);
225+
event = alloc_event();
226+
if (!event) {
227+
mutex_unlock(&hdmi_cec_data.lock);
228+
return;
229+
}
230+
event->event_type = MESSAGE_TYPE_CONNECTED;
231+
list_add_tail(&event->list, &ev_pending);
232+
mutex_unlock(&hdmi_cec_data.lock);
233+
234+
wake_up(&rx_queue);
235+
}
236+
165237
/* The current transmission is successful (for initiator only). */
166238
if (cec_stat & HDMI_IH_CEC_STAT0_DONE) {
167239
hdmi_cec_data.tx_answer = cec_stat;
@@ -170,25 +242,28 @@ static void mxc_hdmi_cec_handle(u32 cec_stat)
170242

171243
/*EOM is detected so that the received data is ready in the receiver data buffer*/
172244
if (cec_stat & HDMI_IH_CEC_STAT0_EOM) {
173-
event = vmalloc(sizeof(struct hdmi_cec_event));
174-
if (NULL == event) {
175-
pr_err("%s: Not enough memory!\n", __func__);
245+
mutex_lock(&hdmi_cec_data.lock);
246+
event = alloc_event();
247+
if (!event) {
248+
mutex_unlock(&hdmi_cec_data.lock);
176249
return;
177250
}
178-
memset(event, 0, sizeof(struct hdmi_cec_event));
251+
179252
event->msg_len = hdmi_readb(HDMI_CEC_RX_CNT);
180-
if (!event->msg_len) {
181-
pr_err("%s: Invalid CEC message length!\n", __func__);
182-
vfree(event);
253+
if (!event->msg_len || event->msg_len > MAX_MESSAGE_LEN) {
254+
pr_err("HDMI-CEC: Invalid CEC message length\n");
255+
list_add_tail(&event->list, &ev_idle);
256+
mutex_unlock(&hdmi_cec_data.lock);
183257
return;
184258
}
259+
185260
event->event_type = MESSAGE_TYPE_RECEIVE_SUCCESS;
186261
for (i = 0; i < event->msg_len; i++)
187-
event->msg[i] = hdmi_readb(HDMI_CEC_RX_DATA0+i);
188-
hdmi_writeb(0x0, HDMI_CEC_LOCK);
189-
mutex_lock(&hdmi_cec_data.lock);
262+
event->msg[i] = hdmi_readb(HDMI_CEC_RX_DATA0 + i);
263+
190264
list_add_tail(&event->list, &ev_pending);
191265
mutex_unlock(&hdmi_cec_data.lock);
266+
192267
wake_up(&rx_queue);
193268
}
194269

@@ -215,35 +290,20 @@ static void mxc_hdmi_cec_handle(u32 cec_stat)
215290
hdmi_cec_data.receive_error++;
216291
}
217292

218-
/* HDMI cable connected */
219-
if (cec_stat & CEC_STAT0_EX_CONNECTED) {
220-
pr_info("HDMI-CEC: link connected\n");
221-
pr_info("HDMI link connected\n");
222-
event = vmalloc(sizeof(struct hdmi_cec_event));
223-
if (NULL == event) {
224-
pr_err("%s: Not enough memory\n", __func__);
225-
return;
226-
}
227-
memset(event, 0, sizeof(struct hdmi_cec_event));
228-
event->event_type = MESSAGE_TYPE_CONNECTED;
229-
mutex_lock(&hdmi_cec_data.lock);
230-
list_add_tail(&event->list, &ev_pending);
231-
mutex_unlock(&hdmi_cec_data.lock);
232-
wake_up(&rx_queue);
233-
}
234-
/*HDMI cable disconnected*/
293+
/*HDMI cable disconnected: handle last*/
235294
if (cec_stat & CEC_STAT0_EX_DISCONNECTED) {
236295
pr_info("HDMI-CEC: link disconnected\n");
237-
event = vmalloc(sizeof(struct hdmi_cec_event));
238-
if (NULL == event) {
239-
pr_err("%s: Not enough memory!\n", __func__);
296+
297+
mutex_lock(&hdmi_cec_data.lock);
298+
event = alloc_event();
299+
if (!event) {
300+
mutex_unlock(&hdmi_cec_data.lock);
240301
return;
241302
}
242-
memset(event, 0, sizeof(struct hdmi_cec_event));
243303
event->event_type = MESSAGE_TYPE_DISCONNECTED;
244-
mutex_lock(&hdmi_cec_data.lock);
245304
list_add_tail(&event->list, &ev_pending);
246305
mutex_unlock(&hdmi_cec_data.lock);
306+
247307
wake_up(&rx_queue);
248308
}
249309
}
@@ -254,6 +314,9 @@ static void mxc_hdmi_cec_worker(struct work_struct *work)
254314

255315
mxc_hdmi_cec_handle(hdmi_cec_data.cec_stat0);
256316

317+
if (hdmi_cec_data.cec_stat0 & HDMI_IH_CEC_STAT0_EOM)
318+
hdmi_writeb(0x0, HDMI_CEC_LOCK);
319+
257320
spin_lock_irqsave(&hdmi_cec_data.irq_lock, flags);
258321
hdmi_cec_data.cec_stat0 = 0;
259322
if (hdmi_cec_data.is_started)
@@ -279,7 +342,8 @@ static int hdmi_cec_open(struct inode *inode, struct file *file)
279342
static ssize_t hdmi_cec_read(struct file *file, char __user *buf, size_t count,
280343
loff_t *ppos)
281344
{
282-
struct hdmi_cec_event *event = NULL;
345+
ssize_t len;
346+
struct hdmi_cec_event *event;
283347

284348
pr_debug("function : %s\n", __func__);
285349

@@ -293,26 +357,24 @@ static ssize_t hdmi_cec_read(struct file *file, char __user *buf, size_t count,
293357
if (file->f_flags & O_NONBLOCK) {
294358
mutex_unlock(&hdmi_cec_data.lock);
295359
return -EAGAIN;
296-
} else {
297-
do {
298-
mutex_unlock(&hdmi_cec_data.lock);
299-
if (wait_event_interruptible(rx_queue, !list_empty(&ev_pending)))
300-
return -ERESTARTSYS;
301-
mutex_lock(&hdmi_cec_data.lock);
302-
} while (list_empty(&ev_pending));
303360
}
361+
362+
do {
363+
mutex_unlock(&hdmi_cec_data.lock);
364+
if (wait_event_interruptible(rx_queue, !list_empty(&ev_pending)))
365+
return -ERESTARTSYS;
366+
mutex_lock(&hdmi_cec_data.lock);
367+
} while (list_empty(&ev_pending));
304368
}
305369

370+
len = offsetof(struct hdmi_cec_event, list);
306371
event = list_first_entry(&ev_pending, struct hdmi_cec_event, list);
307-
list_del(&event->list);
372+
if (copy_to_user(buf, event, len))
373+
len = -EFAULT;
374+
list_move_tail(&event->list, &ev_idle);
308375
mutex_unlock(&hdmi_cec_data.lock);
309-
if (copy_to_user(buf, event,
310-
sizeof(struct hdmi_cec_event) - sizeof(struct list_head))) {
311-
vfree(event);
312-
return -EFAULT;
313-
}
314-
vfree(event);
315-
return (sizeof(struct hdmi_cec_event) - sizeof(struct list_head));
376+
377+
return len;
316378
}
317379

318380
static ssize_t hdmi_cec_write(struct file *file, const char __user *buf,
@@ -503,8 +565,6 @@ static long hdmi_cec_ioctl(struct file *file, u_int cmd,
503565

504566
static int hdmi_cec_release(struct inode *inode, struct file *file)
505567
{
506-
struct hdmi_cec_event *event, *tmp_event;
507-
508568
pr_debug("function : %s\n", __func__);
509569

510570
mutex_lock(&hdmi_cec_data.lock);
@@ -513,11 +573,7 @@ static int hdmi_cec_release(struct inode *inode, struct file *file)
513573
hdmi_cec_data.is_started = false;
514574
hdmi_cec_data.logical_address = 15;
515575

516-
/* Flush eventual events which have not been read by user space */
517-
list_for_each_entry_safe(event, tmp_event, &ev_pending, list) {
518-
list_del(&event->list);
519-
vfree(event);
520-
}
576+
free_events();
521577
}
522578
mutex_unlock(&hdmi_cec_data.lock);
523579

@@ -592,6 +648,7 @@ static int hdmi_cec_dev_probe(struct platform_device *pdev)
592648
goto err_out_class;
593649
}
594650

651+
INIT_LIST_HEAD(&ev_idle);
595652
INIT_LIST_HEAD(&ev_pending);
596653

597654
init_waitqueue_head(&rx_queue);

0 commit comments

Comments
 (0)