failed:
list_for_each_entry(segbuf, &list, sb_list) {
ret = nilfs_sufile_free(sufile, segbuf->sb_nextnum);
- WARN_ON(ret); /* never fails */
+ nilfs_sufile_warn_on_error(sufile, ret);
}
nilfs_destroy_logs(&list);
return err;
segbuf = NILFS_FIRST_SEGBUF(logs);
if (nilfs->ns_nextnum != segbuf->sb_nextnum) {
ret = nilfs_sufile_free(sufile, segbuf->sb_nextnum);
- WARN_ON(ret); /* never fails */
+ nilfs_sufile_warn_on_error(sufile, ret);
}
if (atomic_read(&segbuf->sb_err)) {
/* Case 1: The first segment failed */
list_for_each_entry_continue(segbuf, logs, sb_list) {
if (prev->sb_nextnum != segbuf->sb_nextnum) {
ret = nilfs_sufile_free(sufile, segbuf->sb_nextnum);
- WARN_ON(ret); /* never fails */
+ nilfs_sufile_warn_on_error(sufile, ret);
}
if (atomic_read(&segbuf->sb_err) &&
segbuf->sb_segnum != nilfs->ns_nextnum)
ret = nilfs_sufile_set_segment_usage(sufile, segbuf->sb_segnum,
live_blocks,
sci->sc_seg_ctime);
- WARN_ON(ret); /* always succeed because the segusage is dirty */
+ nilfs_sufile_warn_on_error(sufile, ret);
}
}
ret = nilfs_sufile_set_segment_usage(sufile, segbuf->sb_segnum,
segbuf->sb_pseg_start -
segbuf->sb_fseg_start, 0);
- WARN_ON(ret); /* always succeed because the segusage is dirty */
+ nilfs_sufile_warn_on_error(sufile, ret);
list_for_each_entry_continue(segbuf, logs, sb_list) {
ret = nilfs_sufile_set_segment_usage(sufile, segbuf->sb_segnum,
0, 0);
- WARN_ON(ret); /* always succeed */
+ nilfs_sufile_warn_on_error(sufile, ret);
}
}
-static void nilfs_segctor_truncate_segments(struct nilfs_sc_info *sci,
- struct nilfs_segment_buffer *last,
- struct inode *sufile)
+static int nilfs_segctor_truncate_segments(struct nilfs_sc_info *sci,
+ struct nilfs_segment_buffer *last,
+ struct inode *sufile)
{
struct nilfs_segment_buffer *segbuf = last;
- int ret;
+ int ret, err = 0;
list_for_each_entry_continue(segbuf, &sci->sc_segbufs, sb_list) {
sci->sc_segbuf_nblocks -= segbuf->sb_rest_blocks;
- ret = nilfs_sufile_free(sufile, segbuf->sb_nextnum);
- WARN_ON(ret);
+
+ ret = nilfs_sufile_warn_on_error(
+ sufile, nilfs_sufile_free(sufile, segbuf->sb_nextnum));
+ if (unlikely(ret) && err != -EROFS)
+ err = ret;
}
nilfs_truncate_logs(&sci->sc_segbufs, last);
+ return err;
}
sci->sc_freesegs,
sci->sc_nfreesegs,
NULL);
- WARN_ON(err); /* do not happen */
+ nilfs_sufile_warn_on_error(nilfs->ns_sufile, err);
sci->sc_stage.flags &= ~NILFS_CF_SUFREED;
}
sci->sc_stage = prev_stage;
}
nilfs_segctor_zeropad_segsum(sci);
- nilfs_segctor_truncate_segments(sci, sci->sc_curseg, nilfs->ns_sufile);
- return 0;
+ err = nilfs_segctor_truncate_segments(sci, sci->sc_curseg,
+ nilfs->ns_sufile);
failed:
return err;
sci->sc_freesegs,
sci->sc_nfreesegs,
NULL);
- WARN_ON(ret); /* do not happen */
+ nilfs_sufile_warn_on_error(nilfs->ns_sufile, ret);
}
nilfs_destroy_logs(&logs);
#ifndef _NILFS_SUFILE_H
#define _NILFS_SUFILE_H
+#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/buffer_head.h>
#include "mdt.h"
struct nilfs_inode *raw_inode, struct inode **inodep);
int nilfs_sufile_trim_fs(struct inode *sufile, struct fstrim_range *range);
+/**
+ * nilfs_sufile_warn_on_error - warn on unexpected sufile error
+ * @sufile: inode of segment usage file
+ * @err: status code returned by a sufile function
+ *
+ * Even if buffer heads of blocks containing segment usage entries have
+ * been dirtied in advance by calling functions such as
+ * nilfs_sufile_mark_dirty() or nilfs_sufile_{alloc,free}(), those buffers
+ * can be discarded from memory after the file system detects corruption and
+ * degrades to read-only mode, which may cause sufile operations, including
+ * cancel operations, to return errors. nilfs_sufile_warn_on_error() is used
+ * to detect unexpected errors other than during read-only degradation.
+ *
+ * Return: 0 if @err is 0, %-EROFS if in read-only degraded mode, and %-EIO
+ * otherwise.
+ */
+#define nilfs_sufile_warn_on_error(sufile, err) \
+ ({ \
+ int _err = (err); \
+ \
+ if (unlikely(_err)) \
+ _err = WARN_ONCE(!sb_rdonly((sufile)->i_sb), \
+ "unexpected sufile error %d\n", _err) ? \
+ -EIO : -EROFS; \
+ _err; \
+ })
+
/**
* nilfs_sufile_scrap - make a segment garbage
* @sufile: inode of segment usage file