1 /*****************************************************************************
2 
3 Copyright (c) 1995, 2020, Oracle and/or its affiliates.
4 
5 This program is free software; you can redistribute it and/or modify it under
6 the terms of the GNU General Public License, version 2.0, as published by the
7 Free Software Foundation.
8 
9 This program is also distributed with certain software (including but not
10 limited to OpenSSL) that is licensed under separate terms, as designated in a
11 particular file or component or in included license documentation. The authors
12 of MySQL hereby grant you an additional permission to link the program and
13 your derivative works with the separately licensed software that they have
14 included with MySQL.
15 
16 This program is distributed in the hope that it will be useful, but WITHOUT
17 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
18 FOR A PARTICULAR PURPOSE. See the GNU General Public License, version 2.0,
19 for more details.
20 
21 You should have received a copy of the GNU General Public License along with
22 this program; if not, write to the Free Software Foundation, Inc.,
23 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
24 
25 *****************************************************************************/
26 
27 /** @file include/buf0dblwr.h
28  Doublewrite buffer module
29 
30  Created 2011/12/19 Inaam Rana
31  *******************************************************/
32 
33 #ifndef buf0dblwr_h
34 #define buf0dblwr_h
35 
36 #include "buf0types.h"
37 #include "log0log.h"
38 #include "log0recv.h"
39 #include "ut0byte.h"
40 
41 /** Size of the doublewrite block in pages. */
42 #define DBLWR_V1_EXTENT_SIZE FSP_EXTENT_SIZE
43 
44 /** Offset of the doublewrite buffer header on the trx system header page.  */
45 #define TRX_SYS_DBLWR_V1 (UNIV_PAGE_SIZE - 200)
46 
47 /** 4-byte ver number which shows if we have created the doublewrite buffer. */
48 constexpr ulint DBLWR_VER = FSEG_HEADER_SIZE;
49 
50 /** Page number of the first page in the first sequence of 64 (=
51 FSP_EXTENT_SIZE) consecutive pages in the doublewrite buffer. */
52 constexpr ulint DBLWR_V1_BLOCK1 = (4 + FSEG_HEADER_SIZE);
53 
54 /** Page number of the first page in the second sequence of 64 consecutive
55 pages in the doublewrite buffer. */
56 constexpr ulint DBLWR_V1_BLOCK2 = (8 + FSEG_HEADER_SIZE);
57 
58 namespace dblwr {
59 /** IO buffer in UNIV_PAGE_SIZE units and aligned on UNIV_PAGE_SIZE */
60 struct Buffer {
61   /** Constructor
62   @param[in]	n_pages		        Number of pages to create */
BufferBuffer63   explicit Buffer(size_t n_pages) noexcept
64       : m_n_bytes(n_pages * univ_page_size.physical()) {
65     ut_a(n_pages > 0);
66 
67     auto n_bytes = m_n_bytes + univ_page_size.physical();
68 
69     m_ptr_unaligned = static_cast<byte *>(ut_zalloc_nokey(n_bytes));
70 
71     m_ptr = static_cast<byte *>(ut_align(m_ptr_unaligned, UNIV_PAGE_SIZE));
72 
73     ut_a(ptrdiff_t(m_ptr - m_ptr_unaligned) <=
74          (ssize_t)univ_page_size.physical());
75 
76     m_next = m_ptr;
77   }
78 
79   /** Destructor */
~BufferBuffer80   ~Buffer() noexcept {
81     if (m_ptr_unaligned != nullptr) {
82       ut_free(m_ptr_unaligned);
83     }
84     m_ptr_unaligned = nullptr;
85   }
86 
87   /** Add the contents of ptr upto n_bytes to the buffer.
88   @return false if it won't fit. Nothing is copied if it won't fit. */
appendBuffer89   bool append(const void *ptr, size_t n_bytes) noexcept {
90     ut_a(m_next >= m_ptr && m_next <= m_ptr + m_n_bytes);
91 
92     if (m_next + univ_page_size.physical() > m_ptr + m_n_bytes) {
93       return false;
94     }
95 
96     memcpy(m_next, ptr, n_bytes);
97     m_next += univ_page_size.physical();
98 
99     return true;
100   }
101 
102   /** @return the start of the buffer to write from. */
beginBuffer103   byte *begin() noexcept { return m_ptr; }
104 
105   /** @return the start of the buffer to write from. */
beginBuffer106   const byte *begin() const noexcept { return m_ptr; }
107 
108   /** @return the size of of the buffer to write. */
sizeBuffer109   size_t size() const noexcept {
110     ut_a(m_next >= m_ptr);
111     return std::ptrdiff_t(m_next - m_ptr);
112   }
113 
114   /** @return the capacity of the buffer in bytes. */
capacityBuffer115   size_t capacity() const noexcept { return m_n_bytes; }
116 
117   /** @return true if the buffer is empty. */
emptyBuffer118   bool empty() const noexcept { return size() == 0; }
119 
120   /** Empty the buffer. */
clearBuffer121   void clear() noexcept { m_next = m_ptr; }
122 
123   /** Write buffer used in writing to the doublewrite buffer,
124   aligned to an address divisible by UNIV_PAGE_SIZE (which is
125   required by Windows AIO) */
126   byte *m_ptr{};
127 
128   /** Start of  next write to the buffer. */
129   byte *m_next{};
130 
131   /** Pointer to m_ptr, but unaligned */
132   byte *m_ptr_unaligned{};
133 
134   /** Size of the unaligned (raw) buffer. */
135   const size_t m_n_bytes{};
136 
137   // Disable copying
138   Buffer(const Buffer &) = delete;
139   Buffer(const Buffer &&) = delete;
140   Buffer &operator=(Buffer &&) = delete;
141   Buffer &operator=(const Buffer &) = delete;
142 };
143 
144 }  // namespace dblwr
145 
146 #ifndef UNIV_HOTBACKUP
147 
148 // Forward declaration
149 class buf_page_t;
150 class Double_write;
151 
152 namespace dblwr {
153 
154 /** Double write files location. */
155 extern std::string dir;
156 
157 #ifdef UNIV_DEBUG
158 /** Crash the server after writing this page to the data file. */
159 extern page_id_t Force_crash;
160 #endif /* UNIV_DEBUG */
161 
162 /** Startup the background thread(s) and create the instance.
163 @param[in]  create_new_db Create new database.
164 @return DB_SUCCESS or error code */
165 dberr_t open(bool create_new_db) noexcept MY_ATTRIBUTE((warn_unused_result));
166 
167 /** Shutdown the background thread and destroy the instance */
168 void close() noexcept;
169 
170 /** Force a write of all pages in the queue.
171 @param[in] flush_type           FLUSH LIST or LRU_LIST flush request.
172 @param[in] buf_pool_index       Buffer pool instance for which called. */
173 void force_flush(buf_flush_t flush_type, uint32_t buf_pool_index) noexcept;
174 
175 /** Writes a page to the doublewrite buffer on disk, syncs it,
176 then writes the page to the datafile.
177 @param[in]  flush_type          Flush type
178 @param[in]	bpage		            Buffer block to write
179 @param[in]	sync		            True if sync IO requested
180 @return DB_SUCCESS or error code */
181 dberr_t write(buf_flush_t flush_type, buf_page_t *bpage, bool sync) noexcept
182     MY_ATTRIBUTE((warn_unused_result));
183 
184 /** Updates the double write buffer when a write request is completed.
185 @param[in] bpage               Block that has just been writtent to disk.
186 @param[in] flush_type          Flush type that triggered the write. */
187 void write_complete(buf_page_t *bpage, buf_flush_t flush_type) noexcept;
188 
189 /** Delete or adjust the dblwr file size if required. */
190 void reset_files() noexcept;
191 
192 namespace v1 {
193 /** Read the boundaries of the legacy dblwr buffer extents.
194 @return DB_SUCCESS or error code. */
195 dberr_t init() noexcept MY_ATTRIBUTE((warn_unused_result));
196 
197 /** Create the dblwr data structures in the system tablespace.
198 @return DB_SUCCESS or error code. */
199 dberr_t create() noexcept MY_ATTRIBUTE((warn_unused_result));
200 
201 /** Check if the read is of a page inside the legacy dblwr buffer.
202 @param[in] page_no              Page number to check.
203 @return true if offset inside legacy dblwr buffer. */
204 // clang-format off
205 bool is_inside(page_no_t page_no) noexcept
206     MY_ATTRIBUTE((warn_unused_result));
207 // clang-format on
208 
209 }  // namespace v1
210 }  // namespace dblwr
211 
212 #endif /* !UNIV_HOTBACKUP */
213 
214 namespace dblwr {
215 
216 /** Number of pages per doublewrite thread/segment */
217 extern ulong n_pages;
218 
219 /** true if enabled. */
220 extern bool enabled;
221 
222 /** Number of files to use for the double write buffer. It must be <= than
223 the number of buffer pool instances. */
224 extern ulong n_files;
225 
226 /** Maximum number of pages to write in one batch. */
227 extern ulong batch_size;
228 
229 /** Toggle the doublewrite buffer. */
230 void set();
231 
232 namespace recv {
233 
234 class Pages;
235 
236 /** Create the recovery dblwr data structures
237 @param[out]	pages		Pointer to newly created instance */
238 void create(Pages *&pages) noexcept;
239 
240 /** Load the doublewrite buffer pages.
241 @param[in,out] pages           For storing the doublewrite pages read
242                                from the double write buffer
243 @return DB_SUCCESS or error code */
244 dberr_t load(Pages *pages) noexcept MY_ATTRIBUTE((warn_unused_result));
245 
246 /** Restore pages from the double write buffer to the tablespace.
247 @param[in,out]	pages		Pages from the doublewrite buffer
248 @param[in]	space		Tablespace pages to restore, if set
249                                 to nullptr then try and restore all. */
250 void recover(Pages *pages, fil_space_t *space) noexcept;
251 
252 /** Find a doublewrite copy of a page.
253 @param[in]	pages		Pages read from the doublewrite buffer
254 @param[in]	page_id		Page number to lookup
255 @return	page frame
256 @retval NULL if no page was found */
257 const byte *find(const Pages *pages, const page_id_t &page_id) noexcept
258     MY_ATTRIBUTE((warn_unused_result));
259 
260 /** Check if some pages from the double write buffer could not be
261 restored because of the missing tablespace IDs.
262 @param[in]	pages		Pages to check */
263 void check_missing_tablespaces(const Pages *pages) noexcept;
264 
265 /** Free the recovery dblwr data structures
266 @param[out]	pages		Free the instance */
267 void destroy(Pages *&pages) noexcept;
268 
269 /** Redo recovery configuration. */
270 class DBLWR {
271  public:
272   /** Constructor. */
DBLWR()273   explicit DBLWR() noexcept { create(m_pages); }
274 
275   /** Destructor */
~DBLWR()276   ~DBLWR() noexcept { destroy(m_pages); }
277 
278   /** @return true if empty. */
empty()279   bool empty() const noexcept { return (m_pages == nullptr); }
280 
281   /** Load the doublewrite buffer pages. Doesn't create the doublewrite
282   @return DB_SUCCESS or error code */
load()283   dberr_t load() noexcept MY_ATTRIBUTE((warn_unused_result)) {
284     return (dblwr::recv::load(m_pages));
285   }
286 
287   /** Restore pages from the double write buffer to the tablespace.
288   @param[in]	space		Tablespace pages to restore,
289                                   if set to nullptr then try
290                                   and restore all. */
291   void recover(fil_space_t *space = nullptr) noexcept {
292     dblwr::recv::recover(m_pages, space);
293   }
294 
295   // clang-format off
296   /** Find a doublewrite copy of a page.
297   @param[in]	page_id		Page number to lookup
298   @return	page frame
299   @retval nullptr if no page was found */
find(const page_id_t & page_id)300   const byte *find(const page_id_t &page_id) noexcept
301       MY_ATTRIBUTE((warn_unused_result)) {
302     return (dblwr::recv::find(m_pages, page_id));
303   }
304   // clang-format on
305 
306   /** Check if some pages from the double write buffer
307   could not be restored because of the missing tablespace IDs. */
check_missing_tablespaces()308   void check_missing_tablespaces() noexcept {
309     dblwr::recv::check_missing_tablespaces(m_pages);
310   }
311 
312 #ifndef UNIV_HOTBACKUP
313   /** Note that recovery is complete. Adjust the file sizes if necessary. */
recovered()314   void recovered() noexcept { dblwr::reset_files(); }
315 #endif /* !UNIV_HOTBACKUP */
316 
317   /** Disably copying. */
318   DBLWR(const DBLWR &) = delete;
319   DBLWR(const DBLWR &&) = delete;
320   DBLWR &operator=(DBLWR &&) = delete;
321   DBLWR &operator=(const DBLWR &) = delete;
322 
323  private:
324   /** Pages read from the double write file. */
325   Pages *m_pages{};
326 };
327 }  // namespace recv
328 }  // namespace dblwr
329 
330 #endif /* buf0dblwr_h */
331