]> git.kaiwu.me - haproxy.git/commitdiff
[MEDIUM] fix stats socket limitation to 16 kB
authorWilly Tarreau <w@1wt.eu>
Mon, 17 Mar 2008 20:38:24 +0000 (21:38 +0100)
committerWilly Tarreau <w@1wt.eu>
Mon, 17 Mar 2008 20:43:47 +0000 (21:43 +0100)
Due to the way the stats socket work, it was not possible to
maintain the information related to the command entered, so
after filling a whole buffer, the request was lost and it was
considered that there was nothing to write anymore.

The major reason was that some flags were passed directly
during the first call to stats_dump_raw() instead of being
stored persistently in the session.

To definitely fix this problem, flags were added to the stats
member of the session structure.

A second problem appeared. When the stats were produced, a first
call to client_retnclose() was performed, then one or multiple
subsequent calls to buffer_write_chunks() were done. But once the
stats buffer was full and a reschedule operated, the buffer was
flushed, the write flag cleared from the buffer and nothing was
done to re-arm it.

For this reason, a check was added in the proto_uxst_stats()
function in order to re-call the client FSM when data were added
by stats_dump_raw(). Finally, the whole unix stats dump FSM was
rewritten to avoid all the magics it depended on. It is now
simpler and looks more like the HTTP one.

include/proto/dumpstats.h
include/types/session.h
src/dumpstats.c
src/proto_http.c
src/proto_uxst.c

index 198f0d793492ccac7ba8b58b1dd99302684bb0cf..841ddb961ba81a6e2bfe40d85c228667950790a1 100644 (file)
 #include <types/buffers.h>
 #include <types/session.h>
 
-#define STAT_FMT_HTML  0x1
-#define STAT_SHOW_STAT 0x2
-#define STAT_SHOW_INFO 0x4
+/* Flags for session->data_ctx.stats.flags */
+#define STAT_FMT_CSV    0x00000001     /* dump the stats in CSV format instead of HTML */
+#define STAT_SHOW_STAT  0x00000002     /* dump the stats part */
+#define STAT_SHOW_INFO  0x00000004     /* dump the info part */
+#define STAT_HIDE_DOWN  0x00000008     /* hide 'down' servers in the stats page */
+#define STAT_NO_REFRESH 0x00000010     /* do not automatically refresh the stats page */
 
 int stats_parse_global(const char **args, char *err, int errlen);
-int stats_dump_raw(struct session *s, struct uri_auth *uri, int flags);
-int stats_dump_http(struct session *s, struct uri_auth *uri, int flags);
-int stats_dump_proxy(struct session *s, struct proxy *px, struct uri_auth *uri, int flags);
+int stats_dump_raw(struct session *s, struct uri_auth *uri);
+int stats_dump_http(struct session *s, struct uri_auth *uri);
+int stats_dump_proxy(struct session *s, struct proxy *px, struct uri_auth *uri);
 
 
 #endif /* _PROTO_DUMPSTATS_H */
index 347bbaa8b7bd9312a44e8c9d4dc861adabb88348..691ccee91309c6edc2a26a9c2452a2998c340967 100644 (file)
@@ -2,7 +2,7 @@
   include/types/session.h
   This file defines everything related to sessions.
 
-  Copyright (C) 2000-2007 Willy Tarreau - w@1wt.eu
+  Copyright (C) 2000-2008 Willy Tarreau - w@1wt.eu
   
   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
 #define        SN_FINST_SHIFT  12              /* bit shift */
 /* unused:              0x00008000 */
 
-/* Note: those flags must move to another place */
-#define SN_STAT_HIDEDWN        0x00010000      /* hide 'down' servers in the stats page */
-#define SN_STAT_NORFRSH        0x00020000      /* do not automatically refresh the stats page */
-#define SN_STAT_FMTCSV 0x00040000      /* dump the stats in CSV format instead of HTML */
-
-
 /* WARNING: if new fields are added, they must be initialized in event_accept()
  * and freed in session_free() !
  */
@@ -122,6 +116,7 @@ struct session {
                        struct proxy *px;
                        struct server *sv;
                        short px_st, sv_st;     /* DATA_ST_INIT or DATA_ST_DATA */
+                       unsigned int flags;     /* STAT_* */
                } stats;
        } data_ctx;                             /* used by produce_content to dump the stats right now */
        unsigned int uniq_id;                   /* unique ID used for the traces */
index a0e80dfab8e955d78997c60b03272f66636a2d24..8f5f17ea184084976033690fc0adbbb9c5832c6f 100644 (file)
@@ -177,12 +177,12 @@ int print_csv_header(struct chunk *msg, int size)
 
 /*
  * Produces statistics data for the session <s>. Expects to be called with
- * s->cli_state == CL_STSHUTR. It *may* make use of informations from <uri>
- * and <flags>.
+ * s->cli_state == CL_STSHUTR. It *may* make use of informations from <uri>.
+ * s->data_ctx must have been zeroed first, and the flags properly set.
  * It returns 0 if it had to stop writing data and an I/O is needed, 1 if the
  * dump is finished and the session must be closed, or -1 in case of any error.
  */
-int stats_dump_raw(struct session *s, struct uri_auth *uri, int flags)
+int stats_dump_raw(struct session *s, struct uri_auth *uri)
 {
        struct buffer *rep = s->rep;
        struct proxy *px;
@@ -202,7 +202,7 @@ int stats_dump_raw(struct session *s, struct uri_auth *uri, int flags)
                /* fall through */
 
        case DATA_ST_HEAD:
-               if (flags & STAT_SHOW_STAT) {
+               if (s->data_ctx.stats.flags & STAT_SHOW_STAT) {
                        print_csv_header(&msg, sizeof(trash));
                        if (buffer_write_chunk(rep, &msg) != 0)
                                return 0;
@@ -213,9 +213,7 @@ int stats_dump_raw(struct session *s, struct uri_auth *uri, int flags)
 
        case DATA_ST_INFO:
                up = (now.tv_sec - start_date.tv_sec);
-               memset(&s->data_ctx, 0, sizeof(s->data_ctx));
-
-               if (flags & STAT_SHOW_INFO) {
+               if (s->data_ctx.stats.flags & STAT_SHOW_INFO) {
                        chunk_printf(&msg, sizeof(trash),
                                     "Name: " PRODUCT_NAME "\n"
                                     "Version: " HAPROXY_VERSION "\n"
@@ -253,13 +251,13 @@ int stats_dump_raw(struct session *s, struct uri_auth *uri, int flags)
 
        case DATA_ST_LIST:
                /* dump proxies */
-               if (flags & STAT_SHOW_STAT) {
+               if (s->data_ctx.stats.flags & STAT_SHOW_STAT) {
                        while (s->data_ctx.stats.px) {
                                px = s->data_ctx.stats.px;
                                /* skip the disabled proxies and non-networked ones */
                                if (px->state != PR_STSTOPPED &&
                                    (px->cap & (PR_CAP_FE | PR_CAP_BE)))
-                                       if (stats_dump_proxy(s, px, NULL, 0) == 0)
+                                       if (stats_dump_proxy(s, px, NULL) == 0)
                                                return 0;
 
                                s->data_ctx.stats.px = px->next;
@@ -290,10 +288,11 @@ int stats_dump_raw(struct session *s, struct uri_auth *uri, int flags)
  * s->cli_state == CL_STSHUTR. It stops by itself by unsetting the SN_SELF_GEN
  * flag from the session, which it uses to keep on being called when there is
  * free space in the buffer, of simply by letting an empty buffer upon return.
+ * s->data_ctx must have been zeroed before the first call, and the flags set.
  * It returns 0 if it had to stop writing data and an I/O is needed, 1 if the
  * dump is finished and the session must be closed, or -1 in case of any error.
  */
-int stats_dump_http(struct session *s, struct uri_auth *uri, int flags)
+int stats_dump_http(struct session *s, struct uri_auth *uri)
 {
        struct buffer *rep = s->rep;
        struct proxy *px;
@@ -313,9 +312,9 @@ int stats_dump_http(struct session *s, struct uri_auth *uri, int flags)
                             "Cache-Control: no-cache\r\n"
                             "Connection: close\r\n"
                             "Content-Type: %s\r\n",
-                            (flags & STAT_FMT_HTML) ? "text/html" : "text/plain");
+                            (s->data_ctx.stats.flags & STAT_FMT_CSV) ? "text/plain" : "text/html");
 
-               if (uri->refresh > 0 && !(s->flags & SN_STAT_NORFRSH))
+               if (uri->refresh > 0 && !(s->data_ctx.stats.flags & STAT_NO_REFRESH))
                        chunk_printf(&msg, sizeof(trash), "Refresh: %d\r\n",
                                     uri->refresh);
 
@@ -341,7 +340,7 @@ int stats_dump_http(struct session *s, struct uri_auth *uri, int flags)
                /* fall through */
 
        case DATA_ST_HEAD:
-               if (flags & STAT_FMT_HTML) {
+               if (!(s->data_ctx.stats.flags & STAT_FMT_CSV)) {
                        /* WARNING! This must fit in the first buffer !!! */        
                        chunk_printf(&msg, sizeof(trash),
                             "<html><head><title>Statistics Report for " PRODUCT_NAME "</title>\n"
@@ -432,7 +431,7 @@ int stats_dump_http(struct session *s, struct uri_auth *uri, int flags)
                         * We are around 3.5 kB, add adding entries will
                         * become tricky if we want to support 4kB buffers !
                         */
-               if (flags & STAT_FMT_HTML) {
+               if (!(s->data_ctx.stats.flags & STAT_FMT_CSV)) {
                        chunk_printf(&msg, sizeof(trash),
                             "<body><h1><a href=\"" PRODUCT_URL "\" style=\"text-decoration: none;\">"
                             PRODUCT_NAME "%s</a></h1>\n"
@@ -477,39 +476,39 @@ int stats_dump_http(struct session *s, struct uri_auth *uri, int flags)
                             actconn
                             );
            
-                       if (s->flags & SN_STAT_HIDEDWN)
+                       if (s->data_ctx.stats.flags & STAT_HIDE_DOWN)
                                chunk_printf(&msg, sizeof(trash),
                                     "<li><a href=\"%s%s%s\">Show all servers</a><br>\n",
                                     uri->uri_prefix,
                                     "",
-                                    (s->flags & SN_STAT_NORFRSH) ? ";norefresh" : "");
+                                    (s->data_ctx.stats.flags & STAT_NO_REFRESH) ? ";norefresh" : "");
                        else
                                chunk_printf(&msg, sizeof(trash),
                                     "<li><a href=\"%s%s%s\">Hide 'DOWN' servers</a><br>\n",
                                     uri->uri_prefix,
                                     ";up",
-                                    (s->flags & SN_STAT_NORFRSH) ? ";norefresh" : "");
+                                    (s->data_ctx.stats.flags & STAT_NO_REFRESH) ? ";norefresh" : "");
 
                        if (uri->refresh > 0) {
-                               if (s->flags & SN_STAT_NORFRSH)
+                               if (s->data_ctx.stats.flags & STAT_NO_REFRESH)
                                        chunk_printf(&msg, sizeof(trash),
                                             "<li><a href=\"%s%s%s\">Enable refresh</a><br>\n",
                                             uri->uri_prefix,
-                                            (s->flags & SN_STAT_HIDEDWN) ? ";up" : "",
+                                            (s->data_ctx.stats.flags & STAT_HIDE_DOWN) ? ";up" : "",
                                             "");
                                else
                                        chunk_printf(&msg, sizeof(trash),
                                             "<li><a href=\"%s%s%s\">Disable refresh</a><br>\n",
                                             uri->uri_prefix,
-                                            (s->flags & SN_STAT_HIDEDWN) ? ";up" : "",
+                                            (s->data_ctx.stats.flags & STAT_HIDE_DOWN) ? ";up" : "",
                                             ";norefresh");
                        }
 
                        chunk_printf(&msg, sizeof(trash),
                             "<li><a href=\"%s%s%s\">Refresh now</a><br>\n",
                             uri->uri_prefix,
-                            (s->flags & SN_STAT_HIDEDWN) ? ";up" : "",
-                            (s->flags & SN_STAT_NORFRSH) ? ";norefresh" : "");
+                            (s->data_ctx.stats.flags & STAT_HIDE_DOWN) ? ";up" : "",
+                            (s->data_ctx.stats.flags & STAT_NO_REFRESH) ? ";norefresh" : "");
 
                        chunk_printf(&msg, sizeof(trash),
                             "<li><a href=\"%s;csv%s\">CSV export</a><br>\n",
@@ -533,8 +532,6 @@ int stats_dump_http(struct session *s, struct uri_auth *uri, int flags)
                                return 0;
                }
 
-               memset(&s->data_ctx, 0, sizeof(s->data_ctx));
-
                s->data_ctx.stats.px = proxy;
                s->data_ctx.stats.px_st = DATA_ST_PX_INIT;
                s->data_state = DATA_ST_LIST;
@@ -546,7 +543,7 @@ int stats_dump_http(struct session *s, struct uri_auth *uri, int flags)
                        px = s->data_ctx.stats.px;
                        /* skip the disabled proxies and non-networked ones */
                        if (px->state != PR_STSTOPPED && (px->cap & (PR_CAP_FE | PR_CAP_BE)))
-                               if (stats_dump_proxy(s, px, uri, flags) == 0)
+                               if (stats_dump_proxy(s, px, uri) == 0)
                                        return 0;
 
                        s->data_ctx.stats.px = px->next;
@@ -558,7 +555,7 @@ int stats_dump_http(struct session *s, struct uri_auth *uri, int flags)
                /* fall through */
 
        case DATA_ST_END:
-               if (flags & STAT_FMT_HTML) {
+               if (!(s->data_ctx.stats.flags & STAT_FMT_CSV)) {
                        chunk_printf(&msg, sizeof(trash), "</body></html>\n");
                        if (buffer_write_chunk(rep, &msg) != 0)
                                return 0;
@@ -584,7 +581,7 @@ int stats_dump_http(struct session *s, struct uri_auth *uri, int flags)
  * Returns 0 if it had to stop dumping data because of lack of buffer space,
  * ot non-zero if everything completed.
  */
-int stats_dump_proxy(struct session *s, struct proxy *px, struct uri_auth *uri, int flags)
+int stats_dump_proxy(struct session *s, struct proxy *px, struct uri_auth *uri)
 {
        struct buffer *rep = s->rep;
        struct server *sv;
@@ -625,7 +622,7 @@ int stats_dump_proxy(struct session *s, struct proxy *px, struct uri_auth *uri,
                /* fall through */
 
        case DATA_ST_PX_TH:
-               if (flags & STAT_FMT_HTML) {
+               if (!(s->data_ctx.stats.flags & STAT_FMT_CSV)) {
                        /* print a new table */
                        chunk_printf(&msg, sizeof(trash),
                                     "<table cols=\"26\" class=\"tbl\" width=\"100%%\">\n"
@@ -661,7 +658,7 @@ int stats_dump_proxy(struct session *s, struct proxy *px, struct uri_auth *uri,
        case DATA_ST_PX_FE:
                /* print the frontend */
                if (px->cap & PR_CAP_FE) {
-                       if (flags & STAT_FMT_HTML) {
+                       if (!(s->data_ctx.stats.flags & STAT_FMT_CSV)) {
                                chunk_printf(&msg, sizeof(trash),
                                     /* name, queue */
                                     "<tr align=center class=\"frontend\"><td>Frontend</td><td colspan=3></td>"
@@ -752,13 +749,13 @@ int stats_dump_proxy(struct session *s, struct proxy *px, struct uri_auth *uri,
                                else
                                        sv_state = 0; /* DOWN */
 
-                       if ((sv_state == 0) && (s->flags & SN_STAT_HIDEDWN)) {
+                       if ((sv_state == 0) && (s->data_ctx.stats.flags & STAT_HIDE_DOWN)) {
                                /* do not report servers which are DOWN */
                                s->data_ctx.stats.sv = sv->next;
                                continue;
                        }
 
-                       if (flags & STAT_FMT_HTML) {
+                       if (!(s->data_ctx.stats.flags & STAT_FMT_CSV)) {
                                static char *srv_hlt_st[7] = { "DOWN", "DN %d/%d &uarr;",
                                                               "UP %d/%d &darr;", "UP",
                                                               "NOLB %d/%d &darr;", "NOLB",
@@ -923,7 +920,7 @@ int stats_dump_proxy(struct session *s, struct proxy *px, struct uri_auth *uri,
        case DATA_ST_PX_BE:
                /* print the backend */
                if (px->cap & PR_CAP_BE) {
-                       if (flags & STAT_FMT_HTML) {
+                       if (!(s->data_ctx.stats.flags & STAT_FMT_CSV)) {
                                chunk_printf(&msg, sizeof(trash),
                                     /* name */
                                     "<tr align=center class=\"backend\"><td>Backend</td>"
@@ -1017,7 +1014,7 @@ int stats_dump_proxy(struct session *s, struct proxy *px, struct uri_auth *uri,
                /* fall through */
 
        case DATA_ST_PX_END:
-               if (flags & STAT_FMT_HTML) {
+               if (!(s->data_ctx.stats.flags & STAT_FMT_CSV)) {
                        chunk_printf(&msg, sizeof(trash), "</table><p>\n");
 
                        if (buffer_write_chunk(rep, &msg) != 0)
index c01c90aab76e632ab01b9a92f1fcfc03fe8fd740..2d28737c9272f49d1e031800aaeeacb6feab123d 100644 (file)
@@ -3408,8 +3408,7 @@ int produce_content(struct session *s)
        }
        else if (s->data_source == DATA_SRC_STATS) {
                /* dump server statistics */
-               int ret = stats_dump_http(s, s->be->uri_auth,
-                                         (s->flags & SN_STAT_FMTCSV) ? 0 : STAT_FMT_HTML);
+               int ret = stats_dump_http(s, s->be->uri_auth);
                if (ret >= 0)
                        return ret;
                /* -1 indicates an error */
@@ -4679,6 +4678,8 @@ int stats_check_uri_auth(struct session *t, struct proxy *backend)
        int authenticated, cur_idx;
        char *h;
 
+       memset(&t->data_ctx.stats, 0, sizeof(t->data_ctx.stats));
+
        /* check URI size */
        if (uri_auth->uri_len > txn->req.sl.rq.u_l)
                return 0;
@@ -4692,7 +4693,7 @@ int stats_check_uri_auth(struct session *t, struct proxy *backend)
        h += uri_auth->uri_len;
        while (h <= t->req->data + txn->req.sl.rq.u + txn->req.sl.rq.u_l - 3) {
                if (memcmp(h, ";up", 3) == 0) {
-                       t->flags |= SN_STAT_HIDEDWN;
+                       t->data_ctx.stats.flags |= STAT_HIDE_DOWN;
                        break;
                }
                h++;
@@ -4702,7 +4703,7 @@ int stats_check_uri_auth(struct session *t, struct proxy *backend)
                h = t->req->data + txn->req.sl.rq.u + uri_auth->uri_len;
                while (h <= t->req->data + txn->req.sl.rq.u + txn->req.sl.rq.u_l - 10) {
                        if (memcmp(h, ";norefresh", 10) == 0) {
-                               t->flags |= SN_STAT_NORFRSH;
+                               t->data_ctx.stats.flags |= STAT_NO_REFRESH;
                                break;
                        }
                        h++;
@@ -4712,12 +4713,14 @@ int stats_check_uri_auth(struct session *t, struct proxy *backend)
        h = t->req->data + txn->req.sl.rq.u + uri_auth->uri_len;
        while (h <= t->req->data + txn->req.sl.rq.u + txn->req.sl.rq.u_l - 4) {
                if (memcmp(h, ";csv", 4) == 0) {
-                       t->flags |= SN_STAT_FMTCSV;
+                       t->data_ctx.stats.flags |= STAT_FMT_CSV;
                        break;
                }
                h++;
        }
 
+       t->data_ctx.stats.flags |= STAT_SHOW_STAT | STAT_SHOW_INFO;
+
        /* we are in front of a interceptable URI. Let's check
         * if there's an authentication and if it's valid.
         */
@@ -4776,7 +4779,7 @@ int stats_check_uri_auth(struct session *t, struct proxy *backend)
                return 1;
        }
 
-       /* The request is valid, the user is authenticate. Let's start sending
+       /* The request is valid, the user is authenticated. Let's start sending
         * data.
         */
        t->cli_state = CL_STSHUTR;
index 90547226889cc0c13dbea3d6c80f4a5d8fd3da70..870347661aaf2c6ce76ac550bf69f64b481c9452 100644 (file)
@@ -1354,67 +1354,69 @@ void process_uxst_session(struct task *t, struct timeval *next)
 /* Processes data exchanges on the statistics socket. The client processing
  * is called and the task is put back in the wait queue or it is cleared.
  * In order to ease the transition, we simply simulate the server status
- * for now. It only knows states SV_STIDLE, SV_STDATA and SV_STCLOSE. Returns
- * in <next> the task's expiration date.
+ * for now. It only knows states SV_STIDLE, SV_STCONN, SV_STDATA, and
+ * SV_STCLOSE. Returns in <next> the task's expiration date.
  */
 void process_uxst_stats(struct task *t, struct timeval *next)
 {
        struct session *s = t->context;
        struct listener *listener;
        int fsm_resync = 0;
+       int last_rep_l;
 
-       /* we need to be in DATA phase on the "server" side */
-       if (s->srv_state == SV_STIDLE) {
-               s->srv_state = SV_STDATA;
-               s->data_source = DATA_SRC_STATS;
-       }
-                       
        do {
                fsm_resync = process_uxst_cli(s);
-               if (s->srv_state != SV_STDATA)
-                       continue;
 
                if (s->cli_state == CL_STCLOSE || s->cli_state == CL_STSHUTW) {
                        s->srv_state = SV_STCLOSE;
-                       fsm_resync |= 1;
-                       continue;
+                       break;
                }
 
-               if (s->data_state == DATA_ST_INIT) {
+               switch (s->srv_state) {
+               case SV_STIDLE:
+                       /* stats output not initialized yet */
+                       memset(&s->data_ctx.stats, 0, sizeof(s->data_ctx.stats));
+                       s->data_source = DATA_SRC_STATS;
+                       s->srv_state = SV_STCONN;
+                       fsm_resync |= 1;
+                       break;
+
+               case SV_STCONN: /* will change to SV_STANALYZE */
+                       /* stats initialized, but waiting for the command */
                        if ((s->req->l >= 10) && (memcmp(s->req->data, "show stat\n", 10) == 0)) {
                                /* send the stats, and changes the data_state */
-                               if (stats_dump_raw(s, NULL, STAT_SHOW_STAT) != 0) {
-                                       s->srv_state = SV_STCLOSE;
-                                       fsm_resync |= 1;
-                                       continue;
-                               }
+                               s->data_ctx.stats.flags |= STAT_SHOW_STAT;
+                               s->data_ctx.stats.flags |= STAT_FMT_CSV;
+                               s->srv_state = SV_STDATA;
+                               fsm_resync |= 1;
                        }
-                       if ((s->req->l >= 10) && (memcmp(s->req->data, "show info\n", 10) == 0)) {
+                       else if ((s->req->l >= 10) && (memcmp(s->req->data, "show info\n", 10) == 0)) {
                                /* send the stats, and changes the data_state */
-                               if (stats_dump_raw(s, NULL, STAT_SHOW_INFO) != 0) {
-                                       s->srv_state = SV_STCLOSE;
-                                       fsm_resync |= 1;
-                                       continue;
-                               }
+                               s->data_ctx.stats.flags |= STAT_SHOW_INFO;
+                               s->data_ctx.stats.flags |= STAT_FMT_CSV;
+                               s->srv_state = SV_STDATA;
+                               fsm_resync |= 1;
                        }
                        else if (s->cli_state == CL_STSHUTR || (s->req->l >= s->req->rlim - s->req->data)) {
                                s->srv_state = SV_STCLOSE;
                                fsm_resync |= 1;
-                               continue;
                        }
-               }
+                       break;
 
-               if (s->data_state == DATA_ST_INIT)
-                       continue;
-
-               /* OK we have some remaining data to process. Just for the
-                * sake of an exercice, we copy the req into the resp,
-                * and flush the req. This produces a simple echo function.
-                */
-               if (stats_dump_raw(s, NULL, 0) != 0) {
-                       s->srv_state = SV_STCLOSE;
-                       fsm_resync |= 1;
-                       continue;
+               case SV_STDATA:
+                       /* OK we have to process the request. Since it is possible
+                        * that we get there with the client output paused, we
+                        * will simply check that we have really sent some data
+                        * and wake the client up if needed.
+                        */
+                       last_rep_l = s->rep->l;
+                       if (stats_dump_raw(s, NULL) != 0) {
+                               s->srv_state = SV_STCLOSE;
+                               fsm_resync |= 1;
+                       }
+                       if (s->rep->l != last_rep_l)
+                               fsm_resync |= 1;
+                       break;
                }
        } while (fsm_resync);