// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2025 Christoph Hellwig */ #include "blk.h" struct phys_vec { phys_addr_t paddr; u32 len; }; static bool blk_map_iter_next(struct request *req, struct req_iterator *iter, struct phys_vec *vec) { unsigned int max_size; struct bio_vec bv; if (req->rq_flags & RQF_SPECIAL_PAYLOAD) { if (!iter->bio) return false; vec->paddr = bvec_phys(&req->special_vec); vec->len = req->special_vec.bv_len; iter->bio = NULL; return true; } if (!iter->iter.bi_size) return false; bv = mp_bvec_iter_bvec(iter->bio->bi_io_vec, iter->iter); vec->paddr = bvec_phys(&bv); max_size = get_max_segment_size(&req->q->limits, vec->paddr, UINT_MAX); bv.bv_len = min(bv.bv_len, max_size); bio_advance_iter_single(iter->bio, &iter->iter, bv.bv_len); /* * If we are entirely done with this bi_io_vec entry, check if the next * one could be merged into it. This typically happens when moving to * the next bio, but some callers also don't pack bvecs tight. */ while (!iter->iter.bi_size || !iter->iter.bi_bvec_done) { struct bio_vec next; if (!iter->iter.bi_size) { if (!iter->bio->bi_next) break; iter->bio = iter->bio->bi_next; iter->iter = iter->bio->bi_iter; } next = mp_bvec_iter_bvec(iter->bio->bi_io_vec, iter->iter); if (bv.bv_len + next.bv_len > max_size || !biovec_phys_mergeable(req->q, &bv, &next)) break; bv.bv_len += next.bv_len; bio_advance_iter_single(iter->bio, &iter->iter, next.bv_len); } vec->len = bv.bv_len; return true; } static inline struct scatterlist * blk_next_sg(struct scatterlist **sg, struct scatterlist *sglist) { if (!*sg) return sglist; /* * If the driver previously mapped a shorter list, we could see a * termination bit prematurely unless it fully inits the sg table * on each mapping. We KNOW that there must be more entries here * or the driver would be buggy, so force clear the termination bit * to avoid doing a full sg_init_table() in drivers for each command. */ sg_unmark_end(*sg); return sg_next(*sg); } /* * Map a request to scatterlist, return number of sg entries setup. Caller * must make sure sg can hold rq->nr_phys_segments entries. */ int __blk_rq_map_sg(struct request *rq, struct scatterlist *sglist, struct scatterlist **last_sg) { struct req_iterator iter = { .bio = rq->bio, }; struct phys_vec vec; int nsegs = 0; /* the internal flush request may not have bio attached */ if (iter.bio) iter.iter = iter.bio->bi_iter; while (blk_map_iter_next(rq, &iter, &vec)) { *last_sg = blk_next_sg(last_sg, sglist); sg_set_page(*last_sg, phys_to_page(vec.paddr), vec.len, offset_in_page(vec.paddr)); nsegs++; } if (*last_sg) sg_mark_end(*last_sg); /* * Something must have been wrong if the figured number of * segment is bigger than number of req's physical segments */ WARN_ON(nsegs > blk_rq_nr_phys_segments(rq)); return nsegs; } EXPORT_SYMBOL(__blk_rq_map_sg);