478 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * NILFS segment buffer4 *5 * Copyright (C) 2005-2008 Nippon Telegraph and Telephone Corporation.6 *7 * Written by Ryusuke Konishi.8 *9 */10 11#include <linux/buffer_head.h>12#include <linux/writeback.h>13#include <linux/crc32.h>14#include <linux/backing-dev.h>15#include <linux/slab.h>16#include "page.h"17#include "segbuf.h"18 19 20struct nilfs_write_info {21 struct the_nilfs *nilfs;22 struct bio *bio;23 int start, end; /* The region to be submitted */24 int rest_blocks;25 int max_pages;26 int nr_vecs;27 sector_t blocknr;28};29 30static int nilfs_segbuf_write(struct nilfs_segment_buffer *segbuf,31 struct the_nilfs *nilfs);32static int nilfs_segbuf_wait(struct nilfs_segment_buffer *segbuf);33 34struct nilfs_segment_buffer *nilfs_segbuf_new(struct super_block *sb)35{36 struct nilfs_segment_buffer *segbuf;37 38 segbuf = kmem_cache_alloc(nilfs_segbuf_cachep, GFP_NOFS);39 if (unlikely(!segbuf))40 return NULL;41 42 segbuf->sb_super = sb;43 INIT_LIST_HEAD(&segbuf->sb_list);44 INIT_LIST_HEAD(&segbuf->sb_segsum_buffers);45 INIT_LIST_HEAD(&segbuf->sb_payload_buffers);46 segbuf->sb_super_root = NULL;47 48 init_completion(&segbuf->sb_bio_event);49 atomic_set(&segbuf->sb_err, 0);50 segbuf->sb_nbio = 0;51 52 return segbuf;53}54 55void nilfs_segbuf_free(struct nilfs_segment_buffer *segbuf)56{57 kmem_cache_free(nilfs_segbuf_cachep, segbuf);58}59 60void nilfs_segbuf_map(struct nilfs_segment_buffer *segbuf, __u64 segnum,61 unsigned long offset, struct the_nilfs *nilfs)62{63 segbuf->sb_segnum = segnum;64 nilfs_get_segment_range(nilfs, segnum, &segbuf->sb_fseg_start,65 &segbuf->sb_fseg_end);66 67 segbuf->sb_pseg_start = segbuf->sb_fseg_start + offset;68 segbuf->sb_rest_blocks =69 segbuf->sb_fseg_end - segbuf->sb_pseg_start + 1;70}71 72/**73 * nilfs_segbuf_map_cont - map a new log behind a given log74 * @segbuf: new segment buffer75 * @prev: segment buffer containing a log to be continued76 */77void nilfs_segbuf_map_cont(struct nilfs_segment_buffer *segbuf,78 struct nilfs_segment_buffer *prev)79{80 segbuf->sb_segnum = prev->sb_segnum;81 segbuf->sb_fseg_start = prev->sb_fseg_start;82 segbuf->sb_fseg_end = prev->sb_fseg_end;83 segbuf->sb_pseg_start = prev->sb_pseg_start + prev->sb_sum.nblocks;84 segbuf->sb_rest_blocks =85 segbuf->sb_fseg_end - segbuf->sb_pseg_start + 1;86}87 88void nilfs_segbuf_set_next_segnum(struct nilfs_segment_buffer *segbuf,89 __u64 nextnum, struct the_nilfs *nilfs)90{91 segbuf->sb_nextnum = nextnum;92 segbuf->sb_sum.next = nilfs_get_segment_start_blocknr(nilfs, nextnum);93}94 95int nilfs_segbuf_extend_segsum(struct nilfs_segment_buffer *segbuf)96{97 struct buffer_head *bh;98 99 bh = sb_getblk(segbuf->sb_super,100 segbuf->sb_pseg_start + segbuf->sb_sum.nsumblk);101 if (unlikely(!bh))102 return -ENOMEM;103 104 lock_buffer(bh);105 if (!buffer_uptodate(bh)) {106 memset(bh->b_data, 0, bh->b_size);107 set_buffer_uptodate(bh);108 }109 unlock_buffer(bh);110 nilfs_segbuf_add_segsum_buffer(segbuf, bh);111 return 0;112}113 114int nilfs_segbuf_extend_payload(struct nilfs_segment_buffer *segbuf,115 struct buffer_head **bhp)116{117 struct buffer_head *bh;118 119 bh = sb_getblk(segbuf->sb_super,120 segbuf->sb_pseg_start + segbuf->sb_sum.nblocks);121 if (unlikely(!bh))122 return -ENOMEM;123 124 nilfs_segbuf_add_payload_buffer(segbuf, bh);125 *bhp = bh;126 return 0;127}128 129int nilfs_segbuf_reset(struct nilfs_segment_buffer *segbuf, unsigned int flags,130 time64_t ctime, __u64 cno)131{132 int err;133 134 segbuf->sb_sum.nblocks = segbuf->sb_sum.nsumblk = 0;135 err = nilfs_segbuf_extend_segsum(segbuf);136 if (unlikely(err))137 return err;138 139 segbuf->sb_sum.flags = flags;140 segbuf->sb_sum.sumbytes = sizeof(struct nilfs_segment_summary);141 segbuf->sb_sum.nfinfo = segbuf->sb_sum.nfileblk = 0;142 segbuf->sb_sum.ctime = ctime;143 segbuf->sb_sum.cno = cno;144 return 0;145}146 147/*148 * Setup segment summary149 */150void nilfs_segbuf_fill_in_segsum(struct nilfs_segment_buffer *segbuf)151{152 struct nilfs_segment_summary *raw_sum;153 struct buffer_head *bh_sum;154 155 bh_sum = list_entry(segbuf->sb_segsum_buffers.next,156 struct buffer_head, b_assoc_buffers);157 raw_sum = (struct nilfs_segment_summary *)bh_sum->b_data;158 159 raw_sum->ss_magic = cpu_to_le32(NILFS_SEGSUM_MAGIC);160 raw_sum->ss_bytes = cpu_to_le16(sizeof(*raw_sum));161 raw_sum->ss_flags = cpu_to_le16(segbuf->sb_sum.flags);162 raw_sum->ss_seq = cpu_to_le64(segbuf->sb_sum.seg_seq);163 raw_sum->ss_create = cpu_to_le64(segbuf->sb_sum.ctime);164 raw_sum->ss_next = cpu_to_le64(segbuf->sb_sum.next);165 raw_sum->ss_nblocks = cpu_to_le32(segbuf->sb_sum.nblocks);166 raw_sum->ss_nfinfo = cpu_to_le32(segbuf->sb_sum.nfinfo);167 raw_sum->ss_sumbytes = cpu_to_le32(segbuf->sb_sum.sumbytes);168 raw_sum->ss_pad = 0;169 raw_sum->ss_cno = cpu_to_le64(segbuf->sb_sum.cno);170}171 172/*173 * CRC calculation routines174 */175static void176nilfs_segbuf_fill_in_segsum_crc(struct nilfs_segment_buffer *segbuf, u32 seed)177{178 struct buffer_head *bh;179 struct nilfs_segment_summary *raw_sum;180 unsigned long size, bytes = segbuf->sb_sum.sumbytes;181 u32 crc;182 183 bh = list_entry(segbuf->sb_segsum_buffers.next, struct buffer_head,184 b_assoc_buffers);185 186 raw_sum = (struct nilfs_segment_summary *)bh->b_data;187 size = min_t(unsigned long, bytes, bh->b_size);188 crc = crc32_le(seed,189 (unsigned char *)raw_sum +190 sizeof(raw_sum->ss_datasum) + sizeof(raw_sum->ss_sumsum),191 size - (sizeof(raw_sum->ss_datasum) +192 sizeof(raw_sum->ss_sumsum)));193 194 list_for_each_entry_continue(bh, &segbuf->sb_segsum_buffers,195 b_assoc_buffers) {196 bytes -= size;197 size = min_t(unsigned long, bytes, bh->b_size);198 crc = crc32_le(crc, bh->b_data, size);199 }200 raw_sum->ss_sumsum = cpu_to_le32(crc);201}202 203static void nilfs_segbuf_fill_in_data_crc(struct nilfs_segment_buffer *segbuf,204 u32 seed)205{206 struct buffer_head *bh;207 struct nilfs_segment_summary *raw_sum;208 void *kaddr;209 u32 crc;210 211 bh = list_entry(segbuf->sb_segsum_buffers.next, struct buffer_head,212 b_assoc_buffers);213 raw_sum = (struct nilfs_segment_summary *)bh->b_data;214 crc = crc32_le(seed,215 (unsigned char *)raw_sum + sizeof(raw_sum->ss_datasum),216 bh->b_size - sizeof(raw_sum->ss_datasum));217 218 list_for_each_entry_continue(bh, &segbuf->sb_segsum_buffers,219 b_assoc_buffers) {220 crc = crc32_le(crc, bh->b_data, bh->b_size);221 }222 list_for_each_entry(bh, &segbuf->sb_payload_buffers, b_assoc_buffers) {223 kaddr = kmap_local_page(bh->b_page);224 crc = crc32_le(crc, kaddr + bh_offset(bh), bh->b_size);225 kunmap_local(kaddr);226 }227 raw_sum->ss_datasum = cpu_to_le32(crc);228}229 230static void231nilfs_segbuf_fill_in_super_root_crc(struct nilfs_segment_buffer *segbuf,232 u32 seed)233{234 struct nilfs_super_root *raw_sr;235 struct the_nilfs *nilfs = segbuf->sb_super->s_fs_info;236 unsigned int srsize;237 u32 crc;238 239 raw_sr = (struct nilfs_super_root *)segbuf->sb_super_root->b_data;240 srsize = NILFS_SR_BYTES(nilfs->ns_inode_size);241 crc = crc32_le(seed,242 (unsigned char *)raw_sr + sizeof(raw_sr->sr_sum),243 srsize - sizeof(raw_sr->sr_sum));244 raw_sr->sr_sum = cpu_to_le32(crc);245}246 247static void nilfs_release_buffers(struct list_head *list)248{249 struct buffer_head *bh, *n;250 251 list_for_each_entry_safe(bh, n, list, b_assoc_buffers) {252 list_del_init(&bh->b_assoc_buffers);253 brelse(bh);254 }255}256 257static void nilfs_segbuf_clear(struct nilfs_segment_buffer *segbuf)258{259 nilfs_release_buffers(&segbuf->sb_segsum_buffers);260 nilfs_release_buffers(&segbuf->sb_payload_buffers);261 segbuf->sb_super_root = NULL;262}263 264/*265 * Iterators for segment buffers266 */267void nilfs_clear_logs(struct list_head *logs)268{269 struct nilfs_segment_buffer *segbuf;270 271 list_for_each_entry(segbuf, logs, sb_list)272 nilfs_segbuf_clear(segbuf);273}274 275void nilfs_truncate_logs(struct list_head *logs,276 struct nilfs_segment_buffer *last)277{278 struct nilfs_segment_buffer *n, *segbuf;279 280 segbuf = list_prepare_entry(last, logs, sb_list);281 list_for_each_entry_safe_continue(segbuf, n, logs, sb_list) {282 list_del_init(&segbuf->sb_list);283 nilfs_segbuf_clear(segbuf);284 nilfs_segbuf_free(segbuf);285 }286}287 288int nilfs_write_logs(struct list_head *logs, struct the_nilfs *nilfs)289{290 struct nilfs_segment_buffer *segbuf;291 int ret = 0;292 293 list_for_each_entry(segbuf, logs, sb_list) {294 ret = nilfs_segbuf_write(segbuf, nilfs);295 if (ret)296 break;297 }298 return ret;299}300 301int nilfs_wait_on_logs(struct list_head *logs)302{303 struct nilfs_segment_buffer *segbuf;304 int err, ret = 0;305 306 list_for_each_entry(segbuf, logs, sb_list) {307 err = nilfs_segbuf_wait(segbuf);308 if (err && !ret)309 ret = err;310 }311 return ret;312}313 314/**315 * nilfs_add_checksums_on_logs - add checksums on the logs316 * @logs: list of segment buffers storing target logs317 * @seed: checksum seed value318 */319void nilfs_add_checksums_on_logs(struct list_head *logs, u32 seed)320{321 struct nilfs_segment_buffer *segbuf;322 323 list_for_each_entry(segbuf, logs, sb_list) {324 if (segbuf->sb_super_root)325 nilfs_segbuf_fill_in_super_root_crc(segbuf, seed);326 nilfs_segbuf_fill_in_segsum_crc(segbuf, seed);327 nilfs_segbuf_fill_in_data_crc(segbuf, seed);328 }329}330 331/*332 * BIO operations333 */334static void nilfs_end_bio_write(struct bio *bio)335{336 struct nilfs_segment_buffer *segbuf = bio->bi_private;337 338 if (bio->bi_status)339 atomic_inc(&segbuf->sb_err);340 341 bio_put(bio);342 complete(&segbuf->sb_bio_event);343}344 345static int nilfs_segbuf_submit_bio(struct nilfs_segment_buffer *segbuf,346 struct nilfs_write_info *wi)347{348 struct bio *bio = wi->bio;349 350 bio->bi_end_io = nilfs_end_bio_write;351 bio->bi_private = segbuf;352 submit_bio(bio);353 segbuf->sb_nbio++;354 355 wi->bio = NULL;356 wi->rest_blocks -= wi->end - wi->start;357 wi->nr_vecs = min(wi->max_pages, wi->rest_blocks);358 wi->start = wi->end;359 return 0;360}361 362static void nilfs_segbuf_prepare_write(struct nilfs_segment_buffer *segbuf,363 struct nilfs_write_info *wi)364{365 wi->bio = NULL;366 wi->rest_blocks = segbuf->sb_sum.nblocks;367 wi->max_pages = BIO_MAX_VECS;368 wi->nr_vecs = min(wi->max_pages, wi->rest_blocks);369 wi->start = wi->end = 0;370 wi->blocknr = segbuf->sb_pseg_start;371}372 373static int nilfs_segbuf_submit_bh(struct nilfs_segment_buffer *segbuf,374 struct nilfs_write_info *wi,375 struct buffer_head *bh)376{377 int len, err;378 379 BUG_ON(wi->nr_vecs <= 0);380 repeat:381 if (!wi->bio) {382 wi->bio = bio_alloc(wi->nilfs->ns_bdev, wi->nr_vecs,383 REQ_OP_WRITE, GFP_NOIO);384 wi->bio->bi_iter.bi_sector = (wi->blocknr + wi->end) <<385 (wi->nilfs->ns_blocksize_bits - 9);386 }387 388 len = bio_add_page(wi->bio, bh->b_page, bh->b_size, bh_offset(bh));389 if (len == bh->b_size) {390 wi->end++;391 return 0;392 }393 /* bio is FULL */394 err = nilfs_segbuf_submit_bio(segbuf, wi);395 /* never submit current bh */396 if (likely(!err))397 goto repeat;398 return err;399}400 401/**402 * nilfs_segbuf_write - submit write requests of a log403 * @segbuf: buffer storing a log to be written404 * @nilfs: nilfs object405 *406 * Return Value: On Success, 0 is returned. On Error, one of the following407 * negative error code is returned.408 *409 * %-EIO - I/O error410 *411 * %-ENOMEM - Insufficient memory available.412 */413static int nilfs_segbuf_write(struct nilfs_segment_buffer *segbuf,414 struct the_nilfs *nilfs)415{416 struct nilfs_write_info wi;417 struct buffer_head *bh;418 int res = 0;419 420 wi.nilfs = nilfs;421 nilfs_segbuf_prepare_write(segbuf, &wi);422 423 list_for_each_entry(bh, &segbuf->sb_segsum_buffers, b_assoc_buffers) {424 res = nilfs_segbuf_submit_bh(segbuf, &wi, bh);425 if (unlikely(res))426 goto failed_bio;427 }428 429 list_for_each_entry(bh, &segbuf->sb_payload_buffers, b_assoc_buffers) {430 res = nilfs_segbuf_submit_bh(segbuf, &wi, bh);431 if (unlikely(res))432 goto failed_bio;433 }434 435 if (wi.bio) {436 /*437 * Last BIO is always sent through the following438 * submission.439 */440 wi.bio->bi_opf |= REQ_SYNC;441 res = nilfs_segbuf_submit_bio(segbuf, &wi);442 }443 444 failed_bio:445 return res;446}447 448/**449 * nilfs_segbuf_wait - wait for completion of requested BIOs450 * @segbuf: segment buffer451 *452 * Return Value: On Success, 0 is returned. On Error, one of the following453 * negative error code is returned.454 *455 * %-EIO - I/O error456 */457static int nilfs_segbuf_wait(struct nilfs_segment_buffer *segbuf)458{459 int err = 0;460 461 if (!segbuf->sb_nbio)462 return 0;463 464 do {465 wait_for_completion(&segbuf->sb_bio_event);466 } while (--segbuf->sb_nbio > 0);467 468 if (unlikely(atomic_read(&segbuf->sb_err) > 0)) {469 nilfs_err(segbuf->sb_super,470 "I/O error writing log (start-blocknr=%llu, block-count=%lu) in segment %llu",471 (unsigned long long)segbuf->sb_pseg_start,472 segbuf->sb_sum.nblocks,473 (unsigned long long)segbuf->sb_segnum);474 err = -EIO;475 }476 return err;477}478