Commit | Line | Data |
---|---|---|
6f643c57 SR |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * Copyright (c) 2022 Fujitsu. All Rights Reserved. | |
4 | */ | |
5 | ||
6 | #include "xfs.h" | |
7 | #include "xfs_shared.h" | |
8 | #include "xfs_format.h" | |
9 | #include "xfs_log_format.h" | |
10 | #include "xfs_trans_resv.h" | |
11 | #include "xfs_mount.h" | |
12 | #include "xfs_alloc.h" | |
13 | #include "xfs_bit.h" | |
14 | #include "xfs_btree.h" | |
15 | #include "xfs_inode.h" | |
16 | #include "xfs_icache.h" | |
17 | #include "xfs_rmap.h" | |
18 | #include "xfs_rmap_btree.h" | |
19 | #include "xfs_rtalloc.h" | |
20 | #include "xfs_trans.h" | |
6614a3c3 | 21 | #include "xfs_ag.h" |
6f643c57 SR |
22 | |
23 | #include <linux/mm.h> | |
24 | #include <linux/dax.h> | |
25 | ||
e033f40b | 26 | struct xfs_failure_info { |
6f643c57 SR |
27 | xfs_agblock_t startblock; |
28 | xfs_extlen_t blockcount; | |
29 | int mf_flags; | |
e033f40b | 30 | bool want_shutdown; |
6f643c57 SR |
31 | }; |
32 | ||
33 | static pgoff_t | |
34 | xfs_failure_pgoff( | |
35 | struct xfs_mount *mp, | |
36 | const struct xfs_rmap_irec *rec, | |
e033f40b | 37 | const struct xfs_failure_info *notify) |
6f643c57 SR |
38 | { |
39 | loff_t pos = XFS_FSB_TO_B(mp, rec->rm_offset); | |
40 | ||
41 | if (notify->startblock > rec->rm_startblock) | |
42 | pos += XFS_FSB_TO_B(mp, | |
43 | notify->startblock - rec->rm_startblock); | |
44 | return pos >> PAGE_SHIFT; | |
45 | } | |
46 | ||
47 | static unsigned long | |
48 | xfs_failure_pgcnt( | |
49 | struct xfs_mount *mp, | |
50 | const struct xfs_rmap_irec *rec, | |
e033f40b | 51 | const struct xfs_failure_info *notify) |
6f643c57 SR |
52 | { |
53 | xfs_agblock_t end_rec; | |
54 | xfs_agblock_t end_notify; | |
55 | xfs_agblock_t start_cross; | |
56 | xfs_agblock_t end_cross; | |
57 | ||
58 | start_cross = max(rec->rm_startblock, notify->startblock); | |
59 | ||
60 | end_rec = rec->rm_startblock + rec->rm_blockcount; | |
61 | end_notify = notify->startblock + notify->blockcount; | |
62 | end_cross = min(end_rec, end_notify); | |
63 | ||
64 | return XFS_FSB_TO_B(mp, end_cross - start_cross) >> PAGE_SHIFT; | |
65 | } | |
66 | ||
67 | static int | |
68 | xfs_dax_failure_fn( | |
69 | struct xfs_btree_cur *cur, | |
70 | const struct xfs_rmap_irec *rec, | |
71 | void *data) | |
72 | { | |
73 | struct xfs_mount *mp = cur->bc_mp; | |
74 | struct xfs_inode *ip; | |
e033f40b | 75 | struct xfs_failure_info *notify = data; |
6f643c57 SR |
76 | int error = 0; |
77 | ||
78 | if (XFS_RMAP_NON_INODE_OWNER(rec->rm_owner) || | |
79 | (rec->rm_flags & (XFS_RMAP_ATTR_FORK | XFS_RMAP_BMBT_BLOCK))) { | |
e033f40b DW |
80 | notify->want_shutdown = true; |
81 | return 0; | |
6f643c57 SR |
82 | } |
83 | ||
84 | /* Get files that incore, filter out others that are not in use. */ | |
85 | error = xfs_iget(mp, cur->bc_tp, rec->rm_owner, XFS_IGET_INCORE, | |
86 | 0, &ip); | |
87 | /* Continue the rmap query if the inode isn't incore */ | |
88 | if (error == -ENODATA) | |
89 | return 0; | |
e033f40b DW |
90 | if (error) { |
91 | notify->want_shutdown = true; | |
92 | return 0; | |
93 | } | |
6f643c57 SR |
94 | |
95 | error = mf_dax_kill_procs(VFS_I(ip)->i_mapping, | |
96 | xfs_failure_pgoff(mp, rec, notify), | |
97 | xfs_failure_pgcnt(mp, rec, notify), | |
98 | notify->mf_flags); | |
99 | xfs_irele(ip); | |
100 | return error; | |
101 | } | |
102 | ||
103 | static int | |
104 | xfs_dax_notify_ddev_failure( | |
105 | struct xfs_mount *mp, | |
106 | xfs_daddr_t daddr, | |
107 | xfs_daddr_t bblen, | |
108 | int mf_flags) | |
109 | { | |
e033f40b | 110 | struct xfs_failure_info notify = { .mf_flags = mf_flags }; |
6f643c57 SR |
111 | struct xfs_trans *tp = NULL; |
112 | struct xfs_btree_cur *cur = NULL; | |
113 | struct xfs_buf *agf_bp = NULL; | |
114 | int error = 0; | |
115 | xfs_fsblock_t fsbno = XFS_DADDR_TO_FSB(mp, daddr); | |
116 | xfs_agnumber_t agno = XFS_FSB_TO_AGNO(mp, fsbno); | |
117 | xfs_fsblock_t end_fsbno = XFS_DADDR_TO_FSB(mp, daddr + bblen); | |
118 | xfs_agnumber_t end_agno = XFS_FSB_TO_AGNO(mp, end_fsbno); | |
119 | ||
120 | error = xfs_trans_alloc_empty(mp, &tp); | |
121 | if (error) | |
122 | return error; | |
123 | ||
124 | for (; agno <= end_agno; agno++) { | |
125 | struct xfs_rmap_irec ri_low = { }; | |
126 | struct xfs_rmap_irec ri_high; | |
6f643c57 SR |
127 | struct xfs_agf *agf; |
128 | xfs_agblock_t agend; | |
6614a3c3 | 129 | struct xfs_perag *pag; |
6f643c57 | 130 | |
6614a3c3 LT |
131 | pag = xfs_perag_get(mp, agno); |
132 | error = xfs_alloc_read_agf(pag, tp, 0, &agf_bp); | |
133 | if (error) { | |
134 | xfs_perag_put(pag); | |
6f643c57 | 135 | break; |
6614a3c3 | 136 | } |
6f643c57 | 137 | |
6614a3c3 | 138 | cur = xfs_rmapbt_init_cursor(mp, tp, agf_bp, pag); |
6f643c57 SR |
139 | |
140 | /* | |
141 | * Set the rmap range from ri_low to ri_high, which represents | |
142 | * a [start, end] where we looking for the files or metadata. | |
143 | */ | |
144 | memset(&ri_high, 0xFF, sizeof(ri_high)); | |
145 | ri_low.rm_startblock = XFS_FSB_TO_AGBNO(mp, fsbno); | |
146 | if (agno == end_agno) | |
147 | ri_high.rm_startblock = XFS_FSB_TO_AGBNO(mp, end_fsbno); | |
148 | ||
149 | agf = agf_bp->b_addr; | |
150 | agend = min(be32_to_cpu(agf->agf_length), | |
151 | ri_high.rm_startblock); | |
152 | notify.startblock = ri_low.rm_startblock; | |
153 | notify.blockcount = agend - ri_low.rm_startblock; | |
154 | ||
155 | error = xfs_rmap_query_range(cur, &ri_low, &ri_high, | |
156 | xfs_dax_failure_fn, ¬ify); | |
157 | xfs_btree_del_cursor(cur, error); | |
158 | xfs_trans_brelse(tp, agf_bp); | |
6614a3c3 | 159 | xfs_perag_put(pag); |
6f643c57 SR |
160 | if (error) |
161 | break; | |
162 | ||
163 | fsbno = XFS_AGB_TO_FSB(mp, agno + 1, 0); | |
164 | } | |
165 | ||
166 | xfs_trans_cancel(tp); | |
e033f40b DW |
167 | if (error || notify.want_shutdown) { |
168 | xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_ONDISK); | |
169 | if (!error) | |
170 | error = -EFSCORRUPTED; | |
171 | } | |
6f643c57 SR |
172 | return error; |
173 | } | |
174 | ||
175 | static int | |
176 | xfs_dax_notify_failure( | |
177 | struct dax_device *dax_dev, | |
178 | u64 offset, | |
179 | u64 len, | |
180 | int mf_flags) | |
181 | { | |
182 | struct xfs_mount *mp = dax_holder(dax_dev); | |
183 | u64 ddev_start; | |
184 | u64 ddev_end; | |
185 | ||
fd63612a | 186 | if (!(mp->m_super->s_flags & SB_BORN)) { |
6f643c57 SR |
187 | xfs_warn(mp, "filesystem is not ready for notify_failure()!"); |
188 | return -EIO; | |
189 | } | |
190 | ||
191 | if (mp->m_rtdev_targp && mp->m_rtdev_targp->bt_daxdev == dax_dev) { | |
b14d067e | 192 | xfs_debug(mp, |
6f643c57 SR |
193 | "notify_failure() not supported on realtime device!"); |
194 | return -EOPNOTSUPP; | |
195 | } | |
196 | ||
197 | if (mp->m_logdev_targp && mp->m_logdev_targp->bt_daxdev == dax_dev && | |
198 | mp->m_logdev_targp != mp->m_ddev_targp) { | |
199 | xfs_err(mp, "ondisk log corrupt, shutting down fs!"); | |
200 | xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_ONDISK); | |
201 | return -EFSCORRUPTED; | |
202 | } | |
203 | ||
204 | if (!xfs_has_rmapbt(mp)) { | |
b14d067e | 205 | xfs_debug(mp, "notify_failure() needs rmapbt enabled!"); |
6f643c57 SR |
206 | return -EOPNOTSUPP; |
207 | } | |
208 | ||
209 | ddev_start = mp->m_ddev_targp->bt_dax_part_off; | |
210 | ddev_end = ddev_start + bdev_nr_bytes(mp->m_ddev_targp->bt_bdev) - 1; | |
211 | ||
212 | /* Ignore the range out of filesystem area */ | |
213 | if (offset + len < ddev_start) | |
214 | return -ENXIO; | |
215 | if (offset > ddev_end) | |
216 | return -ENXIO; | |
217 | ||
218 | /* Calculate the real range when it touches the boundary */ | |
219 | if (offset > ddev_start) | |
220 | offset -= ddev_start; | |
221 | else { | |
222 | len -= ddev_start - offset; | |
223 | offset = 0; | |
224 | } | |
225 | if (offset + len > ddev_end) | |
226 | len -= ddev_end - offset; | |
227 | ||
228 | return xfs_dax_notify_ddev_failure(mp, BTOBB(offset), BTOBB(len), | |
229 | mf_flags); | |
230 | } | |
231 | ||
232 | const struct dax_holder_operations xfs_dax_holder_operations = { | |
233 | .notify_failure = xfs_dax_notify_failure, | |
234 | }; |