/* * Copyright (c) 2020 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * $Id: //eng/vdo-releases/aluminum/src/c++/vdo/kernel/bio.h#6 $ */ #ifndef BIO_H #define BIO_H #include #include #include #include "kernelTypes.h" #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0) #define USE_BI_ITER 1 #endif /** * Copy the bio data to a char array. * * @param bio The bio to copy the data from * @param dataPtr The local array to copy the data to **/ void bioCopyDataIn(BIO *bio, char *dataPtr); /** * Copy a char array to the bio data. * * @param bio The bio to copy the data to * @param dataPtr The local array to copy the data from **/ void bioCopyDataOut(BIO *bio, char *dataPtr); /** * Set the bi_rw or equivalent field of a bio to a particular data * operation. Intended to be called only by setBioOperationRead() etc. * * @param bio The bio to modify * @param operation The operation to set it to **/ void setBioOperation(BIO *bio, unsigned int operation); /**********************************************************************/ static inline void setBioOperationRead(BIO *bio) { setBioOperation(bio, READ); } /**********************************************************************/ static inline void setBioOperationWrite(BIO *bio) { setBioOperation(bio, WRITE); } /**********************************************************************/ static inline void clearBioOperationAndFlags(BIO *bio) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(4,10,0) bio->bi_opf = 0; #else bio->bi_rw = 0; #endif } /**********************************************************************/ static inline void copyBioOperationAndFlags(BIO *to, BIO *from) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(4,10,0) to->bi_opf = from->bi_opf; #else to->bi_rw = from->bi_rw; #endif } /**********************************************************************/ static inline void setBioOperationFlag(BIO *bio, unsigned int flag) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(4,10,0) bio->bi_opf |= flag; #else bio->bi_rw |= flag; #endif } /**********************************************************************/ static inline void clearBioOperationFlag(BIO *bio, unsigned int flag) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(4,10,0) bio->bi_opf &= ~flag; #else bio->bi_rw &= ~flag; #endif } /**********************************************************************/ static inline void setBioOperationFlagPreflush(BIO *bio) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(4,10,0) setBioOperationFlag(bio, REQ_PREFLUSH); #else // Preflushes and empty flushes are not currently distinguished. setBioOperation(bio, WRITE_FLUSH); #endif } /**********************************************************************/ static inline void setBioOperationFlagSync(BIO *bio) { setBioOperationFlag(bio, REQ_SYNC); } /**********************************************************************/ static inline void clearBioOperationFlagSync(BIO *bio) { clearBioOperationFlag(bio, REQ_SYNC); } /**********************************************************************/ static inline void setBioOperationFlagFua(BIO *bio) { setBioOperationFlag(bio, REQ_FUA); } /**********************************************************************/ static inline void clearBioOperationFlagFua(BIO *bio) { clearBioOperationFlag(bio, REQ_FUA); } /**********************************************************************/ static inline bool isDiscardBio(BIO *bio) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(4,10,0) return (bio != NULL) && (bio_op(bio) == REQ_OP_DISCARD); #else return (bio != NULL) && ((bio->bi_rw & REQ_DISCARD) != 0); #endif } /**********************************************************************/ static inline bool isFlushBio(BIO *bio) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(4,10,0) return (bio_op(bio) == REQ_OP_FLUSH) || ((bio->bi_opf & REQ_PREFLUSH) != 0); #else return (bio->bi_rw & REQ_FLUSH) != 0; #endif } /**********************************************************************/ static inline bool isFUABio(BIO *bio) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(4,10,0) return (bio->bi_opf & REQ_FUA) != 0; #else return (bio->bi_rw & REQ_FUA) != 0; #endif } /**********************************************************************/ static inline bool isReadBio(BIO *bio) { return bio_data_dir(bio) == READ; } /**********************************************************************/ static inline bool isWriteBio(BIO *bio) { return bio_data_dir(bio) == WRITE; } #if LINUX_VERSION_CODE >= KERNEL_VERSION(4,4,0) /** * Get the error from the bio. * * @param bio The bio * * @return the bio's error if any **/ static inline int getBioResult(BIO *bio) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(4,13,0) return blk_status_to_errno(bio->bi_status); #else return bio->bi_error; #endif } #endif // newer than 4.4 /** * Set the block device for a bio. * * @param bio The bio to modify * @param device The new block device for the bio **/ static inline void setBioBlockDevice(BIO *bio, struct block_device *device) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(4,14,0) bio_set_dev(bio, device); #else bio->bi_bdev = device; #endif } /** * Get a bio's size. * * @param bio The bio * * @return the bio's size **/ static inline unsigned int getBioSize(BIO *bio) { #ifdef USE_BI_ITER return bio->bi_iter.bi_size; #else return bio->bi_size; #endif } /** * Set the bio's sector. * * @param bio The bio * @param sector The sector **/ static inline void setBioSector(BIO *bio, sector_t sector) { #ifdef USE_BI_ITER bio->bi_iter.bi_sector = sector; #else bio->bi_sector = sector; #endif } /** * Get the bio's sector. * * @param bio The bio * * @return the sector **/ static inline sector_t getBioSector(BIO *bio) { #ifdef USE_BI_ITER return bio->bi_iter.bi_sector; #else return bio->bi_sector; #endif } /** * Tell the kernel we've completed processing of this bio. * * @param bio The bio to complete * @param error A system error code, or 0 for success **/ static inline void completeBio(BIO *bio, int error) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(4,13,0) bio->bi_status = errno_to_blk_status(error); bio_endio(bio); #elif LINUX_VERSION_CODE >= KERNEL_VERSION(4,4,0) bio->bi_error = error; bio_endio(bio); #else bio_endio(bio, error); #endif } /** * Frees up a bio structure * * @param bio The bio to free * @param layer The layer the bio was created in **/ void freeBio(BIO *bio, KernelLayer *layer); /** * Count the statistics for the bios. This is used for calls into VDO and * for calls out of VDO. * * @param bioStats Statistics structure to update * @param bio The bio **/ void countBios(AtomicBioStats *bioStats, BIO *bio); /** * Reset a bio so it can be used again. * * @param bio The bio to reset * @param layer The physical layer **/ void resetBio(BIO *bio, KernelLayer *layer); /** * Check to see whether a bio's data are all zeroes. * * @param bio The bio * * @return true if the bio's data are all zeroes **/ bool bioIsZeroData(BIO *bio); /** * Set a bio's data to all zeroes. * * @param [in] bio The bio **/ void bioZeroData(BIO *bio); /** * Create a new bio structure for kernel buffer storage. * * @param [in] layer The physical layer * @param [in] data The buffer (can be NULL) * @param [out] bioPtr A pointer to hold new bio * * @return VDO_SUCCESS or an error **/ int createBio(KernelLayer *layer, char *data, BIO **bioPtr); /** * Prepare a BIO to issue a flush to the device below. * * @param bio The flush BIO * @param context The context for the callback * @param device The device to flush * @param endIOCallback The function to call when the flush is complete **/ void prepareFlushBIO(BIO *bio, void *context, struct block_device *device, bio_end_io_t *endIOCallback); /** * Perform IO with a bio, waiting for completion and returning its result. * The bio must already have its sector, block device, and operation set. * * @param bio The bio to do IO with * * @return The bio result **/ static inline int submitBioAndWait(BIO *bio) { submit_bio_wait(bio); return getBioResult(bio); } #endif /* BIO_H */