一個(gè)令人驚嘆的Linux內(nèi)核補(bǔ)丁
本文轉(zhuǎn)載自微信公眾號(hào)「相遇Linux」,作者JeffXie。轉(zhuǎn)載本文請(qǐng)聯(lián)系相遇Linux公眾號(hào)。
我們都知道linux內(nèi)核中的代碼非常精妙,但是有些代碼由于歷史原因,一個(gè)函數(shù)為了兼容處理各種情況,到最后可能變得非常糟糕,到處都是goto 和if,看的想跳樓(宋老師口頭禪^_^)
如果在系統(tǒng)中讀一個(gè)文件時(shí)會(huì)調(diào)用
generic_file_buffered_read
這個(gè)函數(shù)的功能是把磁盤中的數(shù)據(jù)讀到page之后,或者直接獲取cache中的page,然后調(diào)用copy_page_to_iter把page拷貝到用戶層的buffer中。
一天寂靜的下午,得空,打開電腦,準(zhǔn)備仔細(xì)研究一下這個(gè)函數(shù),發(fā)現(xiàn)這個(gè)函數(shù)的注釋上面就寫明了:
- * This is really ugly. But the goto's actually try to clarify some
- * of the logic when it comes to error handling etc.
仔細(xì)看了一下代碼,果然ugly的不像話,到處都是跳轉(zhuǎn)和判斷,令人眩暈,而且整個(gè)函數(shù)達(dá)到300行左右(原諒我看了注釋才斗膽這樣講:-) ),發(fā)現(xiàn)要是把這個(gè)函數(shù)看下去,今天一整天的心情都不會(huì)好了(當(dāng)時(shí)看的是Linux5.10的代碼)
- ssize_t generic_file_buffered_read(struct kiocb *iocb,
- struct iov_iter *iter, ssize_t written)
- {
- find_page:
- if (fatal_signal_pending(current)) {
- error = -EINTR;
- goto out;
- }
- error = wait_on_page_locked_killable(page);
- if (unlikely(error))
- goto readpage_error;
- if (PageUptodate(page))
- goto page_ok;
- if (inode->i_blkbits == PAGE_SHIFT ||
- !mapping->a_ops->is_partially_uptodate)
- goto page_not_up_to_date;
- /* pipes can't handle partially uptodate pages */
- if (unlikely(iov_iter_is_pipe(iter)))
- goto page_not_up_to_date;
- if (!trylock_page(page))
- goto page_not_up_to_date;
- /* Did it get truncated before we got the lock? */
- if (!page->mapping)
- goto page_not_up_to_date_locked;
- if (!mapping->a_ops->is_partially_uptodate(page,
- offset, iter->count))
- goto page_not_up_to_date_locked;
- unlock_page(page);
- }
于是就想內(nèi)核社區(qū)這么多牛人,他們整天盯著這些代碼,肯定很多人早已經(jīng)注意到了,于是想去看看有沒有人提交patch重構(gòu)這個(gè)函數(shù):
- ./scripts/get_maintainer.pl mm/filemap.c
- linux-kernel@vger.kernel.org (open list)
然后我就在下面網(wǎng)址中搜索generic_file_buffered_read,果然在10月25號(hào)(我看代碼那天在11月1號(hào)前后),就有人發(fā)了相關(guān)patch:
- https://lore.kernel.org/lkml/
然后迫不及待查看patch,并把整個(gè)patch 下載下來:
這里推薦一個(gè)工具,使用b4工具
- https://git.kernel.org/pub/scm/utils/b4/b4.git
可以直接從
- https://lore.kernel.org
獲取原始格式的patch,便于自己git am之后測(cè)試。
- # b4 am https://lore.kernel.org/lkml/20201025212949.602194-1-kent.overstreet@gmail.com
- v2_20201025_kent_overstreet_generic_file_buffered_read_improvements.cover
- v2_20201025_kent_overstreet_generic_file_buffered_read_improvements.mbx
然后直接 git am ,非常方便,這樣就打上了lore.kernel.org上提交的patch.
- git am v2_20201025_kent_overstreet_generic_file_buffered_read_improvements.mbx
- 提示:在git am之前,可以提前git apply --check 一下
- # gitlogdate -3
- fc5608fc9917 2020-10-25 Kent Overstreet fs: generic_file_buffered_read() now uses find_get_pages_contig
- 3bcadc3306be 2020-10-25 Kent Overstreet fs: Break generic_file_buffered_read up into multiple functions
- 3650b228f83a 2020-10-25 Linus Torvalds Linux 5.10-rc1
- alias gitlogdate='git log --pretty=format:"%h%x09%ad%x09%an%x09%s" --date=short'
打了這個(gè)patch之后,generic_file_buffered_read變成了這個(gè)樣子:
- ssize_t generic_file_buffered_read(struct kiocb *iocb,
- struct iov_iter *iter, ssize_t written)
- {
- ..
- pg_nr = generic_file_buffered_read_get_pages(iocb, iter,
- pages, nr_pages);
- ...
- for (i = 0; i < pg_nr; i++) {
- copied = copy_page_to_iter(pages[i], offset, bytes, iter);
- }
而且
- generic_file_buffered_read_get_pages
也非常之清晰:
- static int generic_file_buffered_read_get_pages(struct kiocb *iocb,
- struct iov_iter *iter,
- struct page **pages,
- unsigned int nr)
- nr_got = find_get_pages_contig(mapping, index, nr, pages);
- if (nr_got)
- goto got_pages;
- if (iocb->ki_flags & IOCB_NOIO)
- return -EAGAIN;
- page_cache_sync_readahead(mapping, ra, filp, index, last_index - index);
- nr_got = find_get_pages_contig(mapping, index, nr, pages);
- if (nr_got)
- goto got_pages;
- ...
- }
看完generic_file_buffered_read()之后,整個(gè)下午的心情都好多了。