fs/iomap/ioend.c

Source file repositories/reference/linux-study-clean/fs/iomap/ioend.c

File Facts

System
Linux kernel
Corpus path
fs/iomap/ioend.c
Extension
.c
Size
15153 bytes
Lines
513
Domain
Core OS
Bucket
VFS And Filesystem Core
Inferred role
Core OS: exported/initcall integration point
Status
integration implementation candidate

Why This File Exists

Core operating-system implementation surface: boot, tasks, memory, VFS, syscall-facing interfaces, synchronization, credentials, and isolation.

Dependency Surface

Detected Declarations

Annotated Snippet

if (!bio_flagged(bio, BIO_QUIET)) {
			pr_err_ratelimited(
"%s: writeback error on inode %llu, offset %lld, sector %llu",
				inode->i_sb->s_id, inode->i_ino,
				ioend->io_offset, ioend->io_sector);
		}
	}

	/* walk all folios in bio, ending page IO on them */
	bio_for_each_folio_all(fi, bio) {
		if (ioend->io_error)
			fserror_report_io(inode, FSERR_BUFFERED_WRITE,
					  folio_pos(fi.folio) + fi.offset,
					  fi.length, ioend->io_error,
					  GFP_ATOMIC);
		iomap_finish_folio_write(inode, fi.folio, fi.length);
		folio_count++;
	}

	if (bio_integrity(bio))
		fs_bio_integrity_free(bio);
	bio_put(bio);	/* frees the ioend */
	return folio_count;
}

static DEFINE_SPINLOCK(failed_ioend_lock);
static LIST_HEAD(failed_ioend_list);

static void
iomap_fail_ioends(
	struct work_struct	*work)
{
	struct iomap_ioend	*ioend;
	struct list_head	tmp;
	unsigned long		flags;

	spin_lock_irqsave(&failed_ioend_lock, flags);
	list_replace_init(&failed_ioend_list, &tmp);
	spin_unlock_irqrestore(&failed_ioend_lock, flags);

	while ((ioend = list_first_entry_or_null(&tmp, struct iomap_ioend,
			io_list))) {
		list_del_init(&ioend->io_list);
		iomap_finish_ioend_buffered_write(ioend);
		cond_resched();
	}
}

static DECLARE_WORK(failed_ioend_work, iomap_fail_ioends);

static void iomap_fail_ioend_buffered(struct iomap_ioend *ioend)
{
	unsigned long flags;

	/*
	 * Bounce I/O errors to a workqueue to avoid nested i_lock acquisitions
	 * in the fserror code.  The caller no longer owns the ioend reference
	 * after the spinlock drops.
	 */
	spin_lock_irqsave(&failed_ioend_lock, flags);
	if (list_empty(&failed_ioend_list))
		WARN_ON_ONCE(!schedule_work(&failed_ioend_work));
	list_add_tail(&ioend->io_list, &failed_ioend_list);
	spin_unlock_irqrestore(&failed_ioend_lock, flags);
}

static void ioend_writeback_end_bio(struct bio *bio)
{
	struct iomap_ioend *ioend = iomap_ioend_from_bio(bio);

	ioend->io_error = blk_status_to_errno(bio->bi_status);
	if (ioend->io_error) {
		iomap_fail_ioend_buffered(ioend);
		return;
	}

	iomap_finish_ioend_buffered_write(ioend);
}

/*
 * We cannot cancel the ioend directly in case of an error, so call the bio end
 * I/O handler with the error status here to run the normal I/O completion
 * handler.
 */
int iomap_ioend_writeback_submit(struct iomap_writepage_ctx *wpc, int error)
{
	struct iomap_ioend *ioend = wpc->wb_ctx;

	if (!ioend->io_bio.bi_end_io)
		ioend->io_bio.bi_end_io = ioend_writeback_end_bio;

Annotation

Implementation Notes