Skip to content

Commit 3365135

Browse files
committed
Merge tag 'xfs-for-linus-4.10-rc6-5' of git://git.kernel.org/pub/scm/fs/xfs/xfs-linux
Pull xfs uodates from Darrick Wong: "I have some more fixes this week: better input validation, corruption avoidance, build fixes, memory leak fixes, and a couple from Christoph to avoid an ENOSPC failure. Summary: - Fix race conditions in the CoW code - Fix some incorrect input validation checks - Avoid crashing fs by running out of space when freeing inodes - Fix toctou race wrt whether or not an inode has an attr - Fix build error on arm - Fix page refcount corruption when readahead fails - Don't corrupt userspace in the bmap ioctl" * tag 'xfs-for-linus-4.10-rc6-5' of git://git.kernel.org/pub/scm/fs/xfs/xfs-linux: xfs: prevent quotacheck from overloading inode lru xfs: fix bmv_count confusion w/ shared extents xfs: clear _XBF_PAGES from buffers when readahead page xfs: extsize hints are not unlikely in xfs_bmap_btalloc xfs: remove racy hasattr check from attr ops xfs: use per-AG reservations for the finobt xfs: only update mount/resv fields on success in __xfs_ag_resv_init xfs: verify dirblocklog correctly xfs: fix COW writeback race
2 parents 5906374 + e0d76fa commit 3365135

File tree

13 files changed

+220
-63
lines changed

13 files changed

+220
-63
lines changed

fs/xfs/libxfs/xfs_ag_resv.c

Lines changed: 55 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
#include "xfs_rmap_btree.h"
4040
#include "xfs_btree.h"
4141
#include "xfs_refcount_btree.h"
42+
#include "xfs_ialloc_btree.h"
4243

4344
/*
4445
* Per-AG Block Reservations
@@ -200,29 +201,39 @@ __xfs_ag_resv_init(
200201
struct xfs_mount *mp = pag->pag_mount;
201202
struct xfs_ag_resv *resv;
202203
int error;
204+
xfs_extlen_t reserved;
203205

204-
resv = xfs_perag_resv(pag, type);
205206
if (used > ask)
206207
ask = used;
207-
resv->ar_asked = ask;
208-
resv->ar_reserved = resv->ar_orig_reserved = ask - used;
209-
mp->m_ag_max_usable -= ask;
208+
reserved = ask - used;
210209

211-
trace_xfs_ag_resv_init(pag, type, ask);
212-
213-
error = xfs_mod_fdblocks(mp, -(int64_t)resv->ar_reserved, true);
214-
if (error)
210+
error = xfs_mod_fdblocks(mp, -(int64_t)reserved, true);
211+
if (error) {
215212
trace_xfs_ag_resv_init_error(pag->pag_mount, pag->pag_agno,
216213
error, _RET_IP_);
214+
xfs_warn(mp,
215+
"Per-AG reservation for AG %u failed. Filesystem may run out of space.",
216+
pag->pag_agno);
217+
return error;
218+
}
217219

218-
return error;
220+
mp->m_ag_max_usable -= ask;
221+
222+
resv = xfs_perag_resv(pag, type);
223+
resv->ar_asked = ask;
224+
resv->ar_reserved = resv->ar_orig_reserved = reserved;
225+
226+
trace_xfs_ag_resv_init(pag, type, ask);
227+
return 0;
219228
}
220229

221230
/* Create a per-AG block reservation. */
222231
int
223232
xfs_ag_resv_init(
224233
struct xfs_perag *pag)
225234
{
235+
struct xfs_mount *mp = pag->pag_mount;
236+
xfs_agnumber_t agno = pag->pag_agno;
226237
xfs_extlen_t ask;
227238
xfs_extlen_t used;
228239
int error = 0;
@@ -231,23 +242,45 @@ xfs_ag_resv_init(
231242
if (pag->pag_meta_resv.ar_asked == 0) {
232243
ask = used = 0;
233244

234-
error = xfs_refcountbt_calc_reserves(pag->pag_mount,
235-
pag->pag_agno, &ask, &used);
245+
error = xfs_refcountbt_calc_reserves(mp, agno, &ask, &used);
236246
if (error)
237247
goto out;
238248

239-
error = __xfs_ag_resv_init(pag, XFS_AG_RESV_METADATA,
240-
ask, used);
249+
error = xfs_finobt_calc_reserves(mp, agno, &ask, &used);
241250
if (error)
242251
goto out;
252+
253+
error = __xfs_ag_resv_init(pag, XFS_AG_RESV_METADATA,
254+
ask, used);
255+
if (error) {
256+
/*
257+
* Because we didn't have per-AG reservations when the
258+
* finobt feature was added we might not be able to
259+
* reserve all needed blocks. Warn and fall back to the
260+
* old and potentially buggy code in that case, but
261+
* ensure we do have the reservation for the refcountbt.
262+
*/
263+
ask = used = 0;
264+
265+
mp->m_inotbt_nores = true;
266+
267+
error = xfs_refcountbt_calc_reserves(mp, agno, &ask,
268+
&used);
269+
if (error)
270+
goto out;
271+
272+
error = __xfs_ag_resv_init(pag, XFS_AG_RESV_METADATA,
273+
ask, used);
274+
if (error)
275+
goto out;
276+
}
243277
}
244278

245279
/* Create the AGFL metadata reservation */
246280
if (pag->pag_agfl_resv.ar_asked == 0) {
247281
ask = used = 0;
248282

249-
error = xfs_rmapbt_calc_reserves(pag->pag_mount, pag->pag_agno,
250-
&ask, &used);
283+
error = xfs_rmapbt_calc_reserves(mp, agno, &ask, &used);
251284
if (error)
252285
goto out;
253286

@@ -256,9 +289,16 @@ xfs_ag_resv_init(
256289
goto out;
257290
}
258291

292+
#ifdef DEBUG
293+
/* need to read in the AGF for the ASSERT below to work */
294+
error = xfs_alloc_pagf_init(pag->pag_mount, NULL, pag->pag_agno, 0);
295+
if (error)
296+
return error;
297+
259298
ASSERT(xfs_perag_resv(pag, XFS_AG_RESV_METADATA)->ar_reserved +
260299
xfs_perag_resv(pag, XFS_AG_RESV_AGFL)->ar_reserved <=
261300
pag->pagf_freeblks + pag->pagf_flcount);
301+
#endif
262302
out:
263303
return error;
264304
}

fs/xfs/libxfs/xfs_attr.c

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -131,9 +131,6 @@ xfs_attr_get(
131131
if (XFS_FORCED_SHUTDOWN(ip->i_mount))
132132
return -EIO;
133133

134-
if (!xfs_inode_hasattr(ip))
135-
return -ENOATTR;
136-
137134
error = xfs_attr_args_init(&args, ip, name, flags);
138135
if (error)
139136
return error;
@@ -392,9 +389,6 @@ xfs_attr_remove(
392389
if (XFS_FORCED_SHUTDOWN(dp->i_mount))
393390
return -EIO;
394391

395-
if (!xfs_inode_hasattr(dp))
396-
return -ENOATTR;
397-
398392
error = xfs_attr_args_init(&args, dp, name, flags);
399393
if (error)
400394
return error;

fs/xfs/libxfs/xfs_bmap.c

Lines changed: 34 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3629,7 +3629,7 @@ xfs_bmap_btalloc(
36293629
align = xfs_get_cowextsz_hint(ap->ip);
36303630
else if (xfs_alloc_is_userdata(ap->datatype))
36313631
align = xfs_get_extsz_hint(ap->ip);
3632-
if (unlikely(align)) {
3632+
if (align) {
36333633
error = xfs_bmap_extsize_align(mp, &ap->got, &ap->prev,
36343634
align, 0, ap->eof, 0, ap->conv,
36353635
&ap->offset, &ap->length);
@@ -3701,7 +3701,7 @@ xfs_bmap_btalloc(
37013701
args.minlen = ap->minlen;
37023702
}
37033703
/* apply extent size hints if obtained earlier */
3704-
if (unlikely(align)) {
3704+
if (align) {
37053705
args.prod = align;
37063706
if ((args.mod = (xfs_extlen_t)do_mod(ap->offset, args.prod)))
37073707
args.mod = (xfs_extlen_t)(args.prod - args.mod);
@@ -4514,8 +4514,6 @@ xfs_bmapi_write(
45144514
int n; /* current extent index */
45154515
xfs_fileoff_t obno; /* old block number (offset) */
45164516
int whichfork; /* data or attr fork */
4517-
char inhole; /* current location is hole in file */
4518-
char wasdelay; /* old extent was delayed */
45194517

45204518
#ifdef DEBUG
45214519
xfs_fileoff_t orig_bno; /* original block number value */
@@ -4603,22 +4601,44 @@ xfs_bmapi_write(
46034601
bma.firstblock = firstblock;
46044602

46054603
while (bno < end && n < *nmap) {
4606-
inhole = eof || bma.got.br_startoff > bno;
4607-
wasdelay = !inhole && isnullstartblock(bma.got.br_startblock);
4604+
bool need_alloc = false, wasdelay = false;
46084605

4609-
/*
4610-
* Make sure we only reflink into a hole.
4611-
*/
4612-
if (flags & XFS_BMAPI_REMAP)
4613-
ASSERT(inhole);
4614-
if (flags & XFS_BMAPI_COWFORK)
4615-
ASSERT(!inhole);
4606+
/* in hole or beyoned EOF? */
4607+
if (eof || bma.got.br_startoff > bno) {
4608+
if (flags & XFS_BMAPI_DELALLOC) {
4609+
/*
4610+
* For the COW fork we can reasonably get a
4611+
* request for converting an extent that races
4612+
* with other threads already having converted
4613+
* part of it, as there converting COW to
4614+
* regular blocks is not protected using the
4615+
* IOLOCK.
4616+
*/
4617+
ASSERT(flags & XFS_BMAPI_COWFORK);
4618+
if (!(flags & XFS_BMAPI_COWFORK)) {
4619+
error = -EIO;
4620+
goto error0;
4621+
}
4622+
4623+
if (eof || bno >= end)
4624+
break;
4625+
} else {
4626+
need_alloc = true;
4627+
}
4628+
} else {
4629+
/*
4630+
* Make sure we only reflink into a hole.
4631+
*/
4632+
ASSERT(!(flags & XFS_BMAPI_REMAP));
4633+
if (isnullstartblock(bma.got.br_startblock))
4634+
wasdelay = true;
4635+
}
46164636

46174637
/*
46184638
* First, deal with the hole before the allocated space
46194639
* that we found, if any.
46204640
*/
4621-
if (inhole || wasdelay) {
4641+
if (need_alloc || wasdelay) {
46224642
bma.eof = eof;
46234643
bma.conv = !!(flags & XFS_BMAPI_CONVERT);
46244644
bma.wasdel = wasdelay;

fs/xfs/libxfs/xfs_bmap.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,9 @@ struct xfs_extent_free_item
110110
/* Map something in the CoW fork. */
111111
#define XFS_BMAPI_COWFORK 0x200
112112

113+
/* Only convert delalloc space, don't allocate entirely new extents */
114+
#define XFS_BMAPI_DELALLOC 0x400
115+
113116
#define XFS_BMAPI_FLAGS \
114117
{ XFS_BMAPI_ENTIRE, "ENTIRE" }, \
115118
{ XFS_BMAPI_METADATA, "METADATA" }, \
@@ -120,7 +123,8 @@ struct xfs_extent_free_item
120123
{ XFS_BMAPI_CONVERT, "CONVERT" }, \
121124
{ XFS_BMAPI_ZERO, "ZERO" }, \
122125
{ XFS_BMAPI_REMAP, "REMAP" }, \
123-
{ XFS_BMAPI_COWFORK, "COWFORK" }
126+
{ XFS_BMAPI_COWFORK, "COWFORK" }, \
127+
{ XFS_BMAPI_DELALLOC, "DELALLOC" }
124128

125129

126130
static inline int xfs_bmapi_aflag(int w)

fs/xfs/libxfs/xfs_ialloc_btree.c

Lines changed: 87 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,11 +82,12 @@ xfs_finobt_set_root(
8282
}
8383

8484
STATIC int
85-
xfs_inobt_alloc_block(
85+
__xfs_inobt_alloc_block(
8686
struct xfs_btree_cur *cur,
8787
union xfs_btree_ptr *start,
8888
union xfs_btree_ptr *new,
89-
int *stat)
89+
int *stat,
90+
enum xfs_ag_resv_type resv)
9091
{
9192
xfs_alloc_arg_t args; /* block allocation args */
9293
int error; /* error return value */
@@ -103,6 +104,7 @@ xfs_inobt_alloc_block(
103104
args.maxlen = 1;
104105
args.prod = 1;
105106
args.type = XFS_ALLOCTYPE_NEAR_BNO;
107+
args.resv = resv;
106108

107109
error = xfs_alloc_vextent(&args);
108110
if (error) {
@@ -122,6 +124,27 @@ xfs_inobt_alloc_block(
122124
return 0;
123125
}
124126

127+
STATIC int
128+
xfs_inobt_alloc_block(
129+
struct xfs_btree_cur *cur,
130+
union xfs_btree_ptr *start,
131+
union xfs_btree_ptr *new,
132+
int *stat)
133+
{
134+
return __xfs_inobt_alloc_block(cur, start, new, stat, XFS_AG_RESV_NONE);
135+
}
136+
137+
STATIC int
138+
xfs_finobt_alloc_block(
139+
struct xfs_btree_cur *cur,
140+
union xfs_btree_ptr *start,
141+
union xfs_btree_ptr *new,
142+
int *stat)
143+
{
144+
return __xfs_inobt_alloc_block(cur, start, new, stat,
145+
XFS_AG_RESV_METADATA);
146+
}
147+
125148
STATIC int
126149
xfs_inobt_free_block(
127150
struct xfs_btree_cur *cur,
@@ -328,7 +351,7 @@ static const struct xfs_btree_ops xfs_finobt_ops = {
328351

329352
.dup_cursor = xfs_inobt_dup_cursor,
330353
.set_root = xfs_finobt_set_root,
331-
.alloc_block = xfs_inobt_alloc_block,
354+
.alloc_block = xfs_finobt_alloc_block,
332355
.free_block = xfs_inobt_free_block,
333356
.get_minrecs = xfs_inobt_get_minrecs,
334357
.get_maxrecs = xfs_inobt_get_maxrecs,
@@ -480,3 +503,64 @@ xfs_inobt_rec_check_count(
480503
return 0;
481504
}
482505
#endif /* DEBUG */
506+
507+
static xfs_extlen_t
508+
xfs_inobt_max_size(
509+
struct xfs_mount *mp)
510+
{
511+
/* Bail out if we're uninitialized, which can happen in mkfs. */
512+
if (mp->m_inobt_mxr[0] == 0)
513+
return 0;
514+
515+
return xfs_btree_calc_size(mp, mp->m_inobt_mnr,
516+
(uint64_t)mp->m_sb.sb_agblocks * mp->m_sb.sb_inopblock /
517+
XFS_INODES_PER_CHUNK);
518+
}
519+
520+
static int
521+
xfs_inobt_count_blocks(
522+
struct xfs_mount *mp,
523+
xfs_agnumber_t agno,
524+
xfs_btnum_t btnum,
525+
xfs_extlen_t *tree_blocks)
526+
{
527+
struct xfs_buf *agbp;
528+
struct xfs_btree_cur *cur;
529+
int error;
530+
531+
error = xfs_ialloc_read_agi(mp, NULL, agno, &agbp);
532+
if (error)
533+
return error;
534+
535+
cur = xfs_inobt_init_cursor(mp, NULL, agbp, agno, btnum);
536+
error = xfs_btree_count_blocks(cur, tree_blocks);
537+
xfs_btree_del_cursor(cur, error ? XFS_BTREE_ERROR : XFS_BTREE_NOERROR);
538+
xfs_buf_relse(agbp);
539+
540+
return error;
541+
}
542+
543+
/*
544+
* Figure out how many blocks to reserve and how many are used by this btree.
545+
*/
546+
int
547+
xfs_finobt_calc_reserves(
548+
struct xfs_mount *mp,
549+
xfs_agnumber_t agno,
550+
xfs_extlen_t *ask,
551+
xfs_extlen_t *used)
552+
{
553+
xfs_extlen_t tree_len = 0;
554+
int error;
555+
556+
if (!xfs_sb_version_hasfinobt(&mp->m_sb))
557+
return 0;
558+
559+
error = xfs_inobt_count_blocks(mp, agno, XFS_BTNUM_FINO, &tree_len);
560+
if (error)
561+
return error;
562+
563+
*ask += xfs_inobt_max_size(mp);
564+
*used += tree_len;
565+
return 0;
566+
}

fs/xfs/libxfs/xfs_ialloc_btree.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,4 +72,7 @@ int xfs_inobt_rec_check_count(struct xfs_mount *,
7272
#define xfs_inobt_rec_check_count(mp, rec) 0
7373
#endif /* DEBUG */
7474

75+
int xfs_finobt_calc_reserves(struct xfs_mount *mp, xfs_agnumber_t agno,
76+
xfs_extlen_t *ask, xfs_extlen_t *used);
77+
7578
#endif /* __XFS_IALLOC_BTREE_H__ */

fs/xfs/libxfs/xfs_sb.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,7 @@ xfs_mount_validate_sb(
242242
sbp->sb_blocklog < XFS_MIN_BLOCKSIZE_LOG ||
243243
sbp->sb_blocklog > XFS_MAX_BLOCKSIZE_LOG ||
244244
sbp->sb_blocksize != (1 << sbp->sb_blocklog) ||
245-
sbp->sb_dirblklog > XFS_MAX_BLOCKSIZE_LOG ||
245+
sbp->sb_dirblklog + sbp->sb_blocklog > XFS_MAX_BLOCKSIZE_LOG ||
246246
sbp->sb_inodesize < XFS_DINODE_MIN_SIZE ||
247247
sbp->sb_inodesize > XFS_DINODE_MAX_SIZE ||
248248
sbp->sb_inodelog < XFS_DINODE_MIN_LOG ||

0 commit comments

Comments
 (0)