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