Skip to content

Commit d6c10f1

Browse files
htejunaxboe
authored andcommitted
writeback: implement WB_has_dirty_io wb_state flag
Currently, wb_has_dirty_io() determines whether a wb (bdi_writeback) has any dirty inode by testing all three IO lists on each invocation without actively keeping track. For cgroup writeback support, a single bdi will host multiple wb's each of which will host dirty inodes separately and we'll need to make bdi_has_dirty_io(), which currently only represents the root wb, aggregate has_dirty_io from all member wb's, which requires tracking transitions in has_dirty_io state on each wb. This patch introduces inode_wb_list_{move|del}_locked() to consolidate IO list operations leaving queue_io() the only other function which directly manipulates IO lists (via move_expired_inodes()). All three functions are updated to call wb_io_lists_[de]populated() which keep track of whether the wb has dirty inodes or not and record it using the new WB_has_dirty_io flag. inode_wb_list_moved_locked()'s return value indicates whether the wb had no dirty inodes before. mark_inode_dirty() is restructured so that the return value of inode_wb_list_move_locked() can be used for deciding whether to wake up the wb. While at it, change {bdi|wb}_has_dirty_io()'s return values to bool. These functions were returning 0 and 1 before. Also, add a comment explaining the synchronization of wb_state flags. v2: Updated to accommodate b_dirty_time. Signed-off-by: Tejun Heo <[email protected]> Cc: Jens Axboe <[email protected]> Cc: Jan Kara <[email protected]> Signed-off-by: Jens Axboe <[email protected]>
1 parent 703c270 commit d6c10f1

File tree

4 files changed

+91
-30
lines changed

4 files changed

+91
-30
lines changed

fs/fs-writeback.c

Lines changed: 86 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,66 @@ static inline struct inode *wb_inode(struct list_head *head)
9393

9494
EXPORT_TRACEPOINT_SYMBOL_GPL(wbc_writepage);
9595

96+
static bool wb_io_lists_populated(struct bdi_writeback *wb)
97+
{
98+
if (wb_has_dirty_io(wb)) {
99+
return false;
100+
} else {
101+
set_bit(WB_has_dirty_io, &wb->state);
102+
return true;
103+
}
104+
}
105+
106+
static void wb_io_lists_depopulated(struct bdi_writeback *wb)
107+
{
108+
if (wb_has_dirty_io(wb) && list_empty(&wb->b_dirty) &&
109+
list_empty(&wb->b_io) && list_empty(&wb->b_more_io))
110+
clear_bit(WB_has_dirty_io, &wb->state);
111+
}
112+
113+
/**
114+
* inode_wb_list_move_locked - move an inode onto a bdi_writeback IO list
115+
* @inode: inode to be moved
116+
* @wb: target bdi_writeback
117+
* @head: one of @wb->b_{dirty|io|more_io}
118+
*
119+
* Move @inode->i_wb_list to @list of @wb and set %WB_has_dirty_io.
120+
* Returns %true if @inode is the first occupant of the !dirty_time IO
121+
* lists; otherwise, %false.
122+
*/
123+
static bool inode_wb_list_move_locked(struct inode *inode,
124+
struct bdi_writeback *wb,
125+
struct list_head *head)
126+
{
127+
assert_spin_locked(&wb->list_lock);
128+
129+
list_move(&inode->i_wb_list, head);
130+
131+
/* dirty_time doesn't count as dirty_io until expiration */
132+
if (head != &wb->b_dirty_time)
133+
return wb_io_lists_populated(wb);
134+
135+
wb_io_lists_depopulated(wb);
136+
return false;
137+
}
138+
139+
/**
140+
* inode_wb_list_del_locked - remove an inode from its bdi_writeback IO list
141+
* @inode: inode to be removed
142+
* @wb: bdi_writeback @inode is being removed from
143+
*
144+
* Remove @inode which may be on one of @wb->b_{dirty|io|more_io} lists and
145+
* clear %WB_has_dirty_io if all are empty afterwards.
146+
*/
147+
static void inode_wb_list_del_locked(struct inode *inode,
148+
struct bdi_writeback *wb)
149+
{
150+
assert_spin_locked(&wb->list_lock);
151+
152+
list_del_init(&inode->i_wb_list);
153+
wb_io_lists_depopulated(wb);
154+
}
155+
96156
static void wb_wakeup(struct bdi_writeback *wb)
97157
{
98158
spin_lock_bh(&wb->work_lock);
@@ -217,7 +277,7 @@ void inode_wb_list_del(struct inode *inode)
217277
struct bdi_writeback *wb = inode_to_wb(inode);
218278

219279
spin_lock(&wb->list_lock);
220-
list_del_init(&inode->i_wb_list);
280+
inode_wb_list_del_locked(inode, wb);
221281
spin_unlock(&wb->list_lock);
222282
}
223283

@@ -232,24 +292,22 @@ void inode_wb_list_del(struct inode *inode)
232292
*/
233293
static void redirty_tail(struct inode *inode, struct bdi_writeback *wb)
234294
{
235-
assert_spin_locked(&wb->list_lock);
236295
if (!list_empty(&wb->b_dirty)) {
237296
struct inode *tail;
238297

239298
tail = wb_inode(wb->b_dirty.next);
240299
if (time_before(inode->dirtied_when, tail->dirtied_when))
241300
inode->dirtied_when = jiffies;
242301
}
243-
list_move(&inode->i_wb_list, &wb->b_dirty);
302+
inode_wb_list_move_locked(inode, wb, &wb->b_dirty);
244303
}
245304

246305
/*
247306
* requeue inode for re-scanning after bdi->b_io list is exhausted.
248307
*/
249308
static void requeue_io(struct inode *inode, struct bdi_writeback *wb)
250309
{
251-
assert_spin_locked(&wb->list_lock);
252-
list_move(&inode->i_wb_list, &wb->b_more_io);
310+
inode_wb_list_move_locked(inode, wb, &wb->b_more_io);
253311
}
254312

255313
static void inode_sync_complete(struct inode *inode)
@@ -358,6 +416,8 @@ static void queue_io(struct bdi_writeback *wb, struct wb_writeback_work *work)
358416
moved = move_expired_inodes(&wb->b_dirty, &wb->b_io, 0, work);
359417
moved += move_expired_inodes(&wb->b_dirty_time, &wb->b_io,
360418
EXPIRE_DIRTY_ATIME, work);
419+
if (moved)
420+
wb_io_lists_populated(wb);
361421
trace_writeback_queue_io(wb, work, moved);
362422
}
363423

@@ -483,10 +543,10 @@ static void requeue_inode(struct inode *inode, struct bdi_writeback *wb,
483543
redirty_tail(inode, wb);
484544
} else if (inode->i_state & I_DIRTY_TIME) {
485545
inode->dirtied_when = jiffies;
486-
list_move(&inode->i_wb_list, &wb->b_dirty_time);
546+
inode_wb_list_move_locked(inode, wb, &wb->b_dirty_time);
487547
} else {
488548
/* The inode is clean. Remove from writeback lists. */
489-
list_del_init(&inode->i_wb_list);
549+
inode_wb_list_del_locked(inode, wb);
490550
}
491551
}
492552

@@ -628,7 +688,7 @@ writeback_single_inode(struct inode *inode, struct bdi_writeback *wb,
628688
* touch it. See comment above for explanation.
629689
*/
630690
if (!(inode->i_state & I_DIRTY_ALL))
631-
list_del_init(&inode->i_wb_list);
691+
inode_wb_list_del_locked(inode, wb);
632692
spin_unlock(&wb->list_lock);
633693
inode_sync_complete(inode);
634694
out:
@@ -1327,37 +1387,39 @@ void __mark_inode_dirty(struct inode *inode, int flags)
13271387
* reposition it (that would break b_dirty time-ordering).
13281388
*/
13291389
if (!was_dirty) {
1390+
struct list_head *dirty_list;
13301391
bool wakeup_bdi = false;
13311392
bdi = inode_to_bdi(inode);
13321393

13331394
spin_unlock(&inode->i_lock);
13341395
spin_lock(&bdi->wb.list_lock);
1335-
if (bdi_cap_writeback_dirty(bdi)) {
1336-
WARN(!test_bit(WB_registered, &bdi->wb.state),
1337-
"bdi-%s not registered\n", bdi->name);
13381396

1339-
/*
1340-
* If this is the first dirty inode for this
1341-
* bdi, we have to wake-up the corresponding
1342-
* bdi thread to make sure background
1343-
* write-back happens later.
1344-
*/
1345-
if (!wb_has_dirty_io(&bdi->wb))
1346-
wakeup_bdi = true;
1347-
}
1397+
WARN(bdi_cap_writeback_dirty(bdi) &&
1398+
!test_bit(WB_registered, &bdi->wb.state),
1399+
"bdi-%s not registered\n", bdi->name);
13481400

13491401
inode->dirtied_when = jiffies;
13501402
if (dirtytime)
13511403
inode->dirtied_time_when = jiffies;
1404+
13521405
if (inode->i_state & (I_DIRTY_INODE | I_DIRTY_PAGES))
1353-
list_move(&inode->i_wb_list, &bdi->wb.b_dirty);
1406+
dirty_list = &bdi->wb.b_dirty;
13541407
else
1355-
list_move(&inode->i_wb_list,
1356-
&bdi->wb.b_dirty_time);
1408+
dirty_list = &bdi->wb.b_dirty_time;
1409+
1410+
wakeup_bdi = inode_wb_list_move_locked(inode, &bdi->wb,
1411+
dirty_list);
1412+
13571413
spin_unlock(&bdi->wb.list_lock);
13581414
trace_writeback_dirty_inode_enqueue(inode);
13591415

1360-
if (wakeup_bdi)
1416+
/*
1417+
* If this is the first dirty inode for this bdi,
1418+
* we have to wake-up the corresponding bdi thread
1419+
* to make sure background write-back happens
1420+
* later.
1421+
*/
1422+
if (bdi_cap_writeback_dirty(bdi) && wakeup_bdi)
13611423
wb_wakeup_delayed(&bdi->wb);
13621424
return;
13631425
}

include/linux/backing-dev-defs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ struct dentry;
2121
enum wb_state {
2222
WB_registered, /* bdi_register() was done */
2323
WB_writeback_running, /* Writeback is in progress */
24+
WB_has_dirty_io, /* Dirty inodes on ->b_{dirty|io|more_io} */
2425
};
2526

2627
enum wb_congested_state {

include/linux/backing-dev.h

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,19 +29,17 @@ void bdi_start_writeback(struct backing_dev_info *bdi, long nr_pages,
2929
enum wb_reason reason);
3030
void bdi_start_background_writeback(struct backing_dev_info *bdi);
3131
void wb_workfn(struct work_struct *work);
32-
int bdi_has_dirty_io(struct backing_dev_info *bdi);
32+
bool bdi_has_dirty_io(struct backing_dev_info *bdi);
3333
void wb_wakeup_delayed(struct bdi_writeback *wb);
3434

3535
extern spinlock_t bdi_lock;
3636
extern struct list_head bdi_list;
3737

3838
extern struct workqueue_struct *bdi_wq;
3939

40-
static inline int wb_has_dirty_io(struct bdi_writeback *wb)
40+
static inline bool wb_has_dirty_io(struct bdi_writeback *wb)
4141
{
42-
return !list_empty(&wb->b_dirty) ||
43-
!list_empty(&wb->b_io) ||
44-
!list_empty(&wb->b_more_io);
42+
return test_bit(WB_has_dirty_io, &wb->state);
4543
}
4644

4745
static inline void __add_wb_stat(struct bdi_writeback *wb,

mm/backing-dev.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,7 @@ static int __init default_bdi_init(void)
256256
}
257257
subsys_initcall(default_bdi_init);
258258

259-
int bdi_has_dirty_io(struct backing_dev_info *bdi)
259+
bool bdi_has_dirty_io(struct backing_dev_info *bdi)
260260
{
261261
return wb_has_dirty_io(&bdi->wb);
262262
}

0 commit comments

Comments
 (0)