Chapter 16: Page and Buffer Cache
linked list in which eachb_this_pageelement points to the next buffer. The only exception is the last
buffer, whereb_this_pageholds a null pointer:
fs/buffer.c
void create_empty_buffers(struct page *page,
unsigned long blocksize, unsigned long b_state)
{
struct buffer_head *bh, *head, *tail;
head = alloc_page_buffers(page, blocksize, 1);
...
The function then iterates over all buffer heads to set their state and generate a cyclic list:
fs/buffer.c
do {
bh->b_state |= b_state;
tail = bh;
bh = bh->b_this_page;
} while (bh);
tail->b_this_page = head;
...
The state of the buffers depends on the state of the data in the page in memory:
fs/buffer.c
if (PageUptodate(page) || PageDirty(page)) {
bh = head;
do {
if (PageDirty(page))
set_buffer_dirty(bh);
if (PageUptodate(page))
set_buffer_uptodate(bh);
bh = bh->b_this_page;
} while (bh != head);
}
attach_page_buffers(page, head);
}
set_buffer_dirtyandset_buffer_uptodateset the corresponding flagsBH_DirtyandBH_Uptodate,
respectively, in the buffer head.
The concluding invocation ofattach_page_buffersassociates the buffer with the page in two separate
steps:
- ThePG_privatebit is set in the page flags to inform the rest of the kernel code that the
privateelement of thepageinstance is in use.
- Theprivateelement of the page is equipped with a pointer to the first buffer head in the
cyclic list.
At first sight, setting thePG_Privateflag would not appear to be a far-reaching action. However, it
is important because it is the only way that the kernel is able to detect whether a page has attached