Skip to content

Commit

Permalink
Make atomic writes general
Browse files Browse the repository at this point in the history
- Atomic writes are enabled by default
- Automatically detect if device supports atomic write and use it if
  atomic writes are enabled
- Remove ATOMIC WRITE options from CREATE TABLE
  - Atomic write is a device option, not a table options as the table may
    crash if the media changes
- Add support for SHANNON SSD cards
  • Loading branch information
vuvova authored and montywi committed Jan 11, 2017
1 parent ed0bc17 commit ed008a7
Show file tree
Hide file tree
Showing 29 changed files with 474 additions and 378 deletions.
8 changes: 8 additions & 0 deletions include/my_sys.h
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,14 @@ extern void my_large_free(uchar *ptr);
#define my_large_free(A) my_free_lock((A))
#endif /* HAVE_LARGE_PAGES */

void my_init_atomic_write(void);
#ifdef __linux__
my_bool my_test_if_atomic_write(File handle, int pagesize);
#else
#define my_test_if_atomic_write(A, B) 0
#endif /* __linux__ */
extern my_bool my_may_have_atomic_write;

#if defined(HAVE_ALLOCA) && !defined(HAVE_valgrind)
#if defined(_AIX) && !defined(__GNUC__) && !defined(_AIX43)
#pragma alloca
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
select @@global.innodb_use_atomic_writes;
@@global.innodb_use_atomic_writes
0
1
select @@session.innodb_use_atomic_writes;
ERROR HY000: Variable 'innodb_use_atomic_writes' is a GLOBAL variable
show global variables like 'innodb_use_atomic_writes';
Variable_name Value
innodb_use_atomic_writes OFF
innodb_use_atomic_writes ON
show session variables like 'innodb_use_atomic_writes';
Variable_name Value
innodb_use_atomic_writes OFF
innodb_use_atomic_writes ON
select * from information_schema.global_variables where variable_name='innodb_use_atomic_writes';
VARIABLE_NAME VARIABLE_VALUE
INNODB_USE_ATOMIC_WRITES OFF
INNODB_USE_ATOMIC_WRITES ON
select * from information_schema.session_variables where variable_name='innodb_use_atomic_writes';
VARIABLE_NAME VARIABLE_VALUE
INNODB_USE_ATOMIC_WRITES OFF
INNODB_USE_ATOMIC_WRITES ON
set global innodb_use_atomic_writes=1;
ERROR HY000: Variable 'innodb_use_atomic_writes' is a read only variable
set session innodb_use_atomic_writes=1;
Expand Down
6 changes: 3 additions & 3 deletions mysql-test/suite/sys_vars/r/sysvars_innodb.result
Original file line number Diff line number Diff line change
Expand Up @@ -2584,12 +2584,12 @@ READ_ONLY YES
COMMAND_LINE_ARGUMENT REQUIRED
VARIABLE_NAME INNODB_USE_ATOMIC_WRITES
SESSION_VALUE NULL
GLOBAL_VALUE OFF
GLOBAL_VALUE ON
GLOBAL_VALUE_ORIGIN COMPILE-TIME
DEFAULT_VALUE OFF
DEFAULT_VALUE ON
VARIABLE_SCOPE GLOBAL
VARIABLE_TYPE BOOLEAN
VARIABLE_COMMENT Prevent partial page writes, via atomic writes.The option is used to prevent partial writes in case of a crash/poweroff, as faster alternative to doublewrite buffer.Currently this option works only on Linux only with FusionIO device, and directFS filesystem.
VARIABLE_COMMENT Enable atomic writes, instead of using the doublewrite buffer, for files on devices that supports atomic writes. To use this option one must use file_per_table=1, flush_method=O_DIRECT and use_fallocate=1. This option only works on Linux with either FusionIO cards using the directFS filesystem or with Shannon cards using any file system.
NUMERIC_MIN_VALUE NULL
NUMERIC_MAX_VALUE NULL
NUMERIC_BLOCK_SIZE NULL
Expand Down
1 change: 1 addition & 0 deletions mysys/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ SET(MYSYS_SOURCES array.c charset-def.c charset.c checksum.c my_default.c
my_atomic.c my_getncpus.c my_safehash.c my_chmod.c my_rnd.c
my_uuid.c wqueue.c waiting_threads.c ma_dyncol.c ../sql-common/my_time.c
my_rdtsc.c my_context.c psi_noop.c
my_atomic_writes.c
file_logger.c)

IF (WIN32)
Expand Down
333 changes: 333 additions & 0 deletions mysys/my_atomic_writes.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,333 @@
/* Copyright (c) 2016, 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= 0;

#ifdef __linux__

my_bool has_shannon_atomic_write= 0, has_fusion_io_atomic_write= 0;

#include <sys/ioctl.h>


/***********************************************************************
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;

sprintf(path, "/dev/df%c", dev_part);
#ifdef TEST_SHANNON
if (lstat(path, &stat_buff) < 0)
{
printf("%s(): lstat failed.\n", __func__);
break;
}
#endif
shannon_devices[shannon_found_devices].st_dev= stat_buff.st_rdev;
sprintf(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++)
{
sprintf(path, "/dev/df%c%d", dev_part, dev_no);
if (lstat(path, &stat_buff) < 0)
break;

shannon_devices[shannon_found_devices].st_dev= stat_buff.st_rdev;
sprintf(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)
{
perror("open() failed!");
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's 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 (stat_buff.st_dev == dev->st_dev)
return shannon_dev_has_atomic_write(dev, page_size);
}
return 0;
}


/***********************************************************************
Generic atomic write code
************************************************************************/

/*
Initalize automic write sub systems.
Checks if we have any devices that supports atomic write
*/

void my_init_atomic_write(void)
{
if ((has_shannon_atomic_write= test_if_shannon_card_exists()) ||
(has_fusion_io_atomic_write= test_if_fusion_io_card_exists()))
my_may_have_atomic_write= 1;
#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;

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__ */
Loading

0 comments on commit ed008a7

Please sign in to comment.