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?

    1 answer 1

     static void __iob_enc_dec ( struct bio * iob, sector_t lbn ) { struct bio_vec *bvl; sector_t nlbn; int i; $TRACE("Start %scrypting ...", bio_data_dir(iob) == WRITE ? "En" : "De"); #if 0 { for (i = 0, bvl = iob->bi_io_vec; i < iob->bi_vcnt; i++, bvl++) { $SHOW_PTR(bvl->bv_page); $SHOW_INT(bvl->bv_len); $SHOW_INT(bvl->bv_offset); } } /* 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_data_dir(iob) == WRITE ? __encrypt (&gost89_ctx, lbn, iobuf, iobuf) : __decrypt (&gost89_ctx, lbn, iobuf, iobuf); } __bio_kunmap_atomic(iob); } #endif /* Do each segment independently. */ **for (i = 0, bvl = iob->bi_io_vec; i < iob->bi_vcnt; i++, bvl++)** { void *kaddr = kmap_atomic(bvl->bv_page), *iobuf; iobuf = kaddr + bvl->bv_offset; nlbn = bvl->bv_len/DUDRV$K_BLKSZ; $TRACE("#%02d: page=%p, off=%u, len=%u, iobuf=%p, lbn=%lu, nlbn=%lu", i, bvl->bv_page, bvl->bv_offset, bvl->bv_len, iobuf, lbn, nlbn); for ( ;nlbn; nlbn--, lbn++ , iobuf += DUDRV$K_BLKSZ) { if ( bio_data_dir(iob) == WRITE ) __encrypt (&gost89_ctx, lbn, iobuf, iobuf); else __decrypt (&gost89_ctx, lbn, iobuf, iobuf); } kunmap_atomic(kaddr); } } 

    The solution is to replace the stupid macro with the old school mileage on vectors. For my task, the starting sector number of the disk block is needed, it is transferred in the context block IOB_ARGS, but this is particular.