flusher.c

Go to the documentation of this file.
00001 /*!
00002  * \file        log/server/src/flusher.c
00003  * \brief       TCP-based Server flushing to various output media.
00004  *
00005  * \date        03/02/2001
00006  * \author      Jork Loeser <jork.loeser@inf.tu-dresden.de>
00007  *
00008  * This file includes the code to flush the buffered log-data.
00009  * Network-specific things are handled in tcpip.c.
00010  *
00011  * The communication with the main thread is done by waiting for IPCs from
00012  * the main thread. Our own OSKIT-Code is activated by either our actions,
00013  * or by actions due to interrupts/exceptions. We notice an interrupt/exception
00014  * because our wait-IPC is aborted. In reaction to this, we check if something
00015  * happened on the net.
00016  *
00017  * In detail, this file hanles:
00018  * - creating the listener thread
00019  * - implementing the flush-operations
00020  *
00021  * Function-Interface:  flusher_init(), flush_buffer, do_flush_buffer().
00022  *
00023  */
00024 /* (c) 2003 Technische Universitaet Dresden
00025  * This file is part of DROPS, which is distributed under the terms of the
00026  * GNU General Public License 2. Please see the COPYING file for details.
00027  */
00028 
00029 #include <l4/util/util.h>
00030 #include <l4/sys/ipc.h>
00031 #include <l4/sys/syscalls.h>
00032 #include <l4/sys/kdebug.h>
00033 #include <l4/rmgr/librmgr.h>
00034 #if CONFIG_USE_SERIAL
00035 #include <l4/serial/serial.h>
00036 #endif
00037 #include <assert.h>
00038 
00039 #include <l4/log/l4log.h>
00040 #include <stdio.h>
00041 #include <errno.h>
00042 #include "config.h"
00043 #include "tcpip.h"
00044 #include "flusher.h"
00045 #include "stuff.h"
00046 
00047 #if CONFIG_USE_TCPIP==0
00048 const unsigned client_socket=0;
00049 #endif
00050 
00051 int flusher_prio;
00052 l4_threadid_t main_thread, flusher_thread;
00053 
00054 
00055 static l4_threadid_t flush_requester;
00056 
00057 /*!\brief Wait for an IPC from the main thread.
00058  *
00059  * The main thread will do an IPC-call to indicate a full buffer and
00060  * wait for successful flushing..
00061  */
00062 static int wait_for_flush_request(void){
00063     int err;
00064     l4_msgdope_t result;
00065     l4_umword_t dw0, dw1;
00066 
00067     err = l4_ipc_wait(&flush_requester, NULL, &dw0, &dw1,
00068                            L4_IPC_NEVER, &result);
00069     LOGd(CONFIG_LOG_IPC, "received ipc, err=%#x", err);
00070     return err;
00071 }
00072 
00073 /*!\brief Answer a flush-request from the main thread. */
00074 static int answer_flush_request(void){
00075     int err;
00076     l4_msgdope_t result;
00077 
00078     do{err = l4_ipc_send(flush_requester, NULL, 0, 0,
00079                               L4_IPC_NEVER, &result);
00080     }while(err == L4_IPC_SECANCELED);
00081     return err;
00082 }
00083 
00084 /*!\brief Thread-loop: Wait for a client and handle it's requests
00085  *
00086  * This function opens the bound socket, and triggers the accept on it.
00087  * Then it sends the up-and-running IPC to it's parrent and waits for
00088  * clients to connect. If we have a client, wait for IPC's from the main
00089  * thread requesting a buffer-flush.
00090  *
00091  * Alternatively, this IPC may be aborted by local interrupt handling. If we
00092  * realize this, we check the network stack.
00093  *
00094  * As reaction to creation, we send an IPC to main_thread, dw0 contains
00095  * an error code, 0 means success.
00096  */
00097 static void thread_loop(void){
00098     l4_msgdope_t result;
00099     int ret=0, err;
00100 
00101     /* raise priority */
00102     rmgr_set_prio(l4_myself(), flusher_prio);
00103 
00104 #if CONFIG_USE_TCPIP
00105     if(flush_to_net && (ret=net_init(0,0))!=0){
00106         LOG_Error("Error %#x initializing network", ret);
00107     }
00108 #endif
00109 
00110     do{
00111         err = l4_ipc_send(main_thread, NULL, ret, 0,
00112                                L4_IPC_NEVER, &result);
00113     } while(err==L4_IPC_SECANCELED);
00114     if(err){
00115         LOG_Error("sending ipc to main thread returned error %#x.", err);
00116         enter_kdebug("and now?");
00117     }
00118 
00119     /* Our acceptor socket is up and running */
00120     while(1){
00121 #if CONFIG_USE_TCPIP
00122         if(flush_to_net){
00123             err = net_wait_for_client();
00124             if(err){
00125                 LOG_Error("Error %#x waiting for next client.", err);
00126                 l4_sleep(1000);
00127                 continue;
00128             }
00129             /* We have a new client now. */
00130         }
00131 #endif
00132         /* Wait for requests from the main thread. If the IPC is
00133            aborted, this is probably due to an interrupt at the
00134            network stack. We react to this in polling the
00135            client-socket. */
00136         do{
00137             err = wait_for_flush_request();
00138             if(err & L4_IPC_RECANCELED){
00139                 /* Probably we got an interrupt, check the network */
00140                 LOGd(CONFIG_LOG_IPC,
00141                         "Probably got interrupt, try to read from network");
00142 
00143                 // this may result in calling do_flush_buffer
00144 #if CONFIG_USE_TCPIP
00145                 if(flush_to_net){
00146                     if((err=net_receive_check())!=0){
00147                         LOGd(CONFIG_LOG_TCPIP,
00148                                 "net_receive_check() returned error %#x", err);
00149                         break;
00150                     }
00151                 }
00152 #endif
00153             } else if(err){ // IPC not cancelled
00154                     LOG_Error("Error %#x receiving IPC from main-thread", err);
00155                     continue;
00156             } else { /* got real and valid IPC from main-thread, flush
00157                         the buffer and send an answer */
00158                 // LOG("begin flush");
00159                 do_flush_buffer();
00160                 // LOG("end flush");
00161                 err = answer_flush_request();
00162                 if(err){
00163                     LOG_Error("Error anwering flush-request.");
00164                 }
00165             }
00166         } while (!flush_to_net || client_socket);
00167     } // never ending
00168 
00169     LOG_Error("Log-TCP-Srv-thread: How did I get here?");
00170     enter_kdebug("Something went really wrong.");
00171 }
00172 
00173 static int general_flush_buffer(int head, int(*flush)(const char*, int)){
00174     int err;
00175 
00176     if(head < buffer_tail){
00177         LOGd(CONFIG_LOG_RINGBUFFER, "flushing 1/2 %d+%d",
00178                 buffer_tail, buffer_size-buffer_tail);
00179         if((err=flush(buffer_array+buffer_tail,
00180                       (int)buffer_size-buffer_tail))!=0) return err;
00181         LOGd(CONFIG_LOG_RINGBUFFER, "flushing 2/2 %d+%d",
00182                 0, head);
00183         if((err=flush(buffer_array, head))!=0) return err;
00184     } else {
00185         LOGd(CONFIG_LOG_RINGBUFFER, "flushing 1/1 %d+%d",
00186                 buffer_tail, head-buffer_tail);
00187         err = flush(buffer_array+buffer_tail, head-buffer_tail);
00188     }
00189     return err;
00190 }
00191                 
00192 /*!\brief Flush the buffer to the local console.
00193  *
00194  * \retval      0 on success, error otherwise.
00195  */
00196 static int console_flush(const char*addr, int size){
00197     while(*addr && size--){
00198         outchar(*addr++);
00199     }
00200     return 0;
00201 }
00202 
00203 #if CONFIG_USE_SERIAL
00204 int serial_flush(const char*addr, int size){
00205     while(*addr && size--){
00206         int err;
00207         err = l4serial_outstring(addr, 1);
00208         if(err) printf("\nl4serial_outstring(): %d\n", err);
00209         if(*addr=='\n') l4serial_outstring("\r", 1);
00210         addr++;
00211     }
00212     return 0;
00213 }
00214 #endif
00215 
00216 
00217 /*!\brief Flush the buffered data.
00218  *
00219  * Context: Flusher thread.
00220  *
00221  * net_flush_buffer() is exported from tcpip.c, console_flush() is a local
00222  * function.
00223  */
00224 int do_flush_buffer(void){
00225     unsigned head = buffer_head;
00226     int err;
00227 
00228     LOGd(CONFIG_LOG_RINGBUFFER, "Flushing buffer now.");
00229 
00230     if(flush_local){
00231         err = general_flush_buffer(head, console_flush);
00232         if(err) return err;
00233     }
00234 #if CONFIG_USE_SERIAL
00235     if(flush_serial){
00236         err = general_flush_buffer(head, serial_flush);
00237         if(err) return err;
00238     }
00239 #endif
00240 #if CONFIG_USE_TCPIP
00241     if(flush_to_net){
00242         err = general_flush_buffer(head, net_flush_buffer);
00243         if(err) return err;
00244     }
00245 #endif
00246     buffer_tail = head;
00247     return 0;
00248 }
00249 
00250 /*!\brief Request flushing the buffer.
00251  *
00252  * This function blocks until the buffer is flushed. If we are in muxed mode
00253  * (flush_muxed==1), this function also flushes the binary buffers.
00254  *
00255  * Context: Main thread, flush-signaller thread
00256  *
00257  * \retval      0 on success, error otherwise.
00258  */
00259 int flush_buffer(void){
00260     l4_msgdope_t result;
00261     l4_umword_t dw0, dw1;
00262     int err;
00263 
00264     if(buffer_size){
00265         err = L4_IPC_SECANCELED;
00266         while(err == L4_IPC_SECANCELED){
00267             err = l4_ipc_call(flusher_thread, NULL, 0, 0,
00268                                    NULL, &dw0, &dw1,
00269                                    L4_IPC_NEVER, &result);
00270         }
00271         while(err == L4_IPC_RECANCELED){
00272             err = l4_ipc_receive(flusher_thread, NULL, &dw0, &dw1,
00273                                       L4_IPC_NEVER, &result);
00274         }
00275         
00276         if(err){
00277             LOG_Error("Calling the flusher thread returned %#x.", err);
00278             return err;
00279         }
00280         return dw0;
00281     } else {
00282         return 0;
00283     }
00284 }
00285 
00286 
00287 /*!\brief flush-signaller - request flushing
00288  *
00289  * This function runs at a very low priority, and requests flushing the
00290  * buffer, if it is non-empty.
00291  *
00292  * \pre main_thread and flusher_thread must be set.
00293  */
00294 static void flush_signaller(void){
00295 
00296     /* Don't use 1 as priority for user level threads. The reason is that
00297      * current versions of Fiasco schedule all threads of each priority
00298      * level round robin. Since the kernel idle loop runs at priority 1 too,
00299      * a woken user level thread at this priority does not run until the full
00300      * timeslice of the idle loop is consumed. The kernel idle thread is
00301      * running everytime and threads which become ready are enqueued at the
00302      * end of the running queue. */
00303     rmgr_set_prio(l4_myself(), 2);
00304     while(1){
00305         if(buffer_head!=buffer_tail){
00306             flush_buffer();
00307         }
00308         l4_sleep(100);          // sleep 10 msecs
00309     }
00310 }
00311 
00312 #if CONFIG_USE_SERIAL
00313 static void serial_esc_fn(void){
00314     int err;
00315 
00316     while(1){
00317         err = l4serial_readbyte();
00318         if(err == '\e') enter_kdebug("ESC on serial");
00319     }
00320 }
00321 #endif
00322 
00323 /*!\brief Initialize the tcp-handling stuff
00324  *
00325  * This function creates the TCP/IP handling thread.
00326  * Then it waits for its startup-IPC.
00327  *
00328  * Context: Main thread.
00329  *
00330  * \return      0 on success
00331  */
00332 int flusher_init(void){
00333     l4_msgdope_t result;
00334     l4_umword_t dw0, dw1;
00335     int err;
00336 
00337     main_thread = l4_myself();
00338 
00339     /* Start the threads */
00340     err = thread_create(thread_loop, &flusher_thread, "log.flush");
00341     if(err<0){
00342         LOG_Error("Error %#x creating acceptor thread.", err);
00343         goto error;
00344     }
00345     do{
00346         err = l4_ipc_receive(flusher_thread, NULL, &dw0, &dw1,
00347                                   L4_IPC_NEVER, &result);
00348     } while(err == L4_IPC_RECANCELED);
00349     if(err){
00350         LOG_Error("Error %#x receiving up-and-running IPC from tcpip-thread.",
00351               err);
00352         goto error;
00353     }
00354     if(dw0!=0){
00355         err = dw0;
00356         goto error;
00357     }
00358 
00359 #if CONFIG_USE_SERIAL
00360     if(flush_serial){
00361         err = l4serial_init(MAXTHREADS+1, flush_serial);
00362         if(err){
00363             LOG_Error("Error %#x setting up the serial handler.", err);
00364             goto error;
00365         }
00366         if(serial_esc){
00367             l4_threadid_t dummy_t;
00368             err = thread_create(serial_esc_fn, &dummy_t, "log.serialesc");
00369             if(err<0){
00370                 LOG_Error("Error %#x creating serial-esc thread", err);
00371                 goto error;
00372             }
00373         }
00374     }
00375 #endif
00376 
00377     err = thread_create(flush_signaller, NULL, "log.flushsig");
00378     if(err){
00379         LOG_Error("Error %#x creating flush-signaller thread.", err);
00380         goto error;
00381     }
00382 
00383     return 0;
00384 
00385   error:
00386     return err;
00387 }
00388 

Generated on Wed Apr 11 06:40:52 2012 for Logging and output facility for DROPS by  doxygen 1.5.6