Opened 13 years ago

Closed 13 years ago

#374 closed defect (fixed)

Hang while unmounting dirty MFS

Reported by: Jakub Jermář Owned by: Jakub Jermář
Priority: major Milestone: 0.5.0
Component: helenos/lib/c Version: mainline
Keywords: Cc:
Blocker for: Depends on:
See also:

Description

The following leads to a possibly infinity active loop in file_bd:

/ # cd /tmp
/tmp # mfile -s 300k img
/tmp # file_bd img fd0
...
/tmp # mkmfs fd0
...
/tmp # mfs
...
/tmp # mount mfs /data fd0
/tmp # touch /data/a
/tmp # unmount /data

After the unmount command, the file system will appear hung. Looking at threads from kconsole reveals that file_bd is busy all the time. Using the btrace command shows that it loops somewhere inside fwrite().

Change History (2)

comment:1 by Jakub Jermář, 13 years ago

Component: helenos/fs/mfshelenos/uspace/libc
Owner: set to Jakub Jermář

In this scenario, the while loop in fwrite() seems to be infinite because buf_free evaluates to zero:

                buf_free = stream->buf_size - (stream->buf_head - stream->buf);

Because of this, now evaluates to zero too:

                if (bytes_left > buf_free)
                        now = buf_free;
                else
                        now = bytes_left;

The function is thus not making any forward progress:

                data += now;
                stream->buf_head += now;
                buf_free -= now;
                bytes_left -= now;
                total_written += now;
                stream->buf_state = _bs_write;

                if (buf_free == 0) {
                        /* Only need to drain buffer. */
                        _fflushbuf(stream);
                        need_flush = false;
                }

_fflushbuf() does not change the configuration either:

        bytes_used = stream->buf_head - stream->buf_tail;
        if (bytes_used == 0)
                return;

comment:2 by Jiri Svoboda, 13 years ago

Resolution: fixed
Status: newclosed

Fixed in mainline,1226.

bytes_used is computed as the distance between head and tail, buf_free is the distance of the head from the end of the buffer (tail is not used). fwrite() relies on _fflushbuf() to reset head and tail pointers.

When doing a read() of size equal to buffer size, first head is moved to the end of the buffer when the buffer is filled. Then tail is moved to the end of the buffer when the data is copied out to the client. Thus we have bytes_used == 0 since head == tail. We have buf_free == 0 since head is at the end of the buffer. _fflusbuf() didn't do anything when bytes_used == 0, hence the infinite loop.

When bytes_used == 0 _fflush() needs to reset the head and tail pointers to the beginning of the buffer just like in the general case.

Note: See TracTickets for help on using tickets.