|
Packit |
b55c50 |
/*
|
|
Packit |
b55c50 |
* Copyright (c) 2020 Red Hat, Inc.
|
|
Packit |
b55c50 |
*
|
|
Packit |
b55c50 |
* This program is free software; you can redistribute it and/or
|
|
Packit |
b55c50 |
* modify it under the terms of the GNU General Public License
|
|
Packit |
b55c50 |
* as published by the Free Software Foundation; either version 2
|
|
Packit |
b55c50 |
* of the License, or (at your option) any later version.
|
|
Packit |
b55c50 |
*
|
|
Packit |
b55c50 |
* This program is distributed in the hope that it will be useful,
|
|
Packit |
b55c50 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
b55c50 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
Packit |
b55c50 |
* GNU General Public License for more details.
|
|
Packit |
b55c50 |
*
|
|
Packit |
b55c50 |
* You should have received a copy of the GNU General Public License
|
|
Packit |
b55c50 |
* along with this program; if not, write to the Free Software
|
|
Packit |
b55c50 |
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
Packit |
b55c50 |
* 02110-1301, USA.
|
|
Packit |
b55c50 |
*
|
|
Packit |
b55c50 |
* $Id: //eng/vdo-releases/aluminum/src/c++/vdo/base/compressionState.c#2 $
|
|
Packit |
b55c50 |
*/
|
|
Packit |
b55c50 |
|
|
Packit |
b55c50 |
#include "compressionStateInternals.h"
|
|
Packit |
b55c50 |
|
|
Packit |
b55c50 |
#include "dataVIO.h"
|
|
Packit |
b55c50 |
#include "packer.h"
|
|
Packit |
b55c50 |
|
|
Packit |
b55c50 |
static const uint32_t STATUS_MASK = 0xff;
|
|
Packit |
b55c50 |
static const uint32_t MAY_NOT_COMPRESS_MASK = 0x80000000;
|
|
Packit |
b55c50 |
|
|
Packit |
b55c50 |
/**********************************************************************/
|
|
Packit |
b55c50 |
VIOCompressionState getCompressionState(DataVIO *dataVIO)
|
|
Packit |
b55c50 |
{
|
|
Packit |
b55c50 |
uint32_t packedValue = atomicLoad32(&dataVIO->compression.state);
|
|
Packit |
b55c50 |
return (VIOCompressionState) {
|
|
Packit |
b55c50 |
.status = packedValue & STATUS_MASK,
|
|
Packit |
b55c50 |
.mayNotCompress = ((packedValue & MAY_NOT_COMPRESS_MASK) != 0),
|
|
Packit |
b55c50 |
};
|
|
Packit |
b55c50 |
}
|
|
Packit |
b55c50 |
|
|
Packit |
b55c50 |
/**
|
|
Packit |
b55c50 |
* Convert a VIOCompressionState into a uint32_t which may be stored
|
|
Packit |
b55c50 |
* atomically.
|
|
Packit |
b55c50 |
*
|
|
Packit |
b55c50 |
* @param state The state to convert
|
|
Packit |
b55c50 |
*
|
|
Packit |
b55c50 |
* @return The compression state packed into a uint32_t
|
|
Packit |
b55c50 |
**/
|
|
Packit |
b55c50 |
__attribute__((warn_unused_result))
|
|
Packit |
b55c50 |
static uint32_t packState(VIOCompressionState state)
|
|
Packit |
b55c50 |
{
|
|
Packit |
b55c50 |
return state.status | (state.mayNotCompress ? MAY_NOT_COMPRESS_MASK : 0);
|
|
Packit |
b55c50 |
}
|
|
Packit |
b55c50 |
|
|
Packit |
b55c50 |
/**********************************************************************/
|
|
Packit |
b55c50 |
bool setCompressionState(DataVIO *dataVIO,
|
|
Packit |
b55c50 |
VIOCompressionState state,
|
|
Packit |
b55c50 |
VIOCompressionState newState)
|
|
Packit |
b55c50 |
{
|
|
Packit |
b55c50 |
return compareAndSwap32(&dataVIO->compression.state, packState(state),
|
|
Packit |
b55c50 |
packState(newState));
|
|
Packit |
b55c50 |
}
|
|
Packit |
b55c50 |
|
|
Packit |
b55c50 |
/**
|
|
Packit |
b55c50 |
* Advance to the next compression state along the compression path.
|
|
Packit |
b55c50 |
*
|
|
Packit |
b55c50 |
* @param dataVIO The DataVIO to advance
|
|
Packit |
b55c50 |
*
|
|
Packit |
b55c50 |
* @return The new compression status of the DataVIO
|
|
Packit |
b55c50 |
**/
|
|
Packit |
b55c50 |
static VIOCompressionStatus advanceStatus(DataVIO *dataVIO)
|
|
Packit |
b55c50 |
{
|
|
Packit |
b55c50 |
for (;;) {
|
|
Packit |
b55c50 |
VIOCompressionState state = getCompressionState(dataVIO);
|
|
Packit |
b55c50 |
if (state.status == VIO_POST_PACKER) {
|
|
Packit |
b55c50 |
// We're already in the last state.
|
|
Packit |
b55c50 |
return state.status;
|
|
Packit |
b55c50 |
}
|
|
Packit |
b55c50 |
|
|
Packit |
b55c50 |
VIOCompressionState newState = state;
|
|
Packit |
b55c50 |
if (state.mayNotCompress) {
|
|
Packit |
b55c50 |
// Compression has been dis-allowed for this VIO, so skip the rest of the
|
|
Packit |
b55c50 |
// path and go to the end.
|
|
Packit |
b55c50 |
newState.status = VIO_POST_PACKER;
|
|
Packit |
b55c50 |
} else {
|
|
Packit |
b55c50 |
// Go to the next state.
|
|
Packit |
b55c50 |
newState.status++;
|
|
Packit |
b55c50 |
}
|
|
Packit |
b55c50 |
|
|
Packit |
b55c50 |
if (setCompressionState(dataVIO, state, newState)) {
|
|
Packit |
b55c50 |
return newState.status;
|
|
Packit |
b55c50 |
}
|
|
Packit |
b55c50 |
|
|
Packit |
b55c50 |
// Another thread changed the state out from under us so try again.
|
|
Packit |
b55c50 |
}
|
|
Packit |
b55c50 |
}
|
|
Packit |
b55c50 |
|
|
Packit |
b55c50 |
/**********************************************************************/
|
|
Packit |
b55c50 |
bool mayCompressDataVIO(DataVIO *dataVIO)
|
|
Packit |
b55c50 |
{
|
|
Packit |
b55c50 |
if (!hasAllocation(dataVIO)
|
|
Packit |
b55c50 |
|| ((getWritePolicy(getVDOFromDataVIO(dataVIO)) != WRITE_POLICY_SYNC)
|
|
Packit |
b55c50 |
&& vioRequiresFlushAfter(dataVIOAsVIO(dataVIO)))
|
|
Packit |
b55c50 |
|| !getVDOCompressing(getVDOFromDataVIO(dataVIO))) {
|
|
Packit |
b55c50 |
/*
|
|
Packit |
b55c50 |
* If this VIO didn't get an allocation, the compressed write probably
|
|
Packit |
b55c50 |
* won't either, so don't try compressing it. Also, if compression is off,
|
|
Packit |
b55c50 |
* don't compress.
|
|
Packit |
b55c50 |
*/
|
|
Packit |
b55c50 |
setCompressionDone(dataVIO);
|
|
Packit |
b55c50 |
return false;
|
|
Packit |
b55c50 |
}
|
|
Packit |
b55c50 |
|
|
Packit |
b55c50 |
if (dataVIO->hashLock == NULL) {
|
|
Packit |
b55c50 |
// DataVIOs without a HashLock (which should be extremely rare) aren't
|
|
Packit |
b55c50 |
// able to share the packer's PBN lock, so don't try to compress them.
|
|
Packit |
b55c50 |
return false;
|
|
Packit |
b55c50 |
}
|
|
Packit |
b55c50 |
|
|
Packit |
b55c50 |
return (advanceStatus(dataVIO) == VIO_COMPRESSING);
|
|
Packit |
b55c50 |
}
|
|
Packit |
b55c50 |
|
|
Packit |
b55c50 |
/**********************************************************************/
|
|
Packit |
b55c50 |
bool mayPackDataVIO(DataVIO *dataVIO)
|
|
Packit |
b55c50 |
{
|
|
Packit |
b55c50 |
if (!isSufficientlyCompressible(dataVIO)
|
|
Packit |
b55c50 |
|| !getVDOCompressing(getVDOFromDataVIO(dataVIO))
|
|
Packit |
b55c50 |
|| getCompressionState(dataVIO).mayNotCompress) {
|
|
Packit |
b55c50 |
// If the data in this VIO doesn't compress, or compression is off, or
|
|
Packit |
b55c50 |
// compression for this VIO has been canceled, don't send it to the packer.
|
|
Packit |
b55c50 |
setCompressionDone(dataVIO);
|
|
Packit |
b55c50 |
return false;
|
|
Packit |
b55c50 |
}
|
|
Packit |
b55c50 |
|
|
Packit |
b55c50 |
return true;
|
|
Packit |
b55c50 |
}
|
|
Packit |
b55c50 |
|
|
Packit |
b55c50 |
/**********************************************************************/
|
|
Packit |
b55c50 |
bool mayBlockInPacker(DataVIO *dataVIO)
|
|
Packit |
b55c50 |
{
|
|
Packit |
b55c50 |
return (advanceStatus(dataVIO) == VIO_PACKING);
|
|
Packit |
b55c50 |
}
|
|
Packit |
b55c50 |
|
|
Packit |
b55c50 |
/**********************************************************************/
|
|
Packit |
b55c50 |
bool mayWriteCompressedDataVIO(DataVIO *dataVIO)
|
|
Packit |
b55c50 |
{
|
|
Packit |
b55c50 |
advanceStatus(dataVIO);
|
|
Packit |
b55c50 |
return !getCompressionState(dataVIO).mayNotCompress;
|
|
Packit |
b55c50 |
}
|
|
Packit |
b55c50 |
|
|
Packit |
b55c50 |
/**********************************************************************/
|
|
Packit |
b55c50 |
void setCompressionDone(DataVIO *dataVIO)
|
|
Packit |
b55c50 |
{
|
|
Packit |
b55c50 |
for (;;) {
|
|
Packit |
b55c50 |
VIOCompressionState state = getCompressionState(dataVIO);
|
|
Packit |
b55c50 |
if (state.status == VIO_POST_PACKER) {
|
|
Packit |
b55c50 |
// The VIO is already done.
|
|
Packit |
b55c50 |
return;
|
|
Packit |
b55c50 |
}
|
|
Packit |
b55c50 |
|
|
Packit |
b55c50 |
// If compression was cancelled on this VIO, preserve that fact.
|
|
Packit |
b55c50 |
VIOCompressionState newState = {
|
|
Packit |
b55c50 |
.status = VIO_POST_PACKER,
|
|
Packit |
b55c50 |
.mayNotCompress = true,
|
|
Packit |
b55c50 |
};
|
|
Packit |
b55c50 |
if (setCompressionState(dataVIO, state, newState)) {
|
|
Packit |
b55c50 |
return;
|
|
Packit |
b55c50 |
}
|
|
Packit |
b55c50 |
}
|
|
Packit |
b55c50 |
}
|
|
Packit |
b55c50 |
|
|
Packit |
b55c50 |
/**********************************************************************/
|
|
Packit |
b55c50 |
bool cancelCompression(DataVIO *dataVIO)
|
|
Packit |
b55c50 |
{
|
|
Packit |
b55c50 |
VIOCompressionState state;
|
|
Packit |
b55c50 |
for (;;) {
|
|
Packit |
b55c50 |
state = getCompressionState(dataVIO);
|
|
Packit |
b55c50 |
if (state.mayNotCompress || (state.status == VIO_POST_PACKER)) {
|
|
Packit |
b55c50 |
// This DataVIO is already set up to not block in the packer.
|
|
Packit |
b55c50 |
break;
|
|
Packit |
b55c50 |
}
|
|
Packit |
b55c50 |
|
|
Packit |
b55c50 |
VIOCompressionState newState = {
|
|
Packit |
b55c50 |
.status = state.status,
|
|
Packit |
b55c50 |
.mayNotCompress = true,
|
|
Packit |
b55c50 |
};
|
|
Packit |
b55c50 |
if (setCompressionState(dataVIO, state, newState)) {
|
|
Packit |
b55c50 |
break;
|
|
Packit |
b55c50 |
}
|
|
Packit |
b55c50 |
}
|
|
Packit |
b55c50 |
|
|
Packit |
b55c50 |
return ((state.status == VIO_PACKING) && !state.mayNotCompress);
|
|
Packit |
b55c50 |
}
|