/*
* 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/uds-releases/jasper/kernelLinux/uds/ioFactoryLinuxKernel.c#9 $
*/
#include <linux/blkdev.h>
#include <linux/mount.h>
#include "atomicDefs.h"
#include "ioFactory.h"
#include "logger.h"
#include "memoryAlloc.h"
enum { BLK_FMODE = FMODE_READ | FMODE_WRITE };
/*
* A kernel mode IOFactory object controls access to an index stored on a block
* device.
*/
struct ioFactory {
struct block_device *bdev;
atomic_t refCount;
};
/*****************************************************************************/
void getIOFactory(IOFactory *factory)
{
atomic_inc(&factory->refCount);
}
/*****************************************************************************/
int makeIOFactory(const char *path, IOFactory **factoryPtr)
{
struct block_device *bdev;
dev_t device = name_to_dev_t(path);
if (device != 0) {
bdev = blkdev_get_by_dev(device, BLK_FMODE, NULL);
} else {
bdev = blkdev_get_by_path(path, BLK_FMODE, NULL);
}
if (IS_ERR(bdev)) {
logErrorWithStringError(-PTR_ERR(bdev), "%s is not a block device", path);
return UDS_INVALID_ARGUMENT;
}
IOFactory *factory;
int result = ALLOCATE(1, IOFactory, __func__, &factory);
if (result != UDS_SUCCESS) {
blkdev_put(bdev, BLK_FMODE);
return result;
}
factory->bdev = bdev;
atomic_set_release(&factory->refCount, 1);
*factoryPtr = factory;
return UDS_SUCCESS;
}
/*****************************************************************************/
void putIOFactory(IOFactory *factory)
{
if (atomic_add_return(-1, &factory->refCount) <= 0) {
blkdev_put(factory->bdev, BLK_FMODE);
FREE(factory);
}
}
/*****************************************************************************/
size_t getWritableSize(IOFactory *factory)
{
return i_size_read(factory->bdev->bd_inode);
}
/*****************************************************************************/
int makeBufio(IOFactory *factory,
off_t offset,
size_t blockSize,
unsigned int reservedBuffers,
struct dm_bufio_client **clientPtr)
{
if (offset % SECTOR_SIZE != 0) {
return logErrorWithStringError(UDS_INCORRECT_ALIGNMENT,
"offset %zd not multiple of %d",
offset, SECTOR_SIZE);
}
if (blockSize % UDS_BLOCK_SIZE != 0) {
return logErrorWithStringError(UDS_INCORRECT_ALIGNMENT,
"blockSize %zd not multiple of %d",
blockSize, UDS_BLOCK_SIZE);
}
struct dm_bufio_client *client = dm_bufio_client_create(factory->bdev,
blockSize,
reservedBuffers, 0,
NULL, NULL);
if (IS_ERR(client)) {
return -PTR_ERR(client);
}
dm_bufio_set_sector_offset(client, offset >> SECTOR_SHIFT);
*clientPtr = client;
return UDS_SUCCESS;
}
/*****************************************************************************/
int openBufferedReader(IOFactory *factory,
off_t offset,
size_t size,
BufferedReader **readerPtr)
{
if (size % UDS_BLOCK_SIZE != 0) {
return logErrorWithStringError(UDS_INCORRECT_ALIGNMENT,
"region size %zd is not multiple of %d",
size, UDS_BLOCK_SIZE);
}
struct dm_bufio_client *client = NULL;
int result = makeBufio(factory, offset, UDS_BLOCK_SIZE, 1, &client);
if (result != UDS_SUCCESS) {
return result;
}
result = makeBufferedReader(factory, client, size / UDS_BLOCK_SIZE,
readerPtr);
if (result != UDS_SUCCESS) {
dm_bufio_client_destroy(client);
}
return result;
}
/*****************************************************************************/
int openBufferedWriter(IOFactory *factory,
off_t offset,
size_t size,
BufferedWriter **writerPtr)
{
if (size % UDS_BLOCK_SIZE != 0) {
return logErrorWithStringError(UDS_INCORRECT_ALIGNMENT,
"region size %zd is not multiple of %d",
size, UDS_BLOCK_SIZE);
}
struct dm_bufio_client *client = NULL;
int result = makeBufio(factory, offset, UDS_BLOCK_SIZE, 1, &client);
if (result != UDS_SUCCESS) {
return result;
}
result = makeBufferedWriter(factory, client, size / UDS_BLOCK_SIZE,
writerPtr);
if (result != UDS_SUCCESS) {
dm_bufio_client_destroy(client);
}
return result;
}