summaryrefslogtreecommitdiffstats
path: root/mysys/my_atomic_writes.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 18:00:34 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 18:00:34 +0000
commit3f619478f796eddbba6e39502fe941b285dd97b1 (patch)
treee2c7b5777f728320e5b5542b6213fd3591ba51e2 /mysys/my_atomic_writes.c
parentInitial commit. (diff)
downloadmariadb-3f619478f796eddbba6e39502fe941b285dd97b1.tar.xz
mariadb-3f619478f796eddbba6e39502fe941b285dd97b1.zip
Adding upstream version 1:10.11.6.upstream/1%10.11.6upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'mysys/my_atomic_writes.c')
-rw-r--r--mysys/my_atomic_writes.c533
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__ */