1/* Copyright (c) 2016, MariaDB Corporation
2
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; version 2 of the License.
6
7 This program is distributed in the hope that it will be useful,
8 but WITHOUT ANY WARRANTY; without even the implied warranty of
9 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 GNU General Public License for more details.
11
12 You should have received a copy of the GNU General Public License
13 along with this program; if not, write to the Free Software
14 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
15
16#include "mysys_priv.h"
17
18my_bool my_may_have_atomic_write= 0;
19
20#ifdef __linux__
21
22my_bool has_shannon_atomic_write= 0, has_fusion_io_atomic_write= 0;
23
24#include <sys/ioctl.h>
25
26
27/***********************************************************************
28 FUSION_IO
29************************************************************************/
30
31/** FusionIO atomic write control info */
32#define DFS_IOCTL_ATOMIC_WRITE_SET _IOW(0x95, 2, uint)
33
34
35/**
36 Check if the system has a funsion_io card
37 @return TRUE Card exists
38*/
39
40static my_bool test_if_fusion_io_card_exists()
41{
42 /* Fusion card requires fallocate to exists */
43#ifndef HAVE_POSIX_FALLOCATE
44 return 0;
45#else
46 return (access("/dev/fcta", F_OK)) == 0;
47#endif
48}
49
50
51/**
52 Check if a file is on a Fusion_IO device and that it supports atomic_write
53 @param[in] file OS file handle
54 @param[in] page_size page size
55 @return TRUE Atomic write supported
56*/
57
58static my_bool fusion_io_has_atomic_write(File file, int page_size)
59{
60 int atomic= 1;
61 if (page_size <= 32768 &&
62 ioctl(file, DFS_IOCTL_ATOMIC_WRITE_SET, &atomic) != -1)
63 return(TRUE);
64 return(FALSE);
65}
66
67
68/***********************************************************************
69 SHANNON
70************************************************************************/
71
72#define SHANNON_IOMAGIC 'x'
73#define SHANNON_IOCQATOMIC_SIZE _IO(SHANNON_IOMAGIC, 22)
74
75#define SHANNON_MAX_DEVICES 32
76#define SHANNON_NO_ATOMIC_SIZE_YET -2
77
78struct shannon_dev
79{
80 char dev_name[32];
81 dev_t st_dev;
82 int atomic_size;
83};
84
85
86static struct shannon_dev shannon_devices[SHANNON_MAX_DEVICES+1];
87
88/**
89 Check if the system has a Shannon card
90 If card exists, record device numbers to allow us to later check if
91 a given file is on this device.
92 @return TRUE Card exists
93*/
94
95static my_bool test_if_shannon_card_exists()
96{
97 uint shannon_found_devices= 0;
98 char dev_part;
99 uint dev_no;
100
101 if (access("/dev/scta", F_OK) < 0)
102 return 0;
103
104 /*
105 The Shannon devices are /dev/dfX, where X can be from a-z.
106 We have to check all of them as some may be missing if the user
107 removed one with the U.2 interface.
108 */
109
110 for (dev_part= 'a' ; dev_part < 'z' ; dev_part++)
111 {
112 char path[32];
113 struct stat stat_buff;
114
115 sprintf(path, "/dev/df%c", dev_part);
116#ifdef TEST_SHANNON
117 if (lstat(path, &stat_buff) < 0)
118 {
119 printf("%s(): lstat failed.\n", __func__);
120 break;
121 }
122#endif
123 shannon_devices[shannon_found_devices].st_dev= stat_buff.st_rdev;
124 sprintf(shannon_devices[shannon_found_devices].dev_name, "/dev/sct%c",
125 dev_part);
126
127#ifdef TEST_SHANNON
128 printf("%s(): i=%d, stat_buff.st_dev=0x%lx, stat_buff.st_rdev=0x%lx, st_rdev=0x%lx, dev_name=%s\n",
129 __func__,
130 shannon_found_devices,
131 (ulong) stat_buff.st_dev,
132 (ulong) stat_buff.st_rdev,
133 (ulong) shannon_devices[shannon_found_devices].st_dev,
134 shannon_devices[shannon_found_devices].dev_name);
135#endif
136
137 /*
138 The atomic size will be checked on first access. This is needed
139 as a normal user can't open the /dev/scta file
140 */
141 shannon_devices[shannon_found_devices].atomic_size=
142 SHANNON_NO_ATOMIC_SIZE_YET;
143 if (++shannon_found_devices== SHANNON_MAX_DEVICES)
144 goto end;
145
146 for (dev_no= 1 ; dev_no < 9 ; dev_no++)
147 {
148 sprintf(path, "/dev/df%c%d", dev_part, dev_no);
149 if (lstat(path, &stat_buff) < 0)
150 break;
151
152 shannon_devices[shannon_found_devices].st_dev= stat_buff.st_rdev;
153 sprintf(shannon_devices[shannon_found_devices].dev_name, "/dev/sct%c%d",
154 dev_part, dev_no);
155
156#ifdef TEST_SHANNON
157 printf("%s(): i=%d, st_dev=0x%lx, st_rdev=0x%lx, dev_name=%s\n",
158 __func__,
159 shannon_found_devices,
160 (ulong) stat_buff.st_dev,
161 (ulong) shannon_devices[shannon_found_devices].st_dev,
162 shannon_devices[shannon_found_devices].dev_name);
163#endif
164
165 /*
166 The atomic size will be checked on first access. This is needed
167 as a normal user can't open the /dev/scta file
168 */
169 shannon_devices[shannon_found_devices].atomic_size=
170 SHANNON_NO_ATOMIC_SIZE_YET;
171 if (++shannon_found_devices == SHANNON_MAX_DEVICES)
172 goto end;
173 }
174 }
175end:
176 shannon_devices[shannon_found_devices].st_dev= 0;
177 return shannon_found_devices > 0;
178}
179
180
181static my_bool shannon_dev_has_atomic_write(struct shannon_dev *dev,
182 int page_size)
183{
184#ifdef TEST_SHANNON
185 printf("%s: enter: page_size=%d, atomic_size=%d, dev_name=%s\n",
186 __func__,
187 page_size,
188 dev->atomic_size,
189 dev->dev_name);
190#endif
191 if (dev->atomic_size == SHANNON_NO_ATOMIC_SIZE_YET)
192 {
193 int fd= open(dev->dev_name, 0);
194 if (fd < 0)
195 {
196 perror("open() failed!");
197 dev->atomic_size= 0; /* Don't try again */
198 return FALSE;
199 }
200 dev->atomic_size= ioctl(fd, SHANNON_IOCQATOMIC_SIZE);
201 close(fd);
202 }
203
204#ifdef TEST_SHANNON
205 printf("%s: exit: page_size=%d, atomic_size=%d, dev_name=%s\n",
206 __func__,
207 page_size,
208 dev->atomic_size,
209 dev->dev_name);
210#endif
211 return (page_size <= dev->atomic_size);
212}
213
214
215/**
216 Check if a file is on a Shannon device and that it supports atomic_write
217 @param[in] file OS file handle
218 @param[in] page_size page size
219 @return TRUE Atomic write supported
220
221 @notes
222 This is called only at first open of a file. In this case it's doesn't
223 matter so much that we loop over all cards.
224 We update the atomic size on first access.
225*/
226
227static my_bool shannon_has_atomic_write(File file, int page_size)
228{
229 struct shannon_dev *dev;
230 struct stat stat_buff;
231
232 if (fstat(file, &stat_buff) < 0)
233 {
234#ifdef TEST_SHANNON
235 printf("%s(): fstat failed\n", __func__);
236#endif
237 return 0;
238 }
239
240#ifdef TEST_SHANNON
241 printf("%s(): st_dev=0x%lx, st_rdev=0x%lx\n", __func__,
242 (ulong) stat_buff.st_dev, (ulong) stat_buff.st_rdev);
243#endif
244
245 for (dev= shannon_devices ; dev->st_dev; dev++)
246 {
247#ifdef TEST_SHANNON
248 printf("%s(): st_rdev=0x%lx\n", __func__, (ulong) dev->st_dev);
249#endif
250 if (stat_buff.st_dev == dev->st_dev)
251 return shannon_dev_has_atomic_write(dev, page_size);
252 }
253 return 0;
254}
255
256
257/***********************************************************************
258 Generic atomic write code
259************************************************************************/
260
261/*
262 Initialize automic write sub systems.
263 Checks if we have any devices that supports atomic write
264*/
265
266void my_init_atomic_write(void)
267{
268 if ((has_shannon_atomic_write= test_if_shannon_card_exists()) ||
269 (has_fusion_io_atomic_write= test_if_fusion_io_card_exists()))
270 my_may_have_atomic_write= 1;
271#ifdef TEST_SHANNON
272 printf("%s(): has_shannon_atomic_write=%d, my_may_have_atomic_write=%d\n",
273 __func__,
274 has_shannon_atomic_write,
275 my_may_have_atomic_write);
276#endif
277}
278
279
280/**
281 Check if a file supports atomic write
282
283 @return FALSE No atomic write support
284 TRUE File supports atomic write
285*/
286
287my_bool my_test_if_atomic_write(File handle, int page_size)
288{
289#ifdef TEST_SHANNON
290 printf("%s(): has_shannon_atomic_write=%d, my_may_have_atomic_write=%d\n",
291 __func__,
292 has_shannon_atomic_write,
293 my_may_have_atomic_write);
294#endif
295 if (!my_may_have_atomic_write)
296 return 0;
297 if (has_shannon_atomic_write &&
298 shannon_has_atomic_write(handle, page_size))
299 return 1;
300
301 if (has_fusion_io_atomic_write &&
302 fusion_io_has_atomic_write(handle, page_size))
303 return 1;
304
305 return 0;
306}
307
308#ifdef TEST_SHANNON
309int main()
310{
311 int fd, ret;
312
313 my_init_atomic_write();
314 fd= open("/u01/1.file", O_RDWR);
315 ret= my_test_if_atomic_write(fd, 4096);
316 if (ret)
317 printf("support atomic_write\n");
318 else
319 printf("do not support atomic_write\n");
320 close(fd);
321 return 0;
322}
323#endif
324
325
326#else /* __linux__ */
327
328/* Dummy functions to provide the interfaces for other systems */
329
330void my_init_atomic_write(void)
331{
332}
333#endif /* __linux__ */
334