1 /****************************************************************************
3 * Copyright (c) 2014 - 2017 Samsung Electronics Co., Ltd. All rights reserved
5 ****************************************************************************/
7 #include <linux/module.h>
8 #include <linux/slab.h>
9 #include <linux/sched.h>
10 #include <linux/sched/task.h>
11 #include <scsc/scsc_logring.h>
12 #include "scsc_mif_abs.h"
13 #include "mifintrbit.h"
15 #include "mxlog_transport.h"
17 #define MXLOG_TRANSPORT_BUF_LENGTH (16 * 1024)
18 #define MXLOG_TRANSPORT_PACKET_SIZE (4)
20 /* Flag that an error has occurred so the I/O thread processing should stop */
21 void mxlog_transport_set_error(struct mxlog_transport
*mxlog_transport
)
23 SCSC_TAG_WARNING(MXLOG_TRANS
, "I/O thread processing is suspended\n");
25 mxlog_transport
->mxlog_thread
.block_thread
= 1;
28 static void input_irq_handler(int irq
, void *data
)
30 struct mxlog_transport
*mxlog_transport
= (struct mxlog_transport
*)data
;
31 struct mxlog_thread
*th
= &mxlog_transport
->mxlog_thread
;
32 struct scsc_mif_abs
*mif_abs
;
34 SCSC_TAG_DEBUG(MXLOG_TRANS
, "mxlog intr\n");
35 /* Clear the interrupt first to ensure we can't possibly miss one */
36 mif_abs
= scsc_mx_get_mif_abs(mxlog_transport
->mx
);
37 mif_abs
->irq_bit_clear(mif_abs
, irq
);
39 /* The the other side wrote some data to the input stream,
40 * wake up the thread that deals with this.
42 if (th
->task
== NULL
) {
43 SCSC_TAG_ERR(MXLOG_TRANS
, "mxlog_thread is NOT running\n");
47 * If an error has occured, we discard silently all messages from
48 * the stream until the error has been processed and the system has
51 if (th
->block_thread
== 1) {
52 SCSC_TAG_DEBUG(MXLOG_TRANS
, "discard message.\n");
54 * Do not try to acknowledge a pending interrupt here.
55 * This function is called by a function which in turn can be
56 * running in an atomic or 'disabled irq' level.
62 /* wake up I/O thread */
63 wake_up_interruptible(&th
->wakeup_q
);
66 static void thread_wait_until_stopped(struct mxlog_transport
*mxlog_transport
)
68 struct mxlog_thread
*th
= &mxlog_transport
->mxlog_thread
;
71 * kthread_stop() cannot handle the th exiting while
72 * kthread_should_stop() is false, so sleep until kthread_stop()
75 SCSC_TAG_INFO(MXLOG_TRANS
, "%s waiting for the stop signal.\n", th
->name
);
76 set_current_state(TASK_INTERRUPTIBLE
);
77 if (!kthread_should_stop()) {
78 SCSC_TAG_DEBUG(MXLOG_TRANS
, "%s schedule....\n", th
->name
);
83 * Caller that spawned the kthread did a get_task_struct()
84 * on task_struct, that will be released on stop...
85 * ...so we should NOT nullify th->task here.
87 SCSC_TAG_DEBUG(MXLOG_TRANS
, "%s exiting.\n", th
->name
);
91 * A thread that forwards messages sent across the transport to
92 * the registered handlers for each channel.
94 static int mxlog_thread_function(void *arg
)
96 struct mxlog_transport
*mxlog_transport
= (struct mxlog_transport
*)arg
;
97 struct mxlog_thread
*th
= &mxlog_transport
->mxlog_thread
;
101 size_t buf_sz
= 4096;
103 buf
= kmalloc(buf_sz
, GFP_KERNEL
);
105 SCSC_TAG_ERR(MXLOG_TRANS
, "Failed to alloc %s local buffer...exiting.\n", th
->name
);
108 /* completion is used only for startup thread-synchronization */
109 complete(&th
->completion
);
110 /* Thread ready...start ISR processing*/
111 th
->block_thread
= 0;
112 while (!kthread_should_stop()) {
113 /* wait until an error occurs, or we need to process */
114 ret
= wait_event_interruptible(th
->wakeup_q
,
115 (th
->wakeup_flag
&& !th
->block_thread
) ||
116 kthread_should_stop());
118 if (kthread_should_stop()) {
119 SCSC_TAG_DEBUG(MXLOG_TRANS
, "signalled to exit\n");
123 SCSC_TAG_DEBUG(MXLOG_TRANS
,
124 "wait_event returned %d, thread will exit\n", ret
);
125 thread_wait_until_stopped(mxlog_transport
);
129 SCSC_TAG_DEBUG(MXLOG_TRANS
, "wokeup: r=%d\n", ret
);
130 if (!mxlog_transport
->header_handler_fn
) {
131 /* Invalid header handler:
132 * unrecoverable log and terminate
134 SCSC_TAG_WARNING(MXLOG_TRANS
,
135 "mxlog_transport->header_handler_fn_==NULL\n");
138 while (mif_stream_read(&mxlog_transport
->mif_stream
,
139 &header
, sizeof(uint32_t))) {
144 mutex_lock(&mxlog_transport
->lock
);
145 if (!mxlog_transport
->header_handler_fn
) {
146 /* Invalid header handler:
147 * unrecoverable log and terminate
149 SCSC_TAG_WARNING(MXLOG_TRANS
,
150 "mxlog_transport->header_handler_fn_==NULL. Channel has been released\n");
151 mutex_unlock(&mxlog_transport
->lock
);
152 /* not recoverable, terminate straight away */
153 goto mxlog_thread_exit
;
156 * A generic header processor will properly retrieve
157 * level and num_bytes as specifically implemented
160 if (mxlog_transport
->header_handler_fn(header
, &phase
,
161 &level
, &num_bytes
)) {
162 SCSC_TAG_ERR(MXLOG_TRANS
,
163 "Bad sync in header: header=0x%08x\n", header
);
164 mutex_unlock(&mxlog_transport
->lock
);
165 /* not recoverable, terminate straight away */
166 goto mxlog_thread_exit
;
169 num_bytes
< (MXLOG_TRANSPORT_BUF_LENGTH
- sizeof(uint32_t))) {
172 /* 2nd read - payload (msg) */
173 ret_bytes
= mif_stream_read(&mxlog_transport
->mif_stream
,
175 mxlog_transport
->channel_handler_fn(phase
, buf
,
178 mxlog_transport
->channel_handler_data
);
180 SCSC_TAG_ERR(MXLOG_TRANS
,
181 "Bad num_bytes(%d) in header: header=0x%08x\n",
184 mutex_unlock(&mxlog_transport
->lock
);
189 SCSC_TAG_INFO(MXLOG_TRANS
, "exiting....\n");
194 static int mxlog_thread_start(struct mxlog_transport
*mxlog_transport
)
197 struct mxlog_thread
*th
= &mxlog_transport
->mxlog_thread
;
199 if (th
->task
!= NULL
) {
200 SCSC_TAG_WARNING(MXLOG_TRANS
, "%s thread already started\n", th
->name
);
204 /* Initialise thread structure */
205 th
->block_thread
= 1;
206 init_waitqueue_head(&th
->wakeup_q
);
207 init_completion(&th
->completion
);
209 snprintf(th
->name
, MXLOG_THREAD_NAME_MAX_LENGTH
, "mxlog_thread");
211 /* Start the kernel thread */
212 th
->task
= kthread_run(mxlog_thread_function
, mxlog_transport
, "%s", th
->name
);
213 if (IS_ERR(th
->task
)) {
214 int err
= (int)PTR_ERR(th
->task
);
221 * Avoid this task_struct vanishes immediately
222 * if the kthread exits by its own.
224 get_task_struct(th
->task
);
226 /* wait until thread function is running */
227 #define LOG_THREAD_START_TMO_SEC (3)
228 err
= wait_for_completion_timeout(&th
->completion
, msecs_to_jiffies(LOG_THREAD_START_TMO_SEC
* 1000));
230 SCSC_TAG_ERR(MXLOG_TRANS
, "timeout starting %s\n", th
->name
);
231 kthread_stop(th
->task
);
232 put_task_struct(th
->task
);
235 SCSC_TAG_INFO(MXLOG_TRANS
, "Started thread %s\n", th
->name
);
240 static void mxlog_thread_stop(struct mxlog_transport
*mxlog_transport
)
242 struct mxlog_thread
*th
= &mxlog_transport
->mxlog_thread
;
245 SCSC_TAG_WARNING(MXLOG_TRANS
, "%s is already stopped\n", th
->name
);
248 SCSC_TAG_INFO(MXLOG_TRANS
, "Stopping thread %s [%d]\n", th
->name
, th
->task
->pid
);
249 /* kthread_stop() marks thread as KTHREAD_SHOULD_STOP
250 * and wait for it to terminate
252 if (kthread_stop(th
->task
))
253 SCSC_TAG_ERR(MXLOG_TRANS
, "Failed to stop %s [%d]\n", th
->name
, th
->task
->pid
);
254 /* Finally release the task_struct we held on start */
255 put_task_struct(th
->task
);
259 void mxlog_transport_release(struct mxlog_transport
*mxlog_transport
)
261 mxlog_thread_stop(mxlog_transport
);
262 mif_stream_release(&mxlog_transport
->mif_stream
);
265 void mxlog_transport_config_serialise(struct mxlog_transport
*mxlog_transport
,
266 struct mxlogconf
*mxlogconf
)
268 mif_stream_config_serialise(&mxlog_transport
->mif_stream
, &mxlogconf
->stream_conf
);
271 /** Public functions */
272 int mxlog_transport_init(struct mxlog_transport
*mxlog_transport
, struct scsc_mx
*mx
)
275 uint32_t mem_length
= MXLOG_TRANSPORT_BUF_LENGTH
;
276 uint32_t packet_size
= MXLOG_TRANSPORT_PACKET_SIZE
;
277 uint32_t num_packets
;
280 * Initialising a buffer of 1 byte is never legitimate, do not allow it.
281 * The memory buffer length must be a multiple of the packet size.
284 memset(mxlog_transport
, 0, sizeof(struct mxlog_transport
));
285 mutex_init(&mxlog_transport
->lock
);
286 num_packets
= mem_length
/ packet_size
;
287 mxlog_transport
->mx
= mx
;
288 r
= mif_stream_init(&mxlog_transport
->mif_stream
, SCSC_MIF_ABS_TARGET_R4
, MIF_STREAM_DIRECTION_IN
, num_packets
, packet_size
, mx
, MIF_STREAM_INTRBIT_TYPE_ALLOC
, input_irq_handler
, mxlog_transport
);
291 r
= mxlog_thread_start(mxlog_transport
);
293 mif_stream_release(&mxlog_transport
->mif_stream
);
300 void mxlog_transport_register_channel_handler(struct mxlog_transport
*mxlog_transport
,
301 mxlog_header_handler parser
,
302 mxlog_channel_handler handler
,
305 mutex_lock(&mxlog_transport
->lock
);
306 mxlog_transport
->header_handler_fn
= parser
;
307 mxlog_transport
->channel_handler_fn
= handler
;
308 mxlog_transport
->channel_handler_data
= (void *)data
;
309 mutex_unlock(&mxlog_transport
->lock
);