/* * 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/base/compressionState.c#2 $ */ #include "compressionStateInternals.h" #include "dataVIO.h" #include "packer.h" static const uint32_t STATUS_MASK = 0xff; static const uint32_t MAY_NOT_COMPRESS_MASK = 0x80000000; /**********************************************************************/ VIOCompressionState getCompressionState(DataVIO *dataVIO) { uint32_t packedValue = atomicLoad32(&dataVIO->compression.state); return (VIOCompressionState) { .status = packedValue & STATUS_MASK, .mayNotCompress = ((packedValue & MAY_NOT_COMPRESS_MASK) != 0), }; } /** * Convert a VIOCompressionState into a uint32_t which may be stored * atomically. * * @param state The state to convert * * @return The compression state packed into a uint32_t **/ __attribute__((warn_unused_result)) static uint32_t packState(VIOCompressionState state) { return state.status | (state.mayNotCompress ? MAY_NOT_COMPRESS_MASK : 0); } /**********************************************************************/ bool setCompressionState(DataVIO *dataVIO, VIOCompressionState state, VIOCompressionState newState) { return compareAndSwap32(&dataVIO->compression.state, packState(state), packState(newState)); } /** * Advance to the next compression state along the compression path. * * @param dataVIO The DataVIO to advance * * @return The new compression status of the DataVIO **/ static VIOCompressionStatus advanceStatus(DataVIO *dataVIO) { for (;;) { VIOCompressionState state = getCompressionState(dataVIO); if (state.status == VIO_POST_PACKER) { // We're already in the last state. return state.status; } VIOCompressionState newState = state; if (state.mayNotCompress) { // Compression has been dis-allowed for this VIO, so skip the rest of the // path and go to the end. newState.status = VIO_POST_PACKER; } else { // Go to the next state. newState.status++; } if (setCompressionState(dataVIO, state, newState)) { return newState.status; } // Another thread changed the state out from under us so try again. } } /**********************************************************************/ bool mayCompressDataVIO(DataVIO *dataVIO) { if (!hasAllocation(dataVIO) || ((getWritePolicy(getVDOFromDataVIO(dataVIO)) != WRITE_POLICY_SYNC) && vioRequiresFlushAfter(dataVIOAsVIO(dataVIO))) || !getVDOCompressing(getVDOFromDataVIO(dataVIO))) { /* * If this VIO didn't get an allocation, the compressed write probably * won't either, so don't try compressing it. Also, if compression is off, * don't compress. */ setCompressionDone(dataVIO); return false; } if (dataVIO->hashLock == NULL) { // DataVIOs without a HashLock (which should be extremely rare) aren't // able to share the packer's PBN lock, so don't try to compress them. return false; } return (advanceStatus(dataVIO) == VIO_COMPRESSING); } /**********************************************************************/ bool mayPackDataVIO(DataVIO *dataVIO) { if (!isSufficientlyCompressible(dataVIO) || !getVDOCompressing(getVDOFromDataVIO(dataVIO)) || getCompressionState(dataVIO).mayNotCompress) { // If the data in this VIO doesn't compress, or compression is off, or // compression for this VIO has been canceled, don't send it to the packer. setCompressionDone(dataVIO); return false; } return true; } /**********************************************************************/ bool mayBlockInPacker(DataVIO *dataVIO) { return (advanceStatus(dataVIO) == VIO_PACKING); } /**********************************************************************/ bool mayWriteCompressedDataVIO(DataVIO *dataVIO) { advanceStatus(dataVIO); return !getCompressionState(dataVIO).mayNotCompress; } /**********************************************************************/ void setCompressionDone(DataVIO *dataVIO) { for (;;) { VIOCompressionState state = getCompressionState(dataVIO); if (state.status == VIO_POST_PACKER) { // The VIO is already done. return; } // If compression was cancelled on this VIO, preserve that fact. VIOCompressionState newState = { .status = VIO_POST_PACKER, .mayNotCompress = true, }; if (setCompressionState(dataVIO, state, newState)) { return; } } } /**********************************************************************/ bool cancelCompression(DataVIO *dataVIO) { VIOCompressionState state; for (;;) { state = getCompressionState(dataVIO); if (state.mayNotCompress || (state.status == VIO_POST_PACKER)) { // This DataVIO is already set up to not block in the packer. break; } VIOCompressionState newState = { .status = state.status, .mayNotCompress = true, }; if (setCompressionState(dataVIO, state, newState)) { break; } } return ((state.status == VIO_PACKING) && !state.mayNotCompress); }