I think bug #249340 may be related to this one. I've just run the same version of localedef on 2.6.22-14-generic, where it runs fine, and on 2.6.22-15-generic, where it hangs. Here's the output of "strace localedef" immediately before it hung: 1218150691.384821 creat("/usr/lib/locale/en_NZ.utf8/LC_CTYPE", 0666) = 3 1218150691.403450 writev(3, [{"\x15\x11\x03\x20\x58\x00\x00\x00", 8}, {"\x68\x01\x00\x00\x68\x04\x00\x00\x68\x0a\x00\x00\x68\x0a"..., 352}, {"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"..., 768}, {"\x80\x00\x00\x00\x81\x00\x00\x00\x82\x00\x00\x00\x83\x00"..., 1536}, {NULL, 0}, {"\x80\x00\x00\x00\x81\x00\x00\x00\x82\x00\x00\x00\x83\x00"..., 1536}, {NULL, 0}, {"\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00"..., 1024}, {NULL, 0}, {NULL, 0}, {NULL, 0}, {NULL, 0}, {"\x75\x70\x70\x65\x72\x00", 6}, {"\x6c\x6f\x77\x65\x72\x00", 6}, {"\x61\x6c\x70\x68\x61\x00", 6}, {"\x64\x69\x67\x69\x74\x00", 6}, {"\x78\x64\x69\x67\x69\x74\x00", 7}, {"\x73\x70\x61\x63\x65\x00", 6}, {"\x70\x72\x69\x6e\x74\x00", 6}, {"\x67\x72\x61\x70\x68\x00", 6}, {"\x62\x6c\x61\x6e\x6b\x00", 6}, {"\x63\x6e\x74\x72\x6c\x00", 6}, {"\x70\x75\x6e\x63\x74\x00", 6}, {"\x61\x6c\x6e\x75\x6d\x00", 6}, {"\x63\x6f\x6d\x62\x69\x6e\x69\x6e\x67\x00", 10}, {"\x63\x6f\x6d\x62\x69\x6e\x69\x6e\x67\x5f\x6c\x65\x76\x65"..., 17}, {"\x00\x00\x00\x00", 4}, {"\x74\x6f\x75\x70\x70\x65\x72\x00", 8}, {"\x74\x6f\x6c\x6f\x77\x65\x72\x00", 8}, {"\x74\x6f\x74\x69\x74\x6c\x65\x00", 8}, {"\x00\x00\x00\x00", 4}, {"\x10\x00\x00\x00\x11\x00\x00\x00\x07\x00\x00\x00\xff\x01"..., 25560}, ...], 125 (Note the "{NULL, 0}" elements.) Here's the relevant part of the diff for mm/file: --- linux-source-2.6.22-2.6.22.orig/mm/filemap.c +++ linux-source-2.6.22-2.6.22/mm/filemap.c @@ -2146,22 +2146,9 @@ } status = a_ops->prepare_write(file, page, offset, offset+bytes); - if (unlikely(status)) { - loff_t isize = i_size_read(inode); + if (unlikely(status)) + goto fs_write_aop_error; - if (status != AOP_TRUNCATED_PAGE) - unlock_page(page); - page_cache_release(page); - if (status == AOP_TRUNCATED_PAGE) - continue; - /* - * prepare_write() may have instantiated a few blocks - * outside i_size. Trim these off again. - */ - if (pos + bytes > isize) - vmtruncate(inode, isize); - break; - } if (likely(nr_segs == 1)) copied = filemap_copy_from_user(page, offset, buf, bytes); @@ -2170,41 +2157,54 @@ cur_iov, iov_base, bytes); flush_dcache_page(page); status = a_ops->commit_write(file, page, offset, offset+bytes); - if (status == AOP_TRUNCATED_PAGE) { - page_cache_release(page); - continue; + if (unlikely(status < 0 || status == AOP_TRUNCATED_PAGE)) + goto fs_write_aop_error; + if (unlikely(copied != bytes)) { + status = -EFAULT; + goto fs_write_aop_error; } zero_length_segment: - if (likely(copied >= 0)) { - if (!status) - status = copied; - - if (status >= 0) { - written += status; - count -= status; - pos += status; - buf += status; - if (unlikely(nr_segs > 1)) { - filemap_set_next_iovec(&cur_iov, - &iov_base, status); - if (count) - buf = cur_iov->iov_base + - iov_base; - } else { - iov_base += status; - } + if (unlikely(status > 0)) /* filesystem did partial write */ + copied = status; + + if (likely(copied > 0)) { + written += copied; + count -= copied; + pos += copied; + buf += copied; + if (unlikely(nr_segs > 1)) { + filemap_set_next_iovec(&cur_iov, + &iov_base, copied); + if (count) + buf = cur_iov->iov_base + iov_base; + } else { + iov_base += copied; } } - if (unlikely(copied != bytes)) - if (status >= 0) - status = -EFAULT; unlock_page(page); mark_page_accessed(page); page_cache_release(page); - if (status < 0) - break; balance_dirty_pages_ratelimited(mapping); cond_resched(); + continue; + +fs_write_aop_error: + if (status != AOP_TRUNCATED_PAGE) + unlock_page(page); + page_cache_release(page); + + /* + * prepare_write() may have instantiated a few blocks + * outside i_size. Trim these off again. Don't need + * i_size_read because we hold i_mutex. + */ + if (pos + bytes > inode->i_size) + vmtruncate(inode, inode->i_size); + if (status == AOP_TRUNCATED_PAGE) + continue; + else + break; + } while (count); *ppos = pos; @@ -2269,7 +2269,7 @@ if (count == 0) goto out; - err = remove_suid(file->f_path.dentry); + err = remove_suid(&file->f_path); if (err) goto out;