Opened 14 years ago
Closed 14 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 , 14 years ago
| Component: | helenos/fs/mfs → helenos/uspace/libc |
|---|---|
| Owner: | set to |
comment:2 by , 14 years ago
| Resolution: | → fixed |
|---|---|
| Status: | new → closed |
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.

In this scenario, the while loop in fwrite() seems to be infinite because
buf_freeevaluates to zero:Because of this,
nowevaluates 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;