I make the block device driver, when processing the request for READ, I install my handler (I / O completion routine) in the bio.bi_end_io field. My handler is called, but bio doesn’t have any biovec, and I need to process the data before the block I / O subsystem returns it to the application. I cite code snippets for illustration. *
static void __iob_enc_dec ( struct bio * iob ) { struct bio_vec bvec = {0}; struct bvec_iter iter = {0}; sector_t lbn, nlbn; $TRACE("Start %scrypting ...", bio_data_dir(iob) == WRITE ? "En" : "De"); $SHOW_PTR(bio_data(iob)); /* Do each segment independently. */ **bio_for_each_segment**(bvec, iob, iter) { char * iobuf; iobuf = __bio_kmap_atomic(iob, iter); $TRACE("#%02d: page=%p, off=%u, len=%u, iobuf=%p, lbn=%lu", iter.bi_idx, bvec.bv_page, bvec.bv_offset, bvec.bv_len, iobuf, iter.bi_sector); iobuf += bvec.bv_offset; lbn = iter.bi_sector; nlbn = iter.bi_size/DUDRV$K_BLKSZ; for ( ;nlbn; nlbn--, lbn++ , iobuf += DUDRV$K_BLKSZ) { **...** } __bio_kunmap_atomic(iob); } } static void dua_bio_end_io ( struct bio * iob ) { IOB_ARGS * iob_args; $TRACE("Entering to %s completion I/O, bio=%p ...", bio_data_dir(iob) == WRITE ? "WRITE" : "READ", iob); $SHOW_UNSIGNED(iob->bi_flags); $SHOW_INT(iob->bi_vcnt); $SHOW_BOOL(bio_has_data(iob)); iob_args = iob->bi_private; iob->bi_end_io = iob_args->bi_end_io; iob->bi_private = iob_args->bi_private; bio_put(iob); __ret_iob_args (iob_args); /* In case of READ request - we should decrypt data buffer right now */ if ( bio_data_dir(iob) == READ ) **__iob_enc_dec(iob)**; bio_endio (iob); } static blk_qc_t dua_make_request_fn ( struct request_queue * ioq, struct bio * iob ) { int status = 0; $TRACE("Starting (%s), bio=%p, op=%d ...", bio_data_dir(iob) == WRITE ? "WRITE" : "READ", iob, bio_op(iob)); $SHOW_UNSIGNED(iob->bi_flags); $SHOW_INT(iob->bi_vcnt); $SHOW_BOOL(bio_has_data(iob)); /* In case of WRITE request - we can encrypt data buffer right now */ if ( bio_data_dir(iob) == WRITE ) __iob_enc_dec(iob); /* * A handling of the READ request is require 'enqueue read request' & 'wait for read completion' paradigm, * so we need to allocate IOB_ARGS block to carry data to the "Read Completion I/O" routine. */ else if ( bio_data_dir(iob) == READ ) { IOB_ARGS *iob_args = NULL; if ( __get_iob_args (&iob_args) ) { printk(KERN_ERR __MODULE__ ": Buffered I/O quota limit has been exhausted\n"); iob->bi_error = -EBUSY; bio_endio(iob); } iob_args->bi_end_io = iob->bi_end_io; iob_args->bi_private = iob->bi_private; /* * Replace an address of the Completion I/O routine for 'read' operation, * save original address. */ iob->bi_private = iob_args; iob->bi_end_io = dua_bio_end_io; bio_get(iob); } /* Just for sanity check ... */ else { printk(KERN_WARNING __MODULE__ ": Unhandled I/O request %d\n", bio_data_dir(iob) ); } /* Call original make_reques_fn() to performs a main work ... */ status = backend_make_request_fn(ioq, iob); return status; } [6463.418222] [DUDRIVER\dua_make_request_fn:479] Starting (READ), bio=ffff95c5f9552100, op=0 ... [ 6463.418222] [DUDRIVER\dua_make_request_fn:482] : iob->bi_flags = 0x00000000 [ 6463.418223] [DUDRIVER\dua_make_request_fn:483] : iob->bi_vcnt = 1 [ 6463.418223] [DUDRIVER\dua_make_request_fn:485] : bio_has_data(iob) = ENABLED(TRUE) [ 6463.418266] [DUDRIVER\dua_bio_end_io:445] Entering to READ completion I/O, bio=ffff95c5f9552100 ... [ 6463.418266] [DUDRIVER\dua_bio_end_io:447] : iob->bi_flags = 0x00000102 [ 6463.418267] [DUDRIVER\dua_bio_end_io:448] : iob->bi_vcnt = 1 [ 6463.418267] [DUDRIVER\dua_bio_end_io:450] : bio_has_data(iob) = DISABLED(FALSE) What do I need to do in bio_end_io () to access data buffers? This happens in Ubuntu 16.x, Linux kernel 4.10.y.
Update1: it is possible that the macro bio_for_each_segment when the BIO_CLONED flag is set - does not allow to run through the segments of the buffers, but then how to be?