diff options
Diffstat (limited to 'mysys/my_atomic_writes.c')
-rw-r--r-- | mysys/my_atomic_writes.c | 533 |
1 files changed, 533 insertions, 0 deletions
diff --git a/mysys/my_atomic_writes.c b/mysys/my_atomic_writes.c new file mode 100644 index 00000000..90d0f1d0 --- /dev/null +++ b/mysys/my_atomic_writes.c @@ -0,0 +1,533 @@ +/* Copyright (c) 2016, 2021, MariaDB Corporation. + + 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; version 2 of the License. + + 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 St, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "mysys_priv.h" + +my_bool my_may_have_atomic_write= IF_WIN(1,0); + +#ifdef __linux__ + +my_bool has_shannon_atomic_write, has_fusion_io_atomic_write, + has_sfx_atomic_write; +my_bool has_sfx_card; + +#include <sys/ioctl.h> + +/* Linux seems to allow up to 15 partitions per block device. +Partition number 0 is the whole block device. */ +# define SAME_DEV(fs_dev, blk_dev) \ + (fs_dev == blk_dev) || ((fs_dev & ~15U) == blk_dev) + +/*********************************************************************** + FUSION_IO +************************************************************************/ + +/** FusionIO atomic write control info */ +#define DFS_IOCTL_ATOMIC_WRITE_SET _IOW(0x95, 2, uint) + + +/** + Check if the system has a funsion_io card + @return TRUE Card exists +*/ + +static my_bool test_if_fusion_io_card_exists() +{ + /* Fusion card requires fallocate to exists */ +#ifndef HAVE_POSIX_FALLOCATE + return 0; +#else + return (access("/dev/fcta", F_OK)) == 0; +#endif +} + + +/** + Check if a file is on a Fusion_IO device and that it supports atomic_write + @param[in] file OS file handle + @param[in] page_size page size + @return TRUE Atomic write supported +*/ + +static my_bool fusion_io_has_atomic_write(File file, int page_size) +{ + int atomic= 1; + if (page_size <= 32768 && + ioctl(file, DFS_IOCTL_ATOMIC_WRITE_SET, &atomic) != -1) + return(TRUE); + return(FALSE); +} + + +/*********************************************************************** + SHANNON +************************************************************************/ + +#define SHANNON_IOMAGIC 'x' +#define SHANNON_IOCQATOMIC_SIZE _IO(SHANNON_IOMAGIC, 22) + +#define SHANNON_MAX_DEVICES 32 +#define SHANNON_NO_ATOMIC_SIZE_YET -2 + +struct shannon_dev +{ + char dev_name[32]; + dev_t st_dev; + int atomic_size; +}; + + +static struct shannon_dev shannon_devices[SHANNON_MAX_DEVICES+1]; + +/** + Check if the system has a Shannon card + If card exists, record device numbers to allow us to later check if + a given file is on this device. + @return TRUE Card exists +*/ + +static my_bool test_if_shannon_card_exists() +{ + uint shannon_found_devices= 0; + char dev_part; + uint dev_no; + + if (access("/dev/scta", F_OK) < 0) + return 0; + + /* + The Shannon devices are /dev/dfX, where X can be from a-z. + We have to check all of them as some may be missing if the user + removed one with the U.2 interface. + */ + + for (dev_part= 'a' ; dev_part < 'z' ; dev_part++) + { + char path[32]; + struct stat stat_buff; + + snprintf(path, sizeof(path), "/dev/df%c", dev_part); +#ifdef TEST_SHANNON + if (stat(path, &stat_buff) < 0) + { + printf("%s(): stat %s failed.\n", __func__, path); + break; + } +#endif + shannon_devices[shannon_found_devices].st_dev= stat_buff.st_rdev; + snprintf(shannon_devices[shannon_found_devices].dev_name, + sizeof(shannon_devices[shannon_found_devices].dev_name), + "/dev/sct%c", + dev_part); + +#ifdef TEST_SHANNON + printf("%s(): i=%d, stat_buff.st_dev=0x%lx, stat_buff.st_rdev=0x%lx, st_rdev=0x%lx, dev_name=%s\n", + __func__, + shannon_found_devices, + (ulong) stat_buff.st_dev, + (ulong) stat_buff.st_rdev, + (ulong) shannon_devices[shannon_found_devices].st_dev, + shannon_devices[shannon_found_devices].dev_name); +#endif + + /* + The atomic size will be checked on first access. This is needed + as a normal user can't open the /dev/scta file + */ + shannon_devices[shannon_found_devices].atomic_size= + SHANNON_NO_ATOMIC_SIZE_YET; + if (++shannon_found_devices== SHANNON_MAX_DEVICES) + goto end; + + for (dev_no= 1 ; dev_no < 9 ; dev_no++) + { + snprintf(path, sizeof(path), "/dev/df%c%d", dev_part, dev_no); + if (stat(path, &stat_buff) < 0) + break; + + shannon_devices[shannon_found_devices].st_dev= stat_buff.st_rdev; + snprintf(shannon_devices[shannon_found_devices].dev_name, + sizeof(shannon_devices[shannon_found_devices].dev_name), + "/dev/sct%c%d", + dev_part, dev_no); + +#ifdef TEST_SHANNON + printf("%s(): i=%d, st_dev=0x%lx, st_rdev=0x%lx, dev_name=%s\n", + __func__, + shannon_found_devices, + (ulong) stat_buff.st_dev, + (ulong) shannon_devices[shannon_found_devices].st_dev, + shannon_devices[shannon_found_devices].dev_name); +#endif + + /* + The atomic size will be checked on first access. This is needed + as a normal user can't open the /dev/scta file + */ + shannon_devices[shannon_found_devices].atomic_size= + SHANNON_NO_ATOMIC_SIZE_YET; + if (++shannon_found_devices == SHANNON_MAX_DEVICES) + goto end; + } + } +end: + shannon_devices[shannon_found_devices].st_dev= 0; + return shannon_found_devices > 0; +} + + +static my_bool shannon_dev_has_atomic_write(struct shannon_dev *dev, + int page_size) +{ +#ifdef TEST_SHANNON + printf("%s: enter: page_size=%d, atomic_size=%d, dev_name=%s\n", + __func__, + page_size, + dev->atomic_size, + dev->dev_name); +#endif + if (dev->atomic_size == SHANNON_NO_ATOMIC_SIZE_YET) + { + int fd= open(dev->dev_name, 0); + if (fd < 0) + { + fprintf(stderr, "Unable to determine if atomic writes are supported:" + " open(\"%s\"): %m\n", dev->dev_name); + dev->atomic_size= 0; /* Don't try again */ + return FALSE; + } + dev->atomic_size= ioctl(fd, SHANNON_IOCQATOMIC_SIZE); + close(fd); + } + +#ifdef TEST_SHANNON + printf("%s: exit: page_size=%d, atomic_size=%d, dev_name=%s\n", + __func__, + page_size, + dev->atomic_size, + dev->dev_name); +#endif + return (page_size <= dev->atomic_size); +} + + +/** + Check if a file is on a Shannon device and that it supports atomic_write + @param[in] file OS file handle + @param[in] page_size page size + @return TRUE Atomic write supported + + @notes + This is called only at first open of a file. In this case it doesn't + matter so much that we loop over all cards. + We update the atomic size on first access. +*/ + +static my_bool shannon_has_atomic_write(File file, int page_size) +{ + struct shannon_dev *dev; + struct stat stat_buff; + + if (fstat(file, &stat_buff) < 0) + { +#ifdef TEST_SHANNON + printf("%s(): fstat failed\n", __func__); +#endif + return 0; + } + +#ifdef TEST_SHANNON + printf("%s(): st_dev=0x%lx, st_rdev=0x%lx\n", __func__, + (ulong) stat_buff.st_dev, (ulong) stat_buff.st_rdev); +#endif + + for (dev= shannon_devices ; dev->st_dev; dev++) + { +#ifdef TEST_SHANNON + printf("%s(): st_rdev=0x%lx\n", __func__, (ulong) dev->st_dev); +#endif + if (SAME_DEV(stat_buff.st_dev, dev->st_dev)) + return shannon_dev_has_atomic_write(dev, page_size); + } + return 0; +} + + +/*********************************************************************** + ScaleFlux +************************************************************************/ + +#define SFX_GET_ATOMIC_SIZE _IOR('N', 0x243, int) +#define SFX_MAX_DEVICES (32) +#define SFX_UNKNOWN_ATOMIC_WRITE_YET (-2) +#define SFX_MAX_ATOMIC_SIZE (256 * 1024) + +#define SFX_GET_SPACE_RATIO _IO('N', 0x244) +#define SFX_UNKNOWN_PUNCH_HOLE_YET (-3) + +/** + Threshold for logical_space / physical_space + No less than the threshold means we can disable hole punching +*/ +#define SFX_DISABLE_PUNCH_HOLE_RATIO (2) + +struct sfx_dev +{ + char dev_name[32]; + dev_t st_dev; + int atomic_write; + int disable_punch_hole; +}; + +static struct sfx_dev sfx_devices[SFX_MAX_DEVICES + 1]; + +/** + Check if the system has a ScaleFlux card + If card exists, record device numbers to allow us to later check if + a given file is on this device + Variables for atomic_write and disable_punch_hole will be initialized + @return TRUE Card exists +*/ + +static my_bool test_if_sfx_card_exists() +{ + uint sfx_found_devices = 0; + uint dev_num; + + for (dev_num = 0; dev_num < SFX_MAX_DEVICES; dev_num++) + { + struct stat stat_buff; + + sprintf(sfx_devices[sfx_found_devices].dev_name, "/dev/sfdv%dn1", + dev_num); + if (stat(sfx_devices[sfx_found_devices].dev_name, &stat_buff) < 0) + break; + + sfx_devices[sfx_found_devices].st_dev= stat_buff.st_rdev; + /* + The atomic size will be checked on first access. This is needed + as a normal user can't open the /dev/sfdvXn1 file + */ + sfx_devices[sfx_found_devices].atomic_write= SFX_UNKNOWN_ATOMIC_WRITE_YET; + sfx_devices[sfx_found_devices].disable_punch_hole= + SFX_UNKNOWN_PUNCH_HOLE_YET; + if (++sfx_found_devices == SFX_MAX_DEVICES) + goto end; + } +end: + sfx_devices[sfx_found_devices].st_dev= 0; + has_sfx_card = (sfx_found_devices > 0); + + return sfx_found_devices > 0; +} + +static my_bool sfx_dev_has_atomic_write(struct sfx_dev *dev, + int page_size) +{ + int result= -1, max_atomic_size= SFX_MAX_ATOMIC_SIZE; + + if (dev->atomic_write == SFX_UNKNOWN_ATOMIC_WRITE_YET) + { + int fd= open(dev->dev_name, 0); + if (fd < 0) + fprintf(stderr, "Unable to determine if atomic writes are supported:" + " open(\"%s\"): %m\n", dev->dev_name); + else + { + result= ioctl(fd, SFX_GET_ATOMIC_SIZE, &max_atomic_size); + close(fd); + } + dev->atomic_write= result == 0 && page_size <= max_atomic_size; + } + + return dev->atomic_write; +} + +/** + Check if a file is on a ScaleFlux device and that it supports atomic_write + @param[in] file OS file handle + @param[in] page_size page size + @return TRUE Atomic write supported + + @notes + This is called only at first open of a file. In this case it doesn't + matter so much that we loop over all cards. + We update the atomic size on first access. +*/ + +static my_bool sfx_has_atomic_write(File file, int page_size) +{ + struct sfx_dev *dev; + struct stat stat_buff; + + if (fstat(file, &stat_buff) == 0) + for (dev= sfx_devices; dev->st_dev; dev++) + if (SAME_DEV(stat_buff.st_dev, dev->st_dev)) + return sfx_dev_has_atomic_write(dev, page_size); + return 0; +} + +static my_bool sfx_dev_could_disable_punch_hole(struct sfx_dev *dev, File file) +{ + int result = 0; + + if (dev->disable_punch_hole == SFX_UNKNOWN_PUNCH_HOLE_YET) + { + int fd= open(dev->dev_name, 0); + if (fd < 0) + { + fprintf(stderr, "Unable to determine if thin provisioning is used:" + " open(\"%s\"): %m\n", dev->dev_name); + dev->disable_punch_hole= 0; /* Don't try again */ + return FALSE; + } + + /* + Ratio left-shifts 8 (multiplies 256) inside the ioctl; + will also add 1 to guarantee a round-up integer. + */ + result= ioctl(fd, SFX_GET_SPACE_RATIO); + result+= 1; + dev->disable_punch_hole= (result >= (((double)SFX_DISABLE_PUNCH_HOLE_RATIO) * 256)); + } + + return dev->disable_punch_hole; +} + +/** + Check if a file is on a ScaleFlux device and whether it is possible to + disable hole punch. + @param[in] file OS file handle + @return TRUE Could disable hole punch + + @notes + This is called only at first open of a file. In this case it's doesn't + matter so much that we loop over all cards +*/ + +static my_bool sfx_could_disable_punch_hole(File file) +{ + struct sfx_dev *dev; + struct stat stat_buff; + + if (fstat(file, &stat_buff) == 0) + for (dev = sfx_devices; dev->st_dev; dev++) + if (SAME_DEV(stat_buff.st_dev, dev->st_dev)) + return sfx_dev_could_disable_punch_hole(dev, file); + return 0; +} + +/*********************************************************************** + Generic atomic write code +************************************************************************/ + +/** + Initialize the atomic write subsystem. + Checks if we have any devices that supports atomic write +*/ + +void my_init_atomic_write(void) +{ + has_shannon_atomic_write= test_if_shannon_card_exists(); + has_fusion_io_atomic_write= test_if_fusion_io_card_exists(); + has_sfx_atomic_write= test_if_sfx_card_exists(); + + my_may_have_atomic_write= has_shannon_atomic_write || + has_fusion_io_atomic_write || has_sfx_atomic_write; + +#ifdef TEST_SHANNON + printf("%s(): has_shannon_atomic_write=%d, my_may_have_atomic_write=%d\n", + __func__, + has_shannon_atomic_write, + my_may_have_atomic_write); +#endif +} + + +/** + Check if a file supports atomic write + + @return FALSE No atomic write support + TRUE File supports atomic write +*/ + +my_bool my_test_if_atomic_write(File handle, int page_size) +{ +#ifdef TEST_SHANNON + printf("%s(): has_shannon_atomic_write=%d, my_may_have_atomic_write=%d\n", + __func__, + has_shannon_atomic_write, + my_may_have_atomic_write); +#endif + if (!my_may_have_atomic_write) + return 0; + + if (has_shannon_atomic_write && + shannon_has_atomic_write(handle, page_size)) + return 1; + + if (has_fusion_io_atomic_write && + fusion_io_has_atomic_write(handle, page_size)) + return 1; + + if (has_sfx_atomic_write && + sfx_has_atomic_write(handle, page_size)) + return 1; + + return 0; +} + + +/** + Check if a file resides on thinly provisioned storage. + + @return FALSE File cannot disable hole punch + TRUE File could disable hole punch +*/ + +my_bool my_test_if_thinly_provisioned(File handle) +{ + if (has_sfx_card && sfx_could_disable_punch_hole(handle)) + return 1; + + return 0; +} + +#ifdef TEST_SHANNON +int main() +{ + int fd, ret; + + my_init_atomic_write(); + fd= open("/u01/1.file", O_RDWR); + ret= my_test_if_atomic_write(fd, 4096); + if (ret) + printf("support atomic_write\n"); + else + printf("do not support atomic_write\n"); + close(fd); + return 0; +} +#endif + + +#else /* __linux__ */ + +/* Dummy functions to provide the interfaces for other systems */ + +void my_init_atomic_write(void) +{ +} +#endif /* __linux__ */ |