Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
linux
Commits
232c2f5c
Commit
232c2f5c
authored
May 15, 2014
by
Dave Chinner
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'xfs-filestreams-lookup' into for-next
parents
fdd3a2ae
b94acd47
Changes
10
Show whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
403 additions
and
806 deletions
+403
-806
fs/xfs/xfs_bmap.c
fs/xfs/xfs_bmap.c
+117
-85
fs/xfs/xfs_filestream.c
fs/xfs/xfs_filestream.c
+147
-537
fs/xfs/xfs_filestream.h
fs/xfs/xfs_filestream.h
+2
-32
fs/xfs/xfs_inode.c
fs/xfs/xfs_inode.c
+1
-34
fs/xfs/xfs_inode.h
fs/xfs/xfs_inode.h
+1
-3
fs/xfs/xfs_mru_cache.c
fs/xfs/xfs_mru_cache.c
+63
-88
fs/xfs/xfs_mru_cache.h
fs/xfs/xfs_mru_cache.h
+12
-19
fs/xfs/xfs_super.c
fs/xfs/xfs_super.c
+1
-8
fs/xfs/xfs_trace.c
fs/xfs/xfs_trace.c
+1
-0
fs/xfs/xfs_trace.h
fs/xfs/xfs_trace.h
+58
-0
No files found.
fs/xfs/xfs_bmap.c
View file @
232c2f5c
...
...
@@ -3515,6 +3515,67 @@ xfs_bmap_adjacent(
#undef ISVALID
}
static
int
xfs_bmap_longest_free_extent
(
struct
xfs_trans
*
tp
,
xfs_agnumber_t
ag
,
xfs_extlen_t
*
blen
,
int
*
notinit
)
{
struct
xfs_mount
*
mp
=
tp
->
t_mountp
;
struct
xfs_perag
*
pag
;
xfs_extlen_t
longest
;
int
error
=
0
;
pag
=
xfs_perag_get
(
mp
,
ag
);
if
(
!
pag
->
pagf_init
)
{
error
=
xfs_alloc_pagf_init
(
mp
,
tp
,
ag
,
XFS_ALLOC_FLAG_TRYLOCK
);
if
(
error
)
goto
out
;
if
(
!
pag
->
pagf_init
)
{
*
notinit
=
1
;
goto
out
;
}
}
longest
=
xfs_alloc_longest_free_extent
(
mp
,
pag
);
if
(
*
blen
<
longest
)
*
blen
=
longest
;
out:
xfs_perag_put
(
pag
);
return
error
;
}
static
void
xfs_bmap_select_minlen
(
struct
xfs_bmalloca
*
ap
,
struct
xfs_alloc_arg
*
args
,
xfs_extlen_t
*
blen
,
int
notinit
)
{
if
(
notinit
||
*
blen
<
ap
->
minlen
)
{
/*
* Since we did a BUF_TRYLOCK above, it is possible that
* there is space for this request.
*/
args
->
minlen
=
ap
->
minlen
;
}
else
if
(
*
blen
<
args
->
maxlen
)
{
/*
* If the best seen length is less than the request length,
* use the best as the minimum.
*/
args
->
minlen
=
*
blen
;
}
else
{
/*
* Otherwise we've seen an extent as big as maxlen, use that
* as the minimum.
*/
args
->
minlen
=
args
->
maxlen
;
}
}
STATIC
int
xfs_bmap_btalloc_nullfb
(
struct
xfs_bmalloca
*
ap
,
...
...
@@ -3522,111 +3583,74 @@ xfs_bmap_btalloc_nullfb(
xfs_extlen_t
*
blen
)
{
struct
xfs_mount
*
mp
=
ap
->
ip
->
i_mount
;
struct
xfs_perag
*
pag
;
xfs_agnumber_t
ag
,
startag
;
int
notinit
=
0
;
int
error
;
if
(
ap
->
userdata
&&
xfs_inode_is_filestream
(
ap
->
ip
))
args
->
type
=
XFS_ALLOCTYPE_NEAR_BNO
;
else
args
->
type
=
XFS_ALLOCTYPE_START_BNO
;
args
->
total
=
ap
->
total
;
/*
* Search for an allocation group with a single extent large enough
* for the request. If one isn't found, then adjust the minimum
* allocation size to the largest space found.
*/
startag
=
ag
=
XFS_FSB_TO_AGNO
(
mp
,
args
->
fsbno
);
if
(
startag
==
NULLAGNUMBER
)
startag
=
ag
=
0
;
pag
=
xfs_perag_get
(
mp
,
ag
);
while
(
*
blen
<
args
->
maxlen
)
{
if
(
!
pag
->
pagf_init
)
{
error
=
xfs_alloc_pagf_init
(
mp
,
args
->
tp
,
ag
,
XFS_ALLOC_FLAG_TRYLOCK
);
if
(
error
)
{
xfs_perag_put
(
pag
);
error
=
xfs_bmap_longest_free_extent
(
args
->
tp
,
ag
,
blen
,
&
notinit
);
if
(
error
)
return
error
;
}
if
(
++
ag
==
mp
->
m_sb
.
sb_agcount
)
ag
=
0
;
if
(
ag
==
startag
)
break
;
}
/*
* See xfs_alloc_fix_freelist...
*/
if
(
pag
->
pagf_init
)
{
xfs_extlen_t
longest
;
longest
=
xfs_alloc_longest_free_extent
(
mp
,
pag
);
if
(
*
blen
<
longest
)
*
blen
=
longest
;
}
else
notinit
=
1
;
xfs_bmap_select_minlen
(
ap
,
args
,
blen
,
notinit
);
return
0
;
}
if
(
xfs_inode_is_filestream
(
ap
->
ip
))
{
if
(
*
blen
>=
args
->
maxlen
)
break
;
STATIC
int
xfs_bmap_btalloc_filestreams
(
struct
xfs_bmalloca
*
ap
,
struct
xfs_alloc_arg
*
args
,
xfs_extlen_t
*
blen
)
{
struct
xfs_mount
*
mp
=
ap
->
ip
->
i_mount
;
xfs_agnumber_t
ag
;
int
notinit
=
0
;
int
error
;
if
(
ap
->
userdata
)
{
/*
* If startag is an invalid AG, we've
* come here once before and
* xfs_filestream_new_ag picked the
* best currently available.
*
* Don't continue looping, since we
* could loop forever.
*/
if
(
startag
==
NULLAGNUMBER
)
break
;
args
->
type
=
XFS_ALLOCTYPE_NEAR_BNO
;
args
->
total
=
ap
->
total
;
ag
=
XFS_FSB_TO_AGNO
(
mp
,
args
->
fsbno
);
if
(
ag
==
NULLAGNUMBER
)
ag
=
0
;
error
=
xfs_bmap_longest_free_extent
(
args
->
tp
,
ag
,
blen
,
&
notinit
);
if
(
error
)
return
error
;
if
(
*
blen
<
args
->
maxlen
)
{
error
=
xfs_filestream_new_ag
(
ap
,
&
ag
);
xfs_perag_put
(
pag
);
if
(
error
)
return
error
;
/* loop again to set 'blen'*/
startag
=
NULLAGNUMBER
;
pag
=
xfs_perag_get
(
mp
,
ag
);
continue
;
}
}
if
(
++
ag
==
mp
->
m_sb
.
sb_agcount
)
ag
=
0
;
if
(
ag
==
startag
)
break
;
xfs_perag_put
(
pag
);
pag
=
xfs_perag_get
(
mp
,
ag
);
error
=
xfs_bmap_longest_free_extent
(
args
->
tp
,
ag
,
blen
,
&
notinit
);
if
(
error
)
return
error
;
}
xfs_perag_put
(
pag
);
/*
* Since the above loop did a BUF_TRYLOCK, it is
* possible that there is space for this request.
*/
if
(
notinit
||
*
blen
<
ap
->
minlen
)
args
->
minlen
=
ap
->
minlen
;
/*
* If the best seen length is less than the request
* length, use the best as the minimum.
*/
else
if
(
*
blen
<
args
->
maxlen
)
args
->
minlen
=
*
blen
;
/*
* Otherwise we've seen an extent as big as maxlen,
* use that as the minimum.
*/
else
args
->
minlen
=
args
->
maxlen
;
xfs_bmap_select_minlen
(
ap
,
args
,
blen
,
notinit
);
/*
*
set the failure fallback case to look in the selected
*
AG as the stream
may have moved.
*
Set the failure fallback case to look in the selected AG as stream
* may have moved.
*/
if
(
xfs_inode_is_filestream
(
ap
->
ip
))
ap
->
blkno
=
args
->
fsbno
=
XFS_AGB_TO_FSB
(
mp
,
ag
,
0
);
return
0
;
}
...
...
@@ -3706,6 +3730,14 @@ xfs_bmap_btalloc(
args
.
firstblock
=
*
ap
->
firstblock
;
blen
=
0
;
if
(
nullfb
)
{
/*
* Search for an allocation group with a single extent large
* enough for the request. If one isn't found, then adjust
* the minimum allocation size to the largest space found.
*/
if
(
ap
->
userdata
&&
xfs_inode_is_filestream
(
ap
->
ip
))
error
=
xfs_bmap_btalloc_filestreams
(
ap
,
&
args
,
&
blen
);
else
error
=
xfs_bmap_btalloc_nullfb
(
ap
,
&
args
,
&
blen
);
if
(
error
)
return
error
;
...
...
fs/xfs/xfs_filestream.c
View file @
232c2f5c
/*
* Copyright (c) 2006-2007 Silicon Graphics, Inc.
* Copyright (c) 2014 Christoph Hellwig.
* All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
...
...
@@ -32,100 +33,20 @@
#include "xfs_filestream.h"
#include "xfs_trace.h"
#ifdef XFS_FILESTREAMS_TRACE
ktrace_t
*
xfs_filestreams_trace_buf
;
STATIC
void
xfs_filestreams_trace
(
xfs_mount_t
*
mp
,
/* mount point */
int
type
,
/* type of trace */
const
char
*
func
,
/* source function */
int
line
,
/* source line number */
__psunsigned_t
arg0
,
__psunsigned_t
arg1
,
__psunsigned_t
arg2
,
__psunsigned_t
arg3
,
__psunsigned_t
arg4
,
__psunsigned_t
arg5
)
{
ktrace_enter
(
xfs_filestreams_trace_buf
,
(
void
*
)(
__psint_t
)(
type
|
(
line
<<
16
)),
(
void
*
)
func
,
(
void
*
)(
__psunsigned_t
)
current_pid
(),
(
void
*
)
mp
,
(
void
*
)(
__psunsigned_t
)
arg0
,
(
void
*
)(
__psunsigned_t
)
arg1
,
(
void
*
)(
__psunsigned_t
)
arg2
,
(
void
*
)(
__psunsigned_t
)
arg3
,
(
void
*
)(
__psunsigned_t
)
arg4
,
(
void
*
)(
__psunsigned_t
)
arg5
,
NULL
,
NULL
,
NULL
,
NULL
,
NULL
,
NULL
);
}
struct
xfs_fstrm_item
{
struct
xfs_mru_cache_elem
mru
;
struct
xfs_inode
*
ip
;
xfs_agnumber_t
ag
;
/* AG in use for this directory */
};
#define TRACE0(mp,t) TRACE6(mp,t,0,0,0,0,0,0)
#define TRACE1(mp,t,a0) TRACE6(mp,t,a0,0,0,0,0,0)
#define TRACE2(mp,t,a0,a1) TRACE6(mp,t,a0,a1,0,0,0,0)
#define TRACE3(mp,t,a0,a1,a2) TRACE6(mp,t,a0,a1,a2,0,0,0)
#define TRACE4(mp,t,a0,a1,a2,a3) TRACE6(mp,t,a0,a1,a2,a3,0,0)
#define TRACE5(mp,t,a0,a1,a2,a3,a4) TRACE6(mp,t,a0,a1,a2,a3,a4,0)
#define TRACE6(mp,t,a0,a1,a2,a3,a4,a5) \
xfs_filestreams_trace(mp, t, __func__, __LINE__, \
(__psunsigned_t)a0, (__psunsigned_t)a1, \
(__psunsigned_t)a2, (__psunsigned_t)a3, \
(__psunsigned_t)a4, (__psunsigned_t)a5)
#define TRACE_AG_SCAN(mp, ag, ag2) \
TRACE2(mp, XFS_FSTRM_KTRACE_AGSCAN, ag, ag2);
#define TRACE_AG_PICK1(mp, max_ag, maxfree) \
TRACE2(mp, XFS_FSTRM_KTRACE_AGPICK1, max_ag, maxfree);
#define TRACE_AG_PICK2(mp, ag, ag2, cnt, free, scan, flag) \
TRACE6(mp, XFS_FSTRM_KTRACE_AGPICK2, ag, ag2, \
cnt, free, scan, flag)
#define TRACE_UPDATE(mp, ip, ag, cnt, ag2, cnt2) \
TRACE5(mp, XFS_FSTRM_KTRACE_UPDATE, ip, ag, cnt, ag2, cnt2)
#define TRACE_FREE(mp, ip, pip, ag, cnt) \
TRACE4(mp, XFS_FSTRM_KTRACE_FREE, ip, pip, ag, cnt)
#define TRACE_LOOKUP(mp, ip, pip, ag, cnt) \
TRACE4(mp, XFS_FSTRM_KTRACE_ITEM_LOOKUP, ip, pip, ag, cnt)
#define TRACE_ASSOCIATE(mp, ip, pip, ag, cnt) \
TRACE4(mp, XFS_FSTRM_KTRACE_ASSOCIATE, ip, pip, ag, cnt)
#define TRACE_MOVEAG(mp, ip, pip, oag, ocnt, nag, ncnt) \
TRACE6(mp, XFS_FSTRM_KTRACE_MOVEAG, ip, pip, oag, ocnt, nag, ncnt)
#define TRACE_ORPHAN(mp, ip, ag) \
TRACE2(mp, XFS_FSTRM_KTRACE_ORPHAN, ip, ag);
#else
#define TRACE_AG_SCAN(mp, ag, ag2)
#define TRACE_AG_PICK1(mp, max_ag, maxfree)
#define TRACE_AG_PICK2(mp, ag, ag2, cnt, free, scan, flag)
#define TRACE_UPDATE(mp, ip, ag, cnt, ag2, cnt2)
#define TRACE_FREE(mp, ip, pip, ag, cnt)
#define TRACE_LOOKUP(mp, ip, pip, ag, cnt)
#define TRACE_ASSOCIATE(mp, ip, pip, ag, cnt)
#define TRACE_MOVEAG(mp, ip, pip, oag, ocnt, nag, ncnt)
#define TRACE_ORPHAN(mp, ip, ag)
#endif
static
kmem_zone_t
*
item_zone
;
/*
* Structure for associating a file or a directory with an allocation group.
* The parent directory pointer is only needed for files, but since there will
* generally be vastly more files than directories in the cache, using the same
* data structure simplifies the code with very little memory overhead.
*/
typedef
struct
fstrm_item
{
xfs_agnumber_t
ag
;
/* AG currently in use for the file/directory. */
xfs_inode_t
*
ip
;
/* inode self-pointer. */
xfs_inode_t
*
pip
;
/* Parent directory inode pointer. */
}
fstrm_item_t
;
enum
xfs_fstrm_alloc
{
XFS_PICK_USERDATA
=
1
,
XFS_PICK_LOWSPACE
=
2
,
};
/*
* Allocation group filestream associations are tracked with per-ag atomic
* counters. These counters allow
_
xfs_filestream_pick_ag() to tell whether a
* counters. These counters allow xfs_filestream_pick_ag() to tell whether a
* particular AG already has active filestreams associated with it. The mount
* point's m_peraglock is used to protect these counters from per-ag array
* re-allocation during a growfs operation. When xfs_growfs_data_private() is
...
...
@@ -160,7 +81,7 @@ typedef struct fstrm_item
* the cache that reference per-ag array elements that have since been
* reallocated.
*/
static
int
int
xfs_filestream_peek_ag
(
xfs_mount_t
*
mp
,
xfs_agnumber_t
agno
)
...
...
@@ -200,23 +121,40 @@ xfs_filestream_put_ag(
xfs_perag_put
(
pag
);
}
static
void
xfs_fstrm_free_func
(
struct
xfs_mru_cache_elem
*
mru
)
{
struct
xfs_fstrm_item
*
item
=
container_of
(
mru
,
struct
xfs_fstrm_item
,
mru
);
xfs_filestream_put_ag
(
item
->
ip
->
i_mount
,
item
->
ag
);
trace_xfs_filestream_free
(
item
->
ip
,
item
->
ag
);
kmem_free
(
item
);
}
/*
* Scan the AGs starting at startag looking for an AG that isn't in use and has
* at least minlen blocks free.
*/
static
int
_
xfs_filestream_pick_ag
(
xfs_mount_t
*
m
p
,
xfs_filestream_pick_ag
(
struct
xfs_inode
*
i
p
,
xfs_agnumber_t
startag
,
xfs_agnumber_t
*
agp
,
int
flags
,
xfs_extlen_t
minlen
)
{
int
streams
,
max_streams
;
int
err
,
trylock
,
nscan
;
xfs_extlen_t
longest
,
free
,
minfree
,
maxfree
=
0
;
xfs_agnumber_t
ag
,
max_ag
=
NULLAGNUMBER
;
struct
xfs_mount
*
mp
=
ip
->
i_mount
;
struct
xfs_fstrm_item
*
item
;
struct
xfs_perag
*
pag
;
xfs_extlen_t
longest
,
free
=
0
,
minfree
,
maxfree
=
0
;
xfs_agnumber_t
ag
,
max_ag
=
NULLAGNUMBER
;
int
err
,
trylock
,
nscan
;
ASSERT
(
S_ISDIR
(
ip
->
i_d
.
di_mode
));
/* 2% of an AG's blocks must be free for it to be chosen. */
minfree
=
mp
->
m_sb
.
sb_agblocks
/
50
;
...
...
@@ -228,8 +166,9 @@ _xfs_filestream_pick_ag(
trylock
=
XFS_ALLOC_FLAG_TRYLOCK
;
for
(
nscan
=
0
;
1
;
nscan
++
)
{
trace_xfs_filestream_scan
(
ip
,
ag
);
pag
=
xfs_perag_get
(
mp
,
ag
);
TRACE_AG_SCAN
(
mp
,
ag
,
atomic_read
(
&
pag
->
pagf_fstrms
));
if
(
!
pag
->
pagf_init
)
{
err
=
xfs_alloc_pagf_init
(
mp
,
NULL
,
ag
,
trylock
);
...
...
@@ -246,7 +185,6 @@ _xfs_filestream_pick_ag(
/* Keep track of the AG with the most free blocks. */
if
(
pag
->
pagf_freeblks
>
maxfree
)
{
maxfree
=
pag
->
pagf_freeblks
;
max_streams
=
atomic_read
(
&
pag
->
pagf_fstrms
);
max_ag
=
ag
;
}
...
...
@@ -269,7 +207,6 @@ _xfs_filestream_pick_ag(
/* Break out, retaining the reference on the AG. */
free
=
pag
->
pagf_freeblks
;
streams
=
atomic_read
(
&
pag
->
pagf_fstrms
);
xfs_perag_put
(
pag
);
*
agp
=
ag
;
break
;
...
...
@@ -305,317 +242,98 @@ _xfs_filestream_pick_ag(
*/
if
(
max_ag
!=
NULLAGNUMBER
)
{
xfs_filestream_get_ag
(
mp
,
max_ag
);
TRACE_AG_PICK1
(
mp
,
max_ag
,
maxfree
);
streams
=
max_streams
;
free
=
maxfree
;
*
agp
=
max_ag
;
break
;
}
/* take AG 0 if none matched */
TRACE_AG_PICK1
(
mp
,
max_ag
,
maxfree
);
trace_xfs_filestream_pick
(
ip
,
*
agp
,
free
,
nscan
);
*
agp
=
0
;
return
0
;
}
TRACE_AG_PICK2
(
mp
,
startag
,
*
agp
,
streams
,
free
,
nscan
,
flags
);
return
0
;
}
/*
* Set the allocation group number for a file or a directory, updating inode
* references and per-AG references as appropriate.
*/
static
int
_xfs_filestream_update_ag
(
xfs_inode_t
*
ip
,
xfs_inode_t
*
pip
,
xfs_agnumber_t
ag
)
{
int
err
=
0
;
xfs_mount_t
*
mp
;
xfs_mru_cache_t
*
cache
;
fstrm_item_t
*
item
;
xfs_agnumber_t
old_ag
;
xfs_inode_t
*
old_pip
;
/*
* Either ip is a regular file and pip is a directory, or ip is a
* directory and pip is NULL.
*/
ASSERT
(
ip
&&
((
S_ISREG
(
ip
->
i_d
.
di_mode
)
&&
pip
&&
S_ISDIR
(
pip
->
i_d
.
di_mode
))
||
(
S_ISDIR
(
ip
->
i_d
.
di_mode
)
&&
!
pip
)));
mp
=
ip
->
i_mount
;
cache
=
mp
->
m_filestream
;
item
=
xfs_mru_cache_lookup
(
cache
,
ip
->
i_ino
);
if
(
item
)
{
ASSERT
(
item
->
ip
==
ip
);
old_ag
=
item
->
ag
;
item
->
ag
=
ag
;
old_pip
=
item
->
pip
;
item
->
pip
=
pip
;
xfs_mru_cache_done
(
cache
);
/*
* If the AG has changed, drop the old ref and take a new one,
* effectively transferring the reference from old to new AG.
*/
if
(
ag
!=
old_ag
)
{
xfs_filestream_put_ag
(
mp
,
old_ag
);
xfs_filestream_get_ag
(
mp
,
ag
);
}
/*
* If ip is a file and its pip has changed, drop the old ref and
* take a new one.
*/
if
(
pip
&&
pip
!=
old_pip
)
{
IRELE
(
old_pip
);
IHOLD
(
pip
);
}
trace_xfs_filestream_pick
(
ip
,
*
agp
,
free
,
nscan
);
TRACE_UPDATE
(
mp
,
ip
,
old_ag
,
xfs_filestream_peek_ag
(
mp
,
old_ag
),
ag
,
xfs_filestream_peek_ag
(
mp
,
ag
));
if
(
*
agp
==
NULLAGNUMBER
)
return
0
;
}
item
=
kmem_zone_zalloc
(
item_zone
,
KM_MAYFAIL
);
err
=
ENOMEM
;
item
=
kmem_alloc
(
sizeof
(
*
item
),
KM_MAYFAIL
);
if
(
!
item
)
return
ENOMEM
;
goto
out_put_ag
;
item
->
ag
=
ag
;
item
->
ag
=
*
agp
;
item
->
ip
=
ip
;
item
->
pip
=
pip
;
err
=
xfs_mru_cache_insert
(
cache
,
ip
->
i_ino
,
item
);
err
=
xfs_mru_cache_insert
(
mp
->
m_filestream
,
ip
->
i_ino
,
&
item
->
mru
);
if
(
err
)
{
kmem_zone_free
(
item_zone
,
item
);
return
err
;
if
(
err
==
EEXIST
)
err
=
0
;
goto
out_free_item
;
}
/* Take a reference on the AG. */
xfs_filestream_get_ag
(
mp
,
ag
);
/*
* Take a reference on the inode itself regardless of whether it's a
* regular file or a directory.
*/
IHOLD
(
ip
);
/*
* In the case of a regular file, take a reference on the parent inode
* as well to ensure it remains in-core.
*/
if
(
pip
)
IHOLD
(
pip
);
TRACE_UPDATE
(
mp
,
ip
,
ag
,
xfs_filestream_peek_ag
(
mp
,
ag
),
ag
,
xfs_filestream_peek_ag
(
mp
,
ag
));
return
0
;
}
/* xfs_fstrm_free_func(): callback for freeing cached stream items. */
STATIC
void
xfs_fstrm_free_func
(
unsigned
long
ino
,
void
*
data
)
{
fstrm_item_t
*
item
=
(
fstrm_item_t
*
)
data
;
xfs_inode_t
*
ip
=
item
->
ip
;
ASSERT
(
ip
->
i_ino
==
ino
);
xfs_iflags_clear
(
ip
,
XFS_IFILESTREAM
);
/* Drop the reference taken on the AG when the item was added. */
xfs_filestream_put_ag
(
ip
->
i_mount
,
item
->
ag
);
TRACE_FREE
(
ip
->
i_mount
,
ip
,
item
->
pip
,
item
->
ag
,
xfs_filestream_peek_ag
(
ip
->
i_mount
,
item
->
ag
));
/*
* _xfs_filestream_update_ag() always takes a reference on the inode
* itself, whether it's a file or a directory. Release it here.
* This can result in the inode being freed and so we must
* not hold any inode locks when freeing filesstreams objects
* otherwise we can deadlock here.
*/
IRELE
(
ip
);
/*
* In the case of a regular file, _xfs_filestream_update_ag() also
* takes a ref on the parent inode to keep it in-core. Release that
* too.
*/
if
(
item
->
pip
)
IRELE
(
item
->
pip
);
/* Finally, free the memory allocated for the item. */
kmem_zone_free
(
item_zone
,
item
);
}
/*
* xfs_filestream_init() is called at xfs initialisation time to set up the
* memory zone that will be used for filestream data structure allocation.
*/
int
xfs_filestream_init
(
void
)
{
item_zone
=
kmem_zone_init
(
sizeof
(
fstrm_item_t
),
"fstrm_item"
);
if
(
!
item_zone
)
return
-
ENOMEM
;
return
0
;
}
/*
* xfs_filestream_uninit() is called at xfs termination time to destroy the
* memory zone that was used for filestream data structure allocation.
*/
void
xfs_filestream_uninit
(
void
)
{
kmem_zone_destroy
(
item_zone
);
}
/*
* xfs_filestream_mount() is called when a file system is mounted with the
* filestream option. It is responsible for allocating the data structures
* needed to track the new file system's file streams.
*/
int
xfs_filestream_mount
(
xfs_mount_t
*
mp
)
{
int
err
;
unsigned
int
lifetime
,
grp_count
;
/*
* The filestream timer tunable is currently fixed within the range of
* one second to four minutes, with five seconds being the default. The
* group count is somewhat arbitrary, but it'd be nice to adhere to the
* timer tunable to within about 10 percent. This requires at least 10
* groups.
*/
lifetime
=
xfs_fstrm_centisecs
*
10
;
grp_count
=
10
;
err
=
xfs_mru_cache_create
(
&
mp
->
m_filestream
,
lifetime
,
grp_count
,
xfs_fstrm_free_func
);
out_free_item:
kmem_free
(
item
);
out_put_ag:
xfs_filestream_put_ag
(
mp
,
*
agp
);
return
err
;
}
/*
* xfs_filestream_unmount() is called when a file system that was mounted with
* the filestream option is unmounted. It drains the data structures created
* to track the file system's file streams and frees all the memory that was
* allocated.
*/
void
xfs_filestream_unmount
(
xfs_mount_t
*
mp
)
static
struct
xfs_inode
*
xfs_filestream_get_parent
(
struct
xfs_inode
*
ip
)
{
xfs_mru_cache_destroy
(
mp
->
m_filestream
)
;
}
struct
inode
*
inode
=
VFS_I
(
ip
),
*
dir
=
NULL
;
struct
dentry
*
dentry
,
*
parent
;
/*
* Return the AG of the filestream the file or directory belongs to, or
* NULLAGNUMBER otherwise.
*/
xfs_agnumber_t
xfs_filestream_lookup_ag
(
xfs_inode_t
*
ip
)
{
xfs_mru_cache_t
*
cache
;
fstrm_item_t
*
item
;
xfs_agnumber_t
ag
;
int
ref
;
if
(
!
S_ISREG
(
ip
->
i_d
.
di_mode
)
&&
!
S_ISDIR
(
ip
->
i_d
.
di_mode
))
{
ASSERT
(
0
);
return
NULLAGNUMBER
;
}
dentry
=
d_find_alias
(
inode
);
if
(
!
dentry
)
goto
out
;
cache
=
ip
->
i_mount
->
m_filestream
;
item
=
xfs_mru_cache_lookup
(
cache
,
ip
->
i_ino
);
if
(
!
item
)
{
TRACE_LOOKUP
(
ip
->
i_mount
,
ip
,
NULL
,
NULLAGNUMBER
,
0
);
return
NULLAGNUMBER
;
}
parent
=
dget_parent
(
dentry
);
if
(
!
parent
)
goto
out_dput
;
ASSERT
(
ip
==
item
->
ip
);
ag
=
item
->
ag
;
ref
=
xfs_filestream_peek_ag
(
ip
->
i_mount
,
ag
);
xfs_mru_cache_done
(
cache
);
dir
=
igrab
(
parent
->
d_inode
);
dput
(
parent
);
TRACE_LOOKUP
(
ip
->
i_mount
,
ip
,
item
->
pip
,
ag
,
ref
);
return
ag
;
out_dput:
dput
(
dentry
);
out:
return
dir
?
XFS_I
(
dir
)
:
NULL
;
}
/*
* xfs_filestream_associate() should only be called to associate a regular file
* with its parent directory. Calling it with a child directory isn't
* appropriate because filestreams don't apply to entire directory hierarchies.
* Creating a file in a child directory of an existing filestream directory
* starts a new filestream with its own allocation group association.
* Find the right allocation group for a file, either by finding an
* existing file stream or creating a new one.
*
* Returns < 0 on error, 0 if successful association occurred, > 0 if
* we failed to get an association because of locking issues.
* Returns NULLAGNUMBER in case of an error.
*/
int
xfs_filestream_associate
(
xfs_inode_t
*
pip
,
xfs_inode_t
*
ip
)
xfs_agnumber_t
xfs_filestream_lookup_ag
(
struct
xfs_inode
*
ip
)
{
xfs_mount_t
*
mp
;
xfs_mru_cache_t
*
cache
;
fstrm_item_t
*
item
;
xfs_agnumber_t
ag
,
rotorstep
,
startag
;
int
err
=
0
;
struct
xfs_mount
*
mp
=
ip
->
i_mount
;
struct
xfs_inode
*
pip
=
NULL
;
xfs_agnumber_t
startag
,
ag
=
NULLAGNUMBER
;
struct
xfs_mru_cache_elem
*
mru
;
ASSERT
(
S_ISDIR
(
pip
->
i_d
.
di_mode
));
ASSERT
(
S_ISREG
(
ip
->
i_d
.
di_mode
));
if
(
!
S_ISDIR
(
pip
->
i_d
.
di_mode
)
||
!
S_ISREG
(
ip
->
i_d
.
di_mode
))
return
-
EINVAL
;
mp
=
pip
->
i_mount
;
cache
=
mp
->
m_filestream
;
/*
* We have a problem, Houston.
*
* Taking the iolock here violates inode locking order - we already
* hold the ilock. Hence if we block getting this lock we may never
* wake. Unfortunately, that means if we can't get the lock, we're
* screwed in terms of getting a stream association - we can't spin
* waiting for the lock because someone else is waiting on the lock we
* hold and we cannot drop that as we are in a transaction here.
*
* Lucky for us, this inversion is not a problem because it's a
* directory inode that we are trying to lock here.
*
* So, if we can't get the iolock without sleeping then just give up
*/
if
(
!
xfs_ilock_nowait
(
pip
,
XFS_IOLOCK_EXCL
))
return
1
;
/* If the parent directory is already in the cache, use its AG. */
item
=
xfs_mru_cache_lookup
(
cache
,
pip
->
i_ino
);
if
(
item
)
{
ASSERT
(
item
->
ip
==
pip
);
ag
=
item
->
ag
;
xfs_mru_cache_done
(
cache
);
pip
=
xfs_filestream_get_parent
(
ip
);
if
(
!
pip
)
goto
out
;
TRACE_LOOKUP
(
mp
,
pip
,
pip
,
ag
,
xfs_filestream_peek_ag
(
mp
,
ag
));
err
=
_xfs_filestream_update_ag
(
ip
,
pip
,
ag
);
mru
=
xfs_mru_cache_lookup
(
mp
->
m_filestream
,
pip
->
i_ino
);
if
(
mru
)
{
ag
=
container_of
(
mru
,
struct
xfs_fstrm_item
,
mru
)
->
ag
;
xfs_mru_cache_done
(
mp
->
m_filestream
);
goto
exit
;
trace_xfs_filestream_lookup
(
ip
,
ag
);
goto
out
;
}
/*
...
...
@@ -623,202 +341,94 @@ xfs_filestream_associate(
* use the directory inode's AG.
*/
if
(
mp
->
m_flags
&
XFS_MOUNT_32BITINODES
)
{
rotorstep
=
xfs_rotorstep
;
xfs_agnumber_t
rotorstep
=
xfs_rotorstep
;
startag
=
(
mp
->
m_agfrotor
/
rotorstep
)
%
mp
->
m_sb
.
sb_agcount
;
mp
->
m_agfrotor
=
(
mp
->
m_agfrotor
+
1
)
%
(
mp
->
m_sb
.
sb_agcount
*
rotorstep
);
}
else
startag
=
XFS_INO_TO_AGNO
(
mp
,
pip
->
i_ino
);
/* Pick a new AG for the parent inode starting at startag. */
err
=
_xfs_filestream_pick_ag
(
mp
,
startag
,
&
ag
,
0
,
0
);
if
(
err
||
ag
==
NULLAGNUMBER
)
goto
exit_did_pick
;
/* Associate the parent inode with the AG. */
err
=
_xfs_filestream_update_ag
(
pip
,
NULL
,
ag
);
if
(
err
)
goto
exit_did_pick
;
/* Associate the file inode with the AG. */
err
=
_xfs_filestream_update_ag
(
ip
,
pip
,
ag
);
if
(
err
)
goto
exit_did_pick
;
TRACE_ASSOCIATE
(
mp
,
ip
,
pip
,
ag
,
xfs_filestream_peek_ag
(
mp
,
ag
));
exit_did_pick:
/*
* If _xfs_filestream_pick_ag() returned a valid AG, remove the
* reference it took on it, since the file and directory will have taken
* their own now if they were successfully cached.
*/
if
(
ag
!=
NULLAGNUMBER
)
xfs_filestream_put_ag
(
mp
,
ag
);
exit:
xfs_iunlock
(
pip
,
XFS_IOLOCK_EXCL
);
return
-
err
;
if
(
xfs_filestream_pick_ag
(
pip
,
startag
,
&
ag
,
0
,
0
))
ag
=
NULLAGNUMBER
;
out:
IRELE
(
pip
);
return
ag
;
}
/*
* Pick a new allocation group for the current file and its file stream. This
* function is called by xfs_bmap_filestreams() with the mount point's per-ag
* lock held.
* Pick a new allocation group for the current file and its file stream.
*
* This is called when the allocator can't find a suitable extent in the
* current AG, and we have to move the stream into a new AG with more space.
*/
int
xfs_filestream_new_ag
(
struct
xfs_bmalloca
*
ap
,
xfs_agnumber_t
*
agp
)
{
int
flags
,
err
;
xfs_inode_t
*
ip
,
*
pip
=
NULL
;
xfs_mount_t
*
mp
;
xfs_mru_cache_t
*
cache
;
xfs_extlen_t
minlen
;
fstrm_item_t
*
dir
,
*
file
;
xfs_agnumber_t
ag
=
NULLAGNUMBER
;
ip
=
ap
->
ip
;
mp
=
ip
->
i_mount
;
cache
=
mp
->
m_filestream
;
minlen
=
ap
->
length
;
*
agp
=
NULLAGNUMBER
;
struct
xfs_inode
*
ip
=
ap
->
ip
,
*
pip
;
struct
xfs_mount
*
mp
=
ip
->
i_mount
;
xfs_extlen_t
minlen
=
ap
->
length
;
xfs_agnumber_t
startag
=
0
;
int
flags
,
err
=
0
;
struct
xfs_mru_cache_elem
*
mru
;
/*
* Look for the file in the cache, removing it if it's found. Doing
* this allows it to be held across the dir lookup that follows.
*/
file
=
xfs_mru_cache_remove
(
cache
,
ip
->
i_ino
);
if
(
file
)
{
ASSERT
(
ip
==
file
->
ip
);
/* Save the file's parent inode and old AG number for later. */
pip
=
file
->
pip
;
ag
=
file
->
ag
;
/* Look for the file's directory in the cache. */
dir
=
xfs_mru_cache_lookup
(
cache
,
pip
->
i_ino
);
if
(
dir
)
{
ASSERT
(
pip
==
dir
->
ip
);
/*
* If the directory has already moved on to a new AG,
* use that AG as the new AG for the file. Don't
* forget to twiddle the AG refcounts to match the
* movement.
*/
if
(
dir
->
ag
!=
file
->
ag
)
{
xfs_filestream_put_ag
(
mp
,
file
->
ag
);
xfs_filestream_get_ag
(
mp
,
dir
->
ag
);
*
agp
=
file
->
ag
=
dir
->
ag
;
}
xfs_mru_cache_done
(
cache
);
}
*
agp
=
NULLAGNUMBER
;
/*
* Put the file back in the cache. If this fails, the free
* function needs to be called to tidy up in the same way as if
* the item had simply expired from the cache.
*/
err
=
xfs_mru_cache_insert
(
cache
,
ip
->
i_ino
,
file
);
if
(
err
)
{
xfs_fstrm_free_func
(
ip
->
i_ino
,
file
);
return
err
;
}
pip
=
xfs_filestream_get_parent
(
ip
);
if
(
!
pip
)
goto
exit
;
/*
* If the file's AG was moved to the directory's new AG, there's
* nothing more to be done.
*/
if
(
*
agp
!=
NULLAGNUMBER
)
{
TRACE_MOVEAG
(
mp
,
ip
,
pip
,
ag
,
xfs_filestream_peek_ag
(
mp
,
ag
),
*
agp
,
xfs_filestream_peek_ag
(
mp
,
*
agp
));
return
0
;
}
mru
=
xfs_mru_cache_remove
(
mp
->
m_filestream
,
pip
->
i_ino
);
if
(
mru
)
{
struct
xfs_fstrm_item
*
item
=
container_of
(
mru
,
struct
xfs_fstrm_item
,
mru
);
startag
=
(
item
->
ag
+
1
)
%
mp
->
m_sb
.
sb_agcount
;
}
/*
* If the file's parent directory is known, take its iolock in exclusive
* mode to prevent two sibling files from racing each other to migrate
* themselves and their parent to different AGs.
*
* Note that we lock the parent directory iolock inside the child
* iolock here. That's fine as we never hold both parent and child
* iolock in any other place. This is different from the ilock,
* which requires locking of the child after the parent for namespace
* operations.
*/
if
(
pip
)
xfs_ilock
(
pip
,
XFS_IOLOCK_EXCL
|
XFS_IOLOCK_PARENT
);
/*
* A new AG needs to be found for the file. If the file's parent
* directory is also known, it will be moved to the new AG as well to
* ensure that files created inside it in future use the new AG.
*/
ag
=
(
ag
==
NULLAGNUMBER
)
?
0
:
(
ag
+
1
)
%
mp
->
m_sb
.
sb_agcount
;
flags
=
(
ap
->
userdata
?
XFS_PICK_USERDATA
:
0
)
|
(
ap
->
flist
->
xbf_low
?
XFS_PICK_LOWSPACE
:
0
);
err
=
_xfs_filestream_pick_ag
(
mp
,
ag
,
agp
,
flags
,
minlen
);
if
(
err
||
*
agp
==
NULLAGNUMBER
)
goto
exit
;
err
=
xfs_filestream_pick_ag
(
pip
,
startag
,
agp
,
flags
,
minlen
);
/*
* If the file wasn't found in the file cache, then its parent directory
* inode isn't known. For this to have happened, the file must either
* be pre-existing, or it was created long enough ago that its cache
* entry has expired. This isn't the sort of usage that the filestreams
* allocator is trying to optimise, so there's no point trying to track
* its new AG somehow in the filestream data structures.
* Only free the item here so we skip over the old AG earlier.
*/
if
(
!
pip
)
{
TRACE_ORPHAN
(
mp
,
ip
,
*
agp
);
goto
exit
;
}
/* Associate the parent inode with the AG. */
err
=
_xfs_filestream_update_ag
(
pip
,
NULL
,
*
agp
);
if
(
err
)
goto
exit
;
/* Associate the file inode with the AG. */
err
=
_xfs_filestream_update_ag
(
ip
,
pip
,
*
agp
);
if
(
err
)
goto
exit
;
TRACE_MOVEAG
(
mp
,
ip
,
pip
,
NULLAGNUMBER
,
0
,
*
agp
,
xfs_filestream_peek_ag
(
mp
,
*
agp
));
if
(
mru
)
xfs_fstrm_free_func
(
mru
);
IRELE
(
pip
);
exit:
/*
* If _xfs_filestream_pick_ag() returned a valid AG, remove the
* reference it took on it, since the file and directory will have taken
* their own now if they were successfully cached.
*/
if
(
*
agp
!=
NULLAGNUMBER
)
xfs_filestream_put_ag
(
mp
,
*
agp
);
else
if
(
*
agp
==
NULLAGNUMBER
)
*
agp
=
0
;
if
(
pip
)
xfs_iunlock
(
pip
,
XFS_IOLOCK_EXCL
);
return
err
;
}
/*
* Remove an association between an inode and a filestream object.
* Typically this is done on last close of an unlinked file.
*/
void
xfs_filestream_deassociate
(
xfs_inode_t
*
ip
)
struct
xfs_inode
*
ip
)
{
xfs_mru_cache_delete
(
ip
->
i_mount
->
m_filestream
,
ip
->
i_ino
);
}
int
xfs_filestream_mount
(
xfs_mount_t
*
mp
)
{
xfs_mru_cache_t
*
cache
=
ip
->
i_mount
->
m_filestream
;
/*
* The filestream timer tunable is currently fixed within the range of
* one second to four minutes, with five seconds being the default. The
* group count is somewhat arbitrary, but it'd be nice to adhere to the
* timer tunable to within about 10 percent. This requires at least 10
* groups.
*/
return
xfs_mru_cache_create
(
&
mp
->
m_filestream
,
xfs_fstrm_centisecs
*
10
,
10
,
xfs_fstrm_free_func
);
}
xfs_mru_cache_delete
(
cache
,
ip
->
i_ino
);
void
xfs_filestream_unmount
(
xfs_mount_t
*
mp
)
{
xfs_mru_cache_destroy
(
mp
->
m_filestream
);
}
fs/xfs/xfs_filestream.h
View file @
232c2f5c
...
...
@@ -20,50 +20,20 @@
struct
xfs_mount
;
struct
xfs_inode
;
struct
xfs_perag
;
struct
xfs_bmalloca
;
#ifdef XFS_FILESTREAMS_TRACE
#define XFS_FSTRM_KTRACE_INFO 1
#define XFS_FSTRM_KTRACE_AGSCAN 2
#define XFS_FSTRM_KTRACE_AGPICK1 3
#define XFS_FSTRM_KTRACE_AGPICK2 4
#define XFS_FSTRM_KTRACE_UPDATE 5
#define XFS_FSTRM_KTRACE_FREE 6
#define XFS_FSTRM_KTRACE_ITEM_LOOKUP 7
#define XFS_FSTRM_KTRACE_ASSOCIATE 8
#define XFS_FSTRM_KTRACE_MOVEAG 9
#define XFS_FSTRM_KTRACE_ORPHAN 10
#define XFS_FSTRM_KTRACE_SIZE 16384
extern
ktrace_t
*
xfs_filestreams_trace_buf
;
#endif
/* allocation selection flags */
typedef
enum
xfs_fstrm_alloc
{
XFS_PICK_USERDATA
=
1
,
XFS_PICK_LOWSPACE
=
2
,
}
xfs_fstrm_alloc_t
;
/* prototypes for filestream.c */
int
xfs_filestream_init
(
void
);
void
xfs_filestream_uninit
(
void
);
int
xfs_filestream_mount
(
struct
xfs_mount
*
mp
);
void
xfs_filestream_unmount
(
struct
xfs_mount
*
mp
);
xfs_agnumber_t
xfs_filestream_lookup_ag
(
struct
xfs_inode
*
ip
);
int
xfs_filestream_associate
(
struct
xfs_inode
*
dip
,
struct
xfs_inode
*
ip
);
void
xfs_filestream_deassociate
(
struct
xfs_inode
*
ip
);
xfs_agnumber_t
xfs_filestream_lookup_ag
(
struct
xfs_inode
*
ip
);
int
xfs_filestream_new_ag
(
struct
xfs_bmalloca
*
ap
,
xfs_agnumber_t
*
agp
);
int
xfs_filestream_peek_ag
(
struct
xfs_mount
*
mp
,
xfs_agnumber_t
agno
);
/* filestreams for the inode? */
static
inline
int
xfs_inode_is_filestream
(
struct
xfs_inode
*
ip
)
{
return
(
ip
->
i_mount
->
m_flags
&
XFS_MOUNT_FILESTREAMS
)
||
xfs_iflags_test
(
ip
,
XFS_IFILESTREAM
)
||
(
ip
->
i_d
.
di_flags
&
XFS_DIFLAG_FILESTREAM
);
}
...
...
fs/xfs/xfs_inode.c
View file @
232c2f5c
...
...
@@ -655,7 +655,6 @@ xfs_ialloc(
uint
flags
;
int
error
;
timespec_t
tv
;
int
filestreams
=
0
;
/*
* Call the space management code to pick
...
...
@@ -772,13 +771,6 @@ xfs_ialloc(
flags
|=
XFS_ILOG_DEV
;
break
;
case
S_IFREG
:
/*
* we can't set up filestreams until after the VFS inode
* is set up properly.
*/
if
(
pip
&&
xfs_inode_is_filestream
(
pip
))
filestreams
=
1
;
/* fall through */
case
S_IFDIR
:
if
(
pip
&&
(
pip
->
i_d
.
di_flags
&
XFS_DIFLAG_ANY
))
{
uint
di_flags
=
0
;
...
...
@@ -844,15 +836,6 @@ xfs_ialloc(
/* now that we have an i_mode we can setup inode ops and unlock */
xfs_setup_inode
(
ip
);
/* now we have set up the vfs inode we can associate the filestream */
if
(
filestreams
)
{
error
=
xfs_filestream_associate
(
pip
,
ip
);
if
(
error
<
0
)
return
-
error
;
if
(
!
error
)
xfs_iflags_set
(
ip
,
XFS_IFILESTREAM
);
}
*
ipp
=
ip
;
return
0
;
}
...
...
@@ -1698,16 +1681,6 @@ xfs_release(
if
(
!
XFS_FORCED_SHUTDOWN
(
mp
))
{
int
truncated
;
/*
* If we are using filestreams, and we have an unlinked
* file that we are processing the last close on, then nothing
* will be able to reopen and write to this file. Purge this
* inode from the filestreams cache so that it doesn't delay
* teardown of the inode.
*/
if
((
ip
->
i_d
.
di_nlink
==
0
)
&&
xfs_inode_is_filestream
(
ip
))
xfs_filestream_deassociate
(
ip
);
/*
* If we previously truncated this file and removed old data
* in the process, we want to initiate "early" writeout on
...
...
@@ -2664,13 +2637,7 @@ xfs_remove(
if
(
error
)
goto
std_return
;
/*
* If we are using filestreams, kill the stream association.
* If the file is still open it may get a new one but that
* will get killed on last close in xfs_close() so we don't
* have to worry about that.
*/
if
(
!
is_dir
&&
link_zero
&&
xfs_inode_is_filestream
(
ip
))
if
(
is_dir
&&
xfs_inode_is_filestream
(
ip
))
xfs_filestream_deassociate
(
ip
);
return
0
;
...
...
fs/xfs/xfs_inode.h
View file @
232c2f5c
...
...
@@ -209,7 +209,6 @@ xfs_get_initial_prid(struct xfs_inode *dp)
#define XFS_ISTALE (1 << 1)
/* inode has been staled */
#define XFS_IRECLAIMABLE (1 << 2)
/* inode can be reclaimed */
#define XFS_INEW (1 << 3)
/* inode has just been allocated */
#define XFS_IFILESTREAM (1 << 4)
/* inode is in a filestream dir. */
#define XFS_ITRUNCATED (1 << 5)
/* truncated down so flush-on-close */
#define XFS_IDIRTY_RELEASE (1 << 6)
/* dirty release already seen */
#define __XFS_IFLOCK_BIT 7
/* inode is being flushed right now */
...
...
@@ -225,8 +224,7 @@ xfs_get_initial_prid(struct xfs_inode *dp)
*/
#define XFS_IRECLAIM_RESET_FLAGS \
(XFS_IRECLAIMABLE | XFS_IRECLAIM | \
XFS_IDIRTY_RELEASE | XFS_ITRUNCATED | \
XFS_IFILESTREAM);
XFS_IDIRTY_RELEASE | XFS_ITRUNCATED)
/*
* Synchronize processes attempting to flush the in-core inode back to disk.
...
...
fs/xfs/xfs_mru_cache.c
View file @
232c2f5c
...
...
@@ -100,14 +100,20 @@
* likely result in a loop in one of the lists. That's a sure-fire recipe for
* an infinite loop in the code.
*/
typedef
struct
xfs_mru_cache_elem
{
struct
list_head
list_node
;
unsigned
long
key
;
void
*
value
;
}
xfs_mru_cache_elem_t
;
struct
xfs_mru_cache
{
struct
radix_tree_root
store
;
/* Core storage data structure. */
struct
list_head
*
lists
;
/* Array of lists, one per grp. */
struct
list_head
reap_list
;
/* Elements overdue for reaping. */
spinlock_t
lock
;
/* Lock to protect this struct. */
unsigned
int
grp_count
;
/* Number of discrete groups. */
unsigned
int
grp_time
;
/* Time period spanned by grps. */
unsigned
int
lru_grp
;
/* Group containing time zero. */
unsigned
long
time_zero
;
/* Time first element was added. */
xfs_mru_cache_free_func_t
free_func
;
/* Function pointer for freeing. */
struct
delayed_work
work
;
/* Workqueue data for reaping. */
unsigned
int
queued
;
/* work has been queued */
};
static
kmem_zone_t
*
xfs_mru_elem_zone
;
static
struct
workqueue_struct
*
xfs_mru_reap_wq
;
/*
...
...
@@ -129,7 +135,7 @@ static struct workqueue_struct *xfs_mru_reap_wq;
*/
STATIC
unsigned
long
_xfs_mru_cache_migrate
(
xfs_mru_cache_t
*
mru
,
struct
xfs_mru_cache
*
mru
,
unsigned
long
now
)
{
unsigned
int
grp
;
...
...
@@ -193,8 +199,8 @@ _xfs_mru_cache_migrate(
*/
STATIC
void
_xfs_mru_cache_list_insert
(
xfs_mru_cache_t
*
mru
,
xfs_mru_cache_elem_t
*
elem
)
struct
xfs_mru_cache
*
mru
,
struct
xfs_mru_cache_elem
*
elem
)
{
unsigned
int
grp
=
0
;
unsigned
long
now
=
jiffies
;
...
...
@@ -231,10 +237,10 @@ _xfs_mru_cache_list_insert(
*/
STATIC
void
_xfs_mru_cache_clear_reap_list
(
xfs_mru_cache_t
*
mru
)
__releases
(
mru
->
lock
)
__acquires
(
mru
->
lock
)
struct
xfs_mru_cache
*
mru
)
__releases
(
mru
->
lock
)
__acquires
(
mru
->
lock
)
{
xfs_mru_cache_elem_t
*
elem
,
*
next
;
struct
xfs_mru_cache_elem
*
elem
,
*
next
;
struct
list_head
tmp
;
INIT_LIST_HEAD
(
&
tmp
);
...
...
@@ -252,15 +258,8 @@ _xfs_mru_cache_clear_reap_list(
spin_unlock
(
&
mru
->
lock
);
list_for_each_entry_safe
(
elem
,
next
,
&
tmp
,
list_node
)
{
/* Remove the element from the reap list. */
list_del_init
(
&
elem
->
list_node
);
/* Call the client's free function with the key and value pointer. */
mru
->
free_func
(
elem
->
key
,
elem
->
value
);
/* Free the element structure. */
kmem_zone_free
(
xfs_mru_elem_zone
,
elem
);
mru
->
free_func
(
elem
);
}
spin_lock
(
&
mru
->
lock
);
...
...
@@ -277,7 +276,8 @@ STATIC void
_xfs_mru_cache_reap
(
struct
work_struct
*
work
)
{
xfs_mru_cache_t
*
mru
=
container_of
(
work
,
xfs_mru_cache_t
,
work
.
work
);
struct
xfs_mru_cache
*
mru
=
container_of
(
work
,
struct
xfs_mru_cache
,
work
.
work
);
unsigned
long
now
,
next
;
ASSERT
(
mru
&&
mru
->
lists
);
...
...
@@ -304,28 +304,16 @@ _xfs_mru_cache_reap(
int
xfs_mru_cache_init
(
void
)
{
xfs_mru_elem_zone
=
kmem_zone_init
(
sizeof
(
xfs_mru_cache_elem_t
),
"xfs_mru_cache_elem"
);
if
(
!
xfs_mru_elem_zone
)
goto
out
;
xfs_mru_reap_wq
=
alloc_workqueue
(
"xfs_mru_cache"
,
WQ_MEM_RECLAIM
,
1
);
if
(
!
xfs_mru_reap_wq
)
goto
out_destroy_mru_elem_zone
;
return
0
;
out_destroy_mru_elem_zone:
kmem_zone_destroy
(
xfs_mru_elem_zone
);
out:
return
-
ENOMEM
;
return
0
;
}
void
xfs_mru_cache_uninit
(
void
)
{
destroy_workqueue
(
xfs_mru_reap_wq
);
kmem_zone_destroy
(
xfs_mru_elem_zone
);
}
/*
...
...
@@ -336,12 +324,12 @@ xfs_mru_cache_uninit(void)
*/
int
xfs_mru_cache_create
(
xfs_mru_cache_t
**
mrup
,
struct
xfs_mru_cache
**
mrup
,
unsigned
int
lifetime_ms
,
unsigned
int
grp_count
,
xfs_mru_cache_free_func_t
free_func
)
{
xfs_mru_cache_t
*
mru
=
NULL
;
struct
xfs_mru_cache
*
mru
=
NULL
;
int
err
=
0
,
grp
;
unsigned
int
grp_time
;
...
...
@@ -400,7 +388,7 @@ xfs_mru_cache_create(
*/
static
void
xfs_mru_cache_flush
(
xfs_mru_cache_t
*
mru
)
struct
xfs_mru_cache
*
mru
)
{
if
(
!
mru
||
!
mru
->
lists
)
return
;
...
...
@@ -420,7 +408,7 @@ xfs_mru_cache_flush(
void
xfs_mru_cache_destroy
(
xfs_mru_cache_t
*
mru
)
struct
xfs_mru_cache
*
mru
)
{
if
(
!
mru
||
!
mru
->
lists
)
return
;
...
...
@@ -438,38 +426,30 @@ xfs_mru_cache_destroy(
*/
int
xfs_mru_cache_insert
(
xfs_mru_cache_t
*
mru
,
struct
xfs_mru_cache
*
mru
,
unsigned
long
key
,
void
*
value
)
struct
xfs_mru_cache_elem
*
elem
)
{
xfs_mru_cache_elem_t
*
elem
;
int
error
;
ASSERT
(
mru
&&
mru
->
lists
);
if
(
!
mru
||
!
mru
->
lists
)
return
EINVAL
;
elem
=
kmem_zone_zalloc
(
xfs_mru_elem_zone
,
KM_SLEEP
);
if
(
!
elem
)
if
(
radix_tree_preload
(
GFP_KERNEL
))
return
ENOMEM
;
if
(
radix_tree_preload
(
GFP_KERNEL
))
{
kmem_zone_free
(
xfs_mru_elem_zone
,
elem
);
return
ENOMEM
;
}
INIT_LIST_HEAD
(
&
elem
->
list_node
);
elem
->
key
=
key
;
elem
->
value
=
value
;
spin_lock
(
&
mru
->
lock
);
radix_tree_insert
(
&
mru
->
store
,
key
,
elem
);
error
=
-
radix_tree_insert
(
&
mru
->
store
,
key
,
elem
);
radix_tree_preload_end
();
if
(
!
error
)
_xfs_mru_cache_list_insert
(
mru
,
elem
);
spin_unlock
(
&
mru
->
lock
);
return
0
;
return
error
;
}
/*
...
...
@@ -478,13 +458,12 @@ xfs_mru_cache_insert(
* the client data pointer for the removed element is returned, otherwise this
* function will return a NULL pointer.
*/
void
*
struct
xfs_mru_cache_elem
*
xfs_mru_cache_remove
(
xfs_mru_cache_t
*
mru
,
struct
xfs_mru_cache
*
mru
,
unsigned
long
key
)
{
xfs_mru_cache_elem_t
*
elem
;
void
*
value
=
NULL
;
struct
xfs_mru_cache_elem
*
elem
;
ASSERT
(
mru
&&
mru
->
lists
);
if
(
!
mru
||
!
mru
->
lists
)
...
...
@@ -492,17 +471,11 @@ xfs_mru_cache_remove(
spin_lock
(
&
mru
->
lock
);
elem
=
radix_tree_delete
(
&
mru
->
store
,
key
);
if
(
elem
)
{
value
=
elem
->
value
;
if
(
elem
)
list_del
(
&
elem
->
list_node
);
}
spin_unlock
(
&
mru
->
lock
);
if
(
elem
)
kmem_zone_free
(
xfs_mru_elem_zone
,
elem
);
return
value
;
return
elem
;
}
/*
...
...
@@ -511,13 +484,14 @@ xfs_mru_cache_remove(
*/
void
xfs_mru_cache_delete
(
xfs_mru_cache_t
*
mru
,
struct
xfs_mru_cache
*
mru
,
unsigned
long
key
)
{
void
*
value
=
xfs_mru_cache_remove
(
mru
,
key
)
;
struct
xfs_mru_cache_elem
*
elem
;
if
(
value
)
mru
->
free_func
(
key
,
value
);
elem
=
xfs_mru_cache_remove
(
mru
,
key
);
if
(
elem
)
mru
->
free_func
(
elem
);
}
/*
...
...
@@ -540,12 +514,12 @@ xfs_mru_cache_delete(
* status, we need to help it get it right by annotating the path that does
* not release the lock.
*/
void
*
struct
xfs_mru_cache_elem
*
xfs_mru_cache_lookup
(
xfs_mru_cache_t
*
mru
,
struct
xfs_mru_cache
*
mru
,
unsigned
long
key
)
{
xfs_mru_cache_elem_t
*
elem
;
struct
xfs_mru_cache_elem
*
elem
;
ASSERT
(
mru
&&
mru
->
lists
);
if
(
!
mru
||
!
mru
->
lists
)
...
...
@@ -560,7 +534,7 @@ xfs_mru_cache_lookup(
}
else
spin_unlock
(
&
mru
->
lock
);
return
elem
?
elem
->
value
:
NULL
;
return
elem
;
}
/*
...
...
@@ -570,7 +544,8 @@ xfs_mru_cache_lookup(
*/
void
xfs_mru_cache_done
(
xfs_mru_cache_t
*
mru
)
__releases
(
mru
->
lock
)
struct
xfs_mru_cache
*
mru
)
__releases
(
mru
->
lock
)
{
spin_unlock
(
&
mru
->
lock
);
}
fs/xfs/xfs_mru_cache.h
View file @
232c2f5c
...
...
@@ -18,24 +18,15 @@
#ifndef __XFS_MRU_CACHE_H__
#define __XFS_MRU_CACHE_H__
struct
xfs_mru_cache
;
/* Function pointer type for callback to free a client's data pointer. */
typedef
void
(
*
xfs_mru_cache_free_func_t
)(
unsigned
long
,
void
*
);
struct
xfs_mru_cache_elem
{
struct
list_head
list_node
;
unsigned
long
key
;
};
typedef
struct
xfs_mru_cache
{
struct
radix_tree_root
store
;
/* Core storage data structure. */
struct
list_head
*
lists
;
/* Array of lists, one per grp. */
struct
list_head
reap_list
;
/* Elements overdue for reaping. */
spinlock_t
lock
;
/* Lock to protect this struct. */
unsigned
int
grp_count
;
/* Number of discrete groups. */
unsigned
int
grp_time
;
/* Time period spanned by grps. */
unsigned
int
lru_grp
;
/* Group containing time zero. */
unsigned
long
time_zero
;
/* Time first element was added. */
xfs_mru_cache_free_func_t
free_func
;
/* Function pointer for freeing. */
struct
delayed_work
work
;
/* Workqueue data for reaping. */
unsigned
int
queued
;
/* work has been queued */
}
xfs_mru_cache_t
;
/* Function pointer type for callback to free a client's data pointer. */
typedef
void
(
*
xfs_mru_cache_free_func_t
)(
struct
xfs_mru_cache_elem
*
elem
);
int
xfs_mru_cache_init
(
void
);
void
xfs_mru_cache_uninit
(
void
);
...
...
@@ -44,10 +35,12 @@ int xfs_mru_cache_create(struct xfs_mru_cache **mrup, unsigned int lifetime_ms,
xfs_mru_cache_free_func_t
free_func
);
void
xfs_mru_cache_destroy
(
struct
xfs_mru_cache
*
mru
);
int
xfs_mru_cache_insert
(
struct
xfs_mru_cache
*
mru
,
unsigned
long
key
,
void
*
value
);
void
*
xfs_mru_cache_remove
(
struct
xfs_mru_cache
*
mru
,
unsigned
long
key
);
struct
xfs_mru_cache_elem
*
elem
);
struct
xfs_mru_cache_elem
*
xfs_mru_cache_remove
(
struct
xfs_mru_cache
*
mru
,
unsigned
long
key
);
void
xfs_mru_cache_delete
(
struct
xfs_mru_cache
*
mru
,
unsigned
long
key
);
void
*
xfs_mru_cache_lookup
(
struct
xfs_mru_cache
*
mru
,
unsigned
long
key
);
struct
xfs_mru_cache_elem
*
xfs_mru_cache_lookup
(
struct
xfs_mru_cache
*
mru
,
unsigned
long
key
);
void
xfs_mru_cache_done
(
struct
xfs_mru_cache
*
mru
);
#endif
/* __XFS_MRU_CACHE_H__ */
fs/xfs/xfs_super.c
View file @
232c2f5c
...
...
@@ -1749,13 +1749,9 @@ init_xfs_fs(void)
if
(
error
)
goto
out_destroy_wq
;
error
=
xfs_filestream_init
();
if
(
error
)
goto
out_mru_cache_uninit
;
error
=
xfs_buf_init
();
if
(
error
)
goto
out_
filestream
_uninit
;
goto
out_
mru_cache
_uninit
;
error
=
xfs_init_procfs
();
if
(
error
)
...
...
@@ -1782,8 +1778,6 @@ init_xfs_fs(void)
xfs_cleanup_procfs
();
out_buf_terminate:
xfs_buf_terminate
();
out_filestream_uninit:
xfs_filestream_uninit
();
out_mru_cache_uninit:
xfs_mru_cache_uninit
();
out_destroy_wq:
...
...
@@ -1802,7 +1796,6 @@ exit_xfs_fs(void)
xfs_sysctl_unregister
();
xfs_cleanup_procfs
();
xfs_buf_terminate
();
xfs_filestream_uninit
();
xfs_mru_cache_uninit
();
xfs_destroy_workqueues
();
xfs_destroy_zones
();
...
...
fs/xfs/xfs_trace.c
View file @
232c2f5c
...
...
@@ -46,6 +46,7 @@
#include "xfs_log_recover.h"
#include "xfs_inode_item.h"
#include "xfs_bmap_btree.h"
#include "xfs_filestream.h"
/*
* We include this last to have the helpers above available for the trace
...
...
fs/xfs/xfs_trace.h
View file @
232c2f5c
...
...
@@ -538,6 +538,64 @@ DEFINE_BUF_ITEM_EVENT(xfs_trans_bhold_release);
DEFINE_BUF_ITEM_EVENT
(
xfs_trans_binval
);
DEFINE_BUF_ITEM_EVENT
(
xfs_trans_buf_ordered
);
DECLARE_EVENT_CLASS
(
xfs_filestream_class
,
TP_PROTO
(
struct
xfs_inode
*
ip
,
xfs_agnumber_t
agno
),
TP_ARGS
(
ip
,
agno
),
TP_STRUCT__entry
(
__field
(
dev_t
,
dev
)
__field
(
xfs_ino_t
,
ino
)
__field
(
xfs_agnumber_t
,
agno
)
__field
(
int
,
streams
)
),
TP_fast_assign
(
__entry
->
dev
=
VFS_I
(
ip
)
->
i_sb
->
s_dev
;
__entry
->
ino
=
ip
->
i_ino
;
__entry
->
agno
=
agno
;
__entry
->
streams
=
xfs_filestream_peek_ag
(
ip
->
i_mount
,
agno
);
),
TP_printk
(
"dev %d:%d ino 0x%llx agno %u streams %d"
,
MAJOR
(
__entry
->
dev
),
MINOR
(
__entry
->
dev
),
__entry
->
ino
,
__entry
->
agno
,
__entry
->
streams
)
)
#define DEFINE_FILESTREAM_EVENT(name) \
DEFINE_EVENT(xfs_filestream_class, name, \
TP_PROTO(struct xfs_inode *ip, xfs_agnumber_t agno), \
TP_ARGS(ip, agno))
DEFINE_FILESTREAM_EVENT
(
xfs_filestream_free
);
DEFINE_FILESTREAM_EVENT
(
xfs_filestream_lookup
);
DEFINE_FILESTREAM_EVENT
(
xfs_filestream_scan
);
TRACE_EVENT
(
xfs_filestream_pick
,
TP_PROTO
(
struct
xfs_inode
*
ip
,
xfs_agnumber_t
agno
,
xfs_extlen_t
free
,
int
nscan
),
TP_ARGS
(
ip
,
agno
,
free
,
nscan
),
TP_STRUCT__entry
(
__field
(
dev_t
,
dev
)
__field
(
xfs_ino_t
,
ino
)
__field
(
xfs_agnumber_t
,
agno
)
__field
(
int
,
streams
)
__field
(
xfs_extlen_t
,
free
)
__field
(
int
,
nscan
)
),
TP_fast_assign
(
__entry
->
dev
=
VFS_I
(
ip
)
->
i_sb
->
s_dev
;
__entry
->
ino
=
ip
->
i_ino
;
__entry
->
agno
=
agno
;
__entry
->
streams
=
xfs_filestream_peek_ag
(
ip
->
i_mount
,
agno
);
__entry
->
free
=
free
;
__entry
->
nscan
=
nscan
;
),
TP_printk
(
"dev %d:%d ino 0x%llx agno %u streams %d free %d nscan %d"
,
MAJOR
(
__entry
->
dev
),
MINOR
(
__entry
->
dev
),
__entry
->
ino
,
__entry
->
agno
,
__entry
->
streams
,
__entry
->
free
,
__entry
->
nscan
)
);
DECLARE_EVENT_CLASS
(
xfs_lock_class
,
TP_PROTO
(
struct
xfs_inode
*
ip
,
unsigned
lock_flags
,
unsigned
long
caller_ip
),
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment