割り込みの後半部、ファイルシステム

					2022年02月04日
情報科学類 オペレーティングシステム II

                                       筑波大学 システム情報系 
                                       新城 靖
                                       <yas@cs.tsukuba.ac.jp>

このページは、次の URL にあります。
http://www.coins.tsukuba.ac.jp/~yas/coins/os2-2021/2022-02-04
あるいは、次のページから手繰っていくこともできます。
http://www.coins.tsukuba.ac.jp/~yas/
http://www.cs.tsukuba.ac.jp/~yas/

■連絡事項

試験について レポートは、試験日の、試験が始まる「前」まで受け取る。その後は受け取らない。

メモ用紙の作り方

■今日の大事な話

割り込みの前半部と後半部の分離 ファイルシステムの構造

■割り込み、後半部

割り込みの問題点 割り込みハンドラは、差し迫ったことだけをして、それ以外のことは、後で、 かつ、割り込みを許可した状態で実行したい。

Linux では、割り込みの処理を2つに分ける。

デバイス、割り込みハンドラ、Softirqみハンドラ、Taskletハンドラ
図? 割り込み処理の前半部分と後半部分

1月21日、 request_irq() で示したのは、前半の話。今日は、後半の話。

◆gfp_t gfp_mask

kmalloc() 等で使われる gfp_t gfp_mask (1月13日の資料) のスリープの可否に着目。

◆後半部(bottom half、bottom halves)

後半部の仕事は、割り込み関連の仕事のうち、割り込みハンドラでは行わない 部分を行う。割り込みハンドラ(前半部)を軽くすると、自然に後半部の仕事 は多くなる。

割り込みハンドラ(前半部)と後半部の役割分担の目安。

Linux では、後半部の仕組みとして、歴史的事情から様々な種類がある。 普通は、Tasklet か Work Queue を使えばよい。

注意1: Tasklet は、task 構造体とはまったく関係ない。名前がよくない。

注意2: Softirq という用語を、割り込み処理の後半部という意味で使う人もい る。

注意3: 伝統的なUnixでは、top half は、システム・コールから派生する上位 層の処理、bottom half は、割り込みから派生する下位層の処理の意味で使わ れることがある。Linux では、top half, bottom half は、割り込み処理の前 半部分と後半部分の意味に使う。

■Tasklet

Tasklet で1つの仕事は次のような、struct tasklet_struct で表現される。

linux-5.15.12/include/linux/interrupt.h
 587:	struct tasklet_struct
 588:	{
 589:	        struct tasklet_struct *next;
 590:	        unsigned long state;
 591:	        atomic_t count;
 592:	        bool use_callback;
 593:	        union {
 594:	                void (*func)(unsigned long data);
 595:	                void (*callback)(struct tasklet_struct *t);
 596:	        };
 597:	        unsigned long data;
 598:	};
全体として、次のようなキューに接続されている。

tasklet_vec、head、next、next、next
図? Taskletにおける仕事のキュー

◆Taskletの構造体の宣言

静的に struct tasklet_struct を宣言するには、次のマクロを利用すると簡単 である。
DECLARE_TASKLET(name, func)
    有効な(count==0) の struct tasklet_struct を宣言する

DECLARE_TASKLET_DISABLED(name, func)
    無効な(count==1) の struct tasklet_struct を宣言する
他の構造体に struct tasklet_struct を埋め込む時や kmalloc()等で動的に確保した場合には、次の関数も使える。
void tasklet_init(struct tasklet_struct *t, 
    void (*func)(unsigned long), unsigned long data);

void tasklet_setup(struct tasklet_struct *t,
    void (*callback)(struct tasklet_struct *))
その他に、生成消滅有効無効に関して次のような操作がある。

◆Taskletのハンドラ

tasklet_init() 等の場合、Tasklet のハンドラは、次のような関数である。 data が渡される。
void tasklet_handler(unsigned long data) {
    ...
}
tasklet_setup() 等の場合、Tasklet のハンドラは、次のような関数である。 struct tasklet_struct * が渡される。 struct timer_listで示した例 や 後述する EXT4_I()と同様に、 container_of() で外側の構造体を取り出す。
void tasklet_handler(struct tasklet_struct *t) {
    ...
}

◆Taskletの実行要求

Tasklet のハンドラを実行したい時には、tasklet_schedule() を呼ぶ。
void tasklet_schedule(struct tasklet_struct *t)
    Tasklet t を通常の優先度でスケジュールする

void tasklet_hi_schedule(struct tasklet_struct *t)
    Tasklet t を高優先度でスケジュールする
すると、それは「そのうちに」1度だけ実行される。

◆Taskletの利用例

無線LANのドライバでの利用例。
linux-5.15.12/drivers/net/wireless/ath/ath11k/ce.h
 150:	struct ath11k_ce_pipe {
...
 160:	        struct tasklet_struct intr_tq;
...
 165:	};

linux-5.15.12/drivers/net/wireless/ath/ath11k/pci.c
 780:	static int ath11k_pci_config_irq(struct ath11k_base *ab)
 781:	{
...
 806:	                tasklet_setup(&ce_pipe->intr_tq, ath11k_pci_ce_tasklet);
 807:	
 808:	                ret = request_irq(irq, ath11k_pci_ce_interrupt_handler,
 809:	                                  IRQF_SHARED, irq_name[irq_idx],
 810:	                                  ce_pipe);
...
 828:	}
linux-5.15.12/drivers/net/wireless/ath/ath11k/pci.c
 605:	static irqreturn_t ath11k_pci_ce_interrupt_handler(int irq, void *arg)
 606:	{
 607:	        struct ath11k_ce_pipe *ce_pipe = arg;
 608:	
 609:	        /* last interrupt received for this CE */
 610:	        ce_pipe->timestamp = jiffies;
 611:	
 612:	        ath11k_pci_ce_irq_disable(ce_pipe->ab, ce_pipe->pipe_num);
 613:	        tasklet_schedule(&ce_pipe->intr_tq);
 614:	
 615:	        return IRQ_HANDLED;
 616:	}

 596:	static void ath11k_pci_ce_tasklet(struct tasklet_struct *t)
 597:	{
 598:	        struct ath11k_ce_pipe *ce_pipe = from_tasklet(ce_pipe, t, intr_tq);
 599:	
 600:	        ath11k_ce_per_engine_service(ce_pipe->ab, ce_pipe->pipe_num);
 601:	
 602:	        ath11k_pci_ce_irq_enable(ce_pipe->ab, ce_pipe->pipe_num);
 603:	}

linux-5.15.12/include/linux/interrupt.h
 614:	#define from_tasklet(var, callback_tasklet, tasklet_fieldname)  \
 615:	        container_of(callback_tasklet, typeof(*var), tasklet_fieldname)

■Work Queue

割り込みに関連した処理で、次のような場合(TaskletやSoftirq では不可能な 場合)にWork Queue使う。

◆Work Queueのワーカ・スレッド

Work Queue のワーカ・スレッドは、カーネル・レベルのスレッドで、 割り込み処理の後半部分の処理を行うことができる。 (割り込み処理以外で使ってもよい。)

workqueue_struct、next、next、next、next
図? Work Queueにおける仕事のキュー

キューにつながれる仕事は、Tasklet の仕事とほとんど同じで、関数へのポイ ンタ func と data からなる。処理の主体が、ワーカ・スレッドと呼ばれるカー ネル・レベルのスレッドである所が違う。

汎用の Work Queue デフォルトのワーカ・スレッドは、kworker/n (nはプロセッ サ番号) とよばれ、プロセッサごとに作られる。1つのスレッドで、様々な要 求元の仕事をこなす。下の例では、1つのプロセッサに5個のスレッドが 作られている。そのうち2つは、nice 値が -20 で高優先度。

$ ps alx|grep worker|wc [←]
     22     289    1881
$ ps alx|grep 'worker.*/0' [←]
1     0  4779     2  20   0      0     0 worker S    ?          0:00 [kworker/0:2]
1     0  5276     2  20   0      0     0 worker S    ?          0:00 [kworker/0:1]
1     0  5479     2  20   0      0     0 worker S    ?          0:00 [kworker/0:0]
5     0 12906     2   0 -20      0     0 worker S<   ?          0:59 [kworker/0:1H]
5     0 30659     2   0 -20      0     0 worker S<   ?          0:07 [kworker/0:0H]
0  1013  5803  5611  20   0 117076  1016 pipe_w S+   pts/2      0:00 grep --color=auto worker.*/0
$ []
汎用の Work Queue のワーカ・スレッドの他に、専用のワーカ・スレッドを作 ることもできる。

◆work_struct構造体

ワーク・キューで用いる 1 つの仕事は、構造体 struct work_struct で表現さ れる。
linux-5.15.12/include/linux/workqueue.h
  21:	typedef void (*work_func_t)(struct work_struct *work);

  97:	struct work_struct {
  98:	        atomic_long_t data;
  99:	        struct list_head entry;
 100:	        work_func_t func;
...
 104:	};

次のように、初期化する。
struct work_struct my_work;
...
INIT_WORK(&my_work,my_work_handler);

◆Work Queue ハンドラ

Work Queue ハンドラは、次のように引数に struct work_struct へのポインタを取る。 必要なら container_of() で外側の構造体を取り出す。
void my_work_handler(struct work_struct *work)
{
...
}

◆Work の実行要求

ハンドラを呼び出したい時には、次の関数を呼ぶ。
     schedule_work(&work);
この結果、INIT_WORK() で設定したハンドラがワーカ・スレッドにより「その うち」に呼び出される。

schedule_work() では、即座に実行される可能性もある。少し後に実行したい (間を取りたい)時には、次の関数を呼ぶ。

     schedule_delayed_work(&work, ticks);
ticks は、どのくらい間をとるか。単位は、 ticks (jiffiesの単位)。 多くのシステムで10ミリ秒-1ミリ秒で、設定によって異なる。

◆flush_scheduled_work()

schedule_work() で要求した仕事が完了したことを待って、次の仕事を投げた いことがある。その時には、flush_scheduled_work() を呼ぶ。

◆alloc_workqueue()

専用のワーカ・スレッドを作りたい時には、次のような関数を使う。

◆Work Queueの利用例

x86 CMOS RTC での割り込みハンドラの例。 再掲。
linux-5.15.12/arch/x86/include/asm/mc146818rtc.h
 101:	#define RTC_IRQ 8

linux-5.15.12/drivers/rtc/rtc-cmos.c
  73:	struct cmos_rtc {
  74:	        struct rtc_device       *rtc;
  75:	        struct device           *dev;
  76:	        int                     irq;
....
  92:	};

 636:	static struct cmos_rtc  cmos_rtc;

 693:	static int INITSECTION
 694:	cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq)
 695:	{
...
 785:	        cmos_rtc.rtc = devm_rtc_allocate_device(dev);
...
 846:	                        rtc_cmos_int_handler = cmos_interrupt;
...
 848:	                retval = request_irq(rtc_irq, rtc_cmos_int_handler,
 849:	                                0, dev_name(&cmos_rtc.rtc->dev),
 850:	                                cmos_rtc.rtc);
...
 861:	        retval = devm_rtc_register_device(cmos_rtc.rtc);
...
 894:	}

 638:	static irqreturn_t cmos_interrupt(int irq, void *p)
 639:	{
 640:	        u8              irqstat;
...
 652:	        irqstat = CMOS_READ(RTC_INTR_FLAGS);
...
 679:	        if (is_intr(irqstat)) {
 680:	                rtc_update_irq(p, 1, irqstat);
 681:	                return IRQ_HANDLED;
 682:	        } else
 683:	                return IRQ_NONE;
 684:	}
以下、追加。
linux-5.15.12/include/linux/rtc.h
  84:	struct rtc_device {
...
 109:	        struct work_struct irqwork;
...
 162:	};

linux-5.15.12/drivers/rtc/class.c
 194:	static struct rtc_device *rtc_allocate_device(void)
 195:	{
 196:	        struct rtc_device *rtc;
 197:	
 198:	        rtc = kzalloc(sizeof(*rtc), GFP_KERNEL);
...
 224:	        INIT_WORK(&rtc->irqwork, rtc_timer_do_work);
...
 236:	        return rtc;
 237:	}

linux-5.15.12/drivers/rtc/interface.c
 886:	void rtc_timer_do_work(struct work_struct *work)
...
 952:	}

 672:	void rtc_update_irq(struct rtc_device *rtc,
 673:	                    unsigned long num, unsigned long events)
 674:	{
...
 679:	        schedule_work(&rtc->irqwork);
 680:	}

■割り込みの後半部の選択

■ファイルシステム

◆求められる機能

システム・コール 様々な物理媒体と接続方法の利用 様々なディスク上の表現(ファイルシステム) 注意: 「ファイルシステム」という言葉が、様々な意味で使われる

◆層構造

問題: 様々な物理媒体やディスク上の表現の違いを吸収して、共通のシステム・ コールでファイルを扱いたい。

解決策:

図? システム・コール、VFS、ブロックデバイス
図? 層構造を用いたファイル・システムの実装

◆継承/委譲

問題: ファイルシステム間で共通部分のコードを再利用したい。

解決策

◆VFSレベルのファイルの概念

◆inode番号

ls -i で inode 番号が表示される。
$ ls -l /usr/bin/perl{,5.10.1} [←]
-rwxr-xr-x. 2 root root 13304 Mar 22  2017 /usr/bin/perl
-rwxr-xr-x. 2 root root 13304 Mar 22  2017 /usr/bin/perl5.10.1
$ ls -li /usr/bin/perl{,5.10.1} [←]
1846686 -rwxr-xr-x. 2 root root 13304 Mar 22  2017 /usr/bin/perl
1846686 -rwxr-xr-x. 2 root root 13304 Mar 22  2017 /usr/bin/perl5.10.1
$ []

◆/etc/fstab

/etc/fstab は、「起動時に」にマウントすべきファイルシステムのリストを保 持している。
$ grep -v '#' /etc/fstab  [←]

UUID=9cfbc67e-781c-48d1-8303-1dde8ce87ee9 /                       ext4    defaults        1 1
UUID=bab1faf1-5f5b-4a2a-b24f-e850a2b0b82d /boot                   ext4    defaults        1 2
UUID=a1f61ff2-2c99-4c54-8c3e-2178eed3ec10 swap                    swap    defaults        0 0
tmpfs                   /dev/shm                tmpfs   defaults        0 0
devpts                  /dev/pts                devpts  gid=5,mode=620  0 0
sysfs                   /sys                    sysfs   defaults        0 0
proc                    /proc                   proc    defaults        0 0
pentas-fs:/vol0/home   /home        nfs     rw,hard,bg,nfsvers=3,intr 0 0
pentas-fs:/vol0/web    /var/www     nfs     rw,hard,bg,nfsvers=3,intr 0 0
pentas-fs:/vol0/local3 /usr/local3  nfs     rw,hard,bg,nfsvers=3,intr 0 0
$ df / [←]
Filesystem     1K-blocks    Used Available Use% Mounted on
/dev/sda3       49071944 6721604  39857568  15% /
$ blkid /dev/sda3 [←]
/dev/sda3: UUID="9cfbc67e-781c-48d1-8303-1dde8ce87ee9" TYPE="ext4" 
$ ls -l /dev/sda3 [←]
brw-rw----. 1 root disk 8, 3 Feb  2 10:50 /dev/sda3
$ lsblk  [←]
NAME   MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
sda      8:0    0   50G  0 disk 
|-sda1   8:1    0  512M  0 part /boot
|-sda2   8:2    0    2G  0 part [SWAP]
`-sda3   8:3    0 47.6G  0 part /
sr0     11:0    1 1024M  0 rom  
$ ls -l /dev/sda  [←]
brw-rw----. 1 root disk 8, 0 Feb  2 10:50 /dev/sda
$ []

◆stat()システム・コールとstatコマンド

STAT(2)                    Linux Programmer's Manual                   STAT(2)
...
       int stat(const char *path, struct stat *buf);
...
          struct stat {
              dev_t     st_dev;     /* ID of device containing file */
              ino_t     st_ino;     /* inode number */
              mode_t    st_mode;    /* protection */
              nlink_t   st_nlink;   /* number of hard links */
              uid_t     st_uid;     /* user ID of owner */
              gid_t     st_gid;     /* group ID of owner */
              dev_t     st_rdev;    /* device ID (if special file) */
              off_t     st_size;    /* total size, in bytes */
              blksize_t st_blksize; /* blocksize for filesystem I/O */
              blkcnt_t  st_blocks;  /* number of blocks allocated */
              time_t    st_atime;   /* time of last access */
              time_t    st_mtime;   /* time of last modification */
              time_t    st_ctime;   /* time of last status change */
          };
stat コマンドを使うと stat システム・コールで返される値に近いものが表示 される。
$ stat .bashrc [←]
  File: `.bashrc'
  Size: 240		Blocks: 16         IO Block: 65536  regular file
Device: 14h/20d		Inode: 50700660    Links: 1
Access: (0644/-rw-r--r--)  Uid: ( 1013/     yas)   Gid: (  510/    prof)
Access: 2019-01-27 15:29:58.000000000 +0900
Modify: 2018-06-08 10:46:57.004451000 +0900
Change: 2018-06-08 10:46:57.004451000 +0900
$ []

■VFSのオブジェクト

VFSの実装では、次のようオブジェクト(データと手続きをカプセル 化したもの)を通じて実装される。 ファイルシステム固有の処理は、_operations の手続きを入れ替えることで実 現される。固有のデータは、構造体を入れ子にしたり、固有データへのポイン タを使ったりして実現している。

図? struct file,struct dentry,struct inode,struct super_block
図? スーパーブロック、inode、dentry、file

◆struct file

struct file は、プロセスがファイルを open() した時に割り当てられる。
    int fd1 = open("file1",O_RDONLY);
    int fd2 = open("file1",O_RDONLY);
ファイル名 "file1" で表現されるファイルの inode 構造体は、1 個でも、 file 構造体は、2 個割り当てられる。

ディスク上には対応するデータ構造は存在しない。

linux-5.15.12/include/linux/fs.h
 965:	struct file {
...
 970:	        struct path             f_path;
 971:	        struct inode            *f_inode;       /* cached value */
 972:	        const struct file_operations    *f_op;
...
 980:	        atomic_long_t           f_count;
 981:	        unsigned int            f_flags;
 982:	        fmode_t                 f_mode;
...
 984:	        loff_t                  f_pos;
...
 994:	        void                    *private_data;
...
1000:	        struct address_space    *f_mapping;
...
1003:	} __randomize_layout
1004:	  __attribute__((aligned(4)));  /* lest something weird decides that 2 is OK */

linux-5.15.12/include/linux/path.h
   8:	struct path {
   9:	        struct vfsmount *mnt;
  10:	        struct dentry *dentry;
  11:	} __randomize_layout;

◆継承・委譲の実装方法

C言語によるオブジェクト指向の継承・委譲の実装方法。共通インスタンス変数・関数、固有インスタンス変数関数の置き方

図?

図? 方法1

図?

図? 方法2

図?

図? 方法3

◆struct file_operations

デバイスドライバの回 でも登場している。

struct fileの操作は、たとえば次のような形で行われる。 第1引数は、struct file *。

    struct file *file;
    file->f_op->read(file, buf, count, pos);
f_op には、次のような手続きがある。各ファイルシステム (ext4,nfs,tmpfs,...) ごとに、手続きの実体は異なるが、インタフェースは同じ。
linux-5.15.12/include/linux/fs.h
2071:	struct file_operations {
2072:	        struct module *owner;
2073:	        loff_t (*llseek) (struct file *, loff_t, int);
2074:	        ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
2075:	        ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
2076:	        ssize_t (*read_iter) (struct kiocb *, struct iov_iter *);
2077:	        ssize_t (*write_iter) (struct kiocb *, struct iov_iter *);
2078:	        int (*iopoll)(struct kiocb *kiocb, bool spin);
2079:	        int (*iterate) (struct file *, struct dir_context *);
2080:	        int (*iterate_shared) (struct file *, struct dir_context *);
2081:	        __poll_t (*poll) (struct file *, struct poll_table_struct *);
2082:	        long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
2083:	        long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
2084:	        int (*mmap) (struct file *, struct vm_area_struct *);
2085:	        unsigned long mmap_supported_flags;
2086:	        int (*open) (struct inode *, struct file *);
2087:	        int (*flush) (struct file *, fl_owner_t id);
2088:	        int (*release) (struct inode *, struct file *);
2089:	        int (*fsync) (struct file *, loff_t, loff_t, int datasync);
2090:	        int (*fasync) (int, struct file *, int);
2091:	        int (*lock) (struct file *, int, struct file_lock *);
2092:	        ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
2093:	        unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
2094:	        int (*check_flags)(int);
2095:	        int (*flock) (struct file *, int, struct file_lock *);
2096:	        ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
2097:	        ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
2098:	        int (*setlease)(struct file *, long, struct file_lock **, void **);
2099:	        long (*fallocate)(struct file *file, int mode, loff_t offset,
2100:	                          loff_t len);
2101:	        void (*show_fdinfo)(struct seq_file *m, struct file *f);
2102:	#ifndef CONFIG_MMU
2103:	        unsigned (*mmap_capabilities)(struct file *);
2104:	#endif
2105:	        ssize_t (*copy_file_range)(struct file *, loff_t, struct file *,
2106:	                        loff_t, size_t, unsigned int);
2107:	        loff_t (*remap_file_range)(struct file *file_in, loff_t pos_in,
2108:	                                   struct file *file_out, loff_t pos_out,
2109:	                                   loff_t len, unsigned int remap_flags);
2110:	        int (*fadvise)(struct file *, loff_t, loff_t, int);
2111:	} __randomize_layout;
主な手続きの意味

◆struct dentry

struct dentry は、ディレクトリに含まれている名前の要素(「/」を含まない 名前)を表す構造体。 struct dentry は、メモリ中にのみ存在する。ディスク中に対応するデータは ない。ディレクトリで名前を検索したりファイルを作成する時にメモリ中に作 られる。
linux-5.15.12/include/linux/dcache.h
  91:	struct dentry {
...
  96:	        struct dentry *d_parent;        /* parent directory */
  97:	        struct qstr d_name;
  98:	        struct inode *d_inode;          /* Where the name belongs to - NULL is
  99:	                                         * negative */
 100:	        unsigned char d_iname[DNAME_INLINE_LEN];        /* small names */
 101:	
 102:	        /* Ref lookup also touches following */
 103:	        struct lockref d_lockref;       /* per-dentry lock and refcount */
 104:	        const struct dentry_operations *d_op;
 105:	        struct super_block *d_sb;       /* The root of the dentry tree */
...
 107:	        void *d_fsdata;                 /* fs-specific data */
...
 113:	        struct list_head d_child;       /* child of parent list */
 114:	        struct list_head d_subdirs;     /* our children */
...
 123:	} __randomize_layout;

 290:	static inline unsigned d_count(const struct dentry *dentry)
 291:	{
 292:	        return dentry->d_lockref.count;
 293:	}

  34:	 #define HASH_LEN_DECLARE u32 hash; u32 len

  48:	struct qstr {
  49:	        union {
  50:	                struct {
  51:	                        HASH_LEN_DECLARE;
  52:	                };
  53:	                u64 hash_len;
  54:	        };
  55:	        const unsigned char *name;
  56:	};

  85:	#  define DNAME_INLINE_LEN 40 /* 128 bytes */
dentry は、 スラブアロケータ (kmem_cache_create(),kmem_cache_alloc(),kmem_cache_free())で管理されて いる。

◆dentryの状態

◆struct inode

struct inode は、全てのファイル・システムで共通の要素を保持する、メモリ 中の構造体。各ファイル・システムは、これに含まれるようなデータをそれぞ れ独自の方法でディスクに保存する。
linux-5.15.12/include/linux/fs.h
 623:	struct inode {
 624:	        umode_t                 i_mode;
 625:	        unsigned short          i_opflags;
 626:	        kuid_t                  i_uid;
 627:	        kgid_t                  i_gid;
...
 635:	        const struct inode_operations   *i_op;
 636:	        struct super_block      *i_sb;
...
 644:	        unsigned long           i_ino;
...
 656:	        dev_t                   i_rdev;
 657:	        loff_t                  i_size;
 658:	        struct timespec64       i_atime;
 659:	        struct timespec64       i_mtime;
 660:	        struct timespec64       i_ctime;
 661:	        spinlock_t              i_lock; /* i_blocks, i_bytes, maybe i_size */
 662:	        unsigned short          i_bytes;
 663:	        u8                      i_blkbits;
 664:	        u8                      i_write_hint;
 665:	        blkcnt_t                i_blocks;
...
 678:	        struct hlist_node       i_hash;
...
 692:	                struct hlist_head       i_dentry;
...
 697:	        atomic_t                i_count;
...
 732:	        void                    *i_private; /* fs or device private pointer */
 733:	} __randomize_layout;

◆struct inode_operations

inodeの操作は、たとえば次のような形で行われる。
    struct inode *inode;
    ...
    inode->i_op->create(inode, name, mode, true);
i_op には、次のような手続きがある。各ファイルシステム (ext4,nfs,tmpfs,...) ごとに、手続きの実体は異なるが、インタフェースは同じ。
2113:	struct inode_operations {
2114:	        struct dentry * (*lookup) (struct inode *,struct dentry *, unsigned int);
2115:	        const char * (*get_link) (struct dentry *, struct inode *, struct delayed_call *);
2116:	        int (*permission) (struct user_namespace *, struct inode *, int);
2117:	        struct posix_acl * (*get_acl)(struct inode *, int, bool);
2118:	
2119:	        int (*readlink) (struct dentry *, char __user *,int);
2120:	
2121:	        int (*create) (struct user_namespace *, struct inode *,struct dentry *,
2122:	                       umode_t, bool);
2123:	        int (*link) (struct dentry *,struct inode *,struct dentry *);
2124:	        int (*unlink) (struct inode *,struct dentry *);
2125:	        int (*symlink) (struct user_namespace *, struct inode *,struct dentry *,
2126:	                        const char *);
2127:	        int (*mkdir) (struct user_namespace *, struct inode *,struct dentry *,
2128:	                      umode_t);
2129:	        int (*rmdir) (struct inode *,struct dentry *);
2130:	        int (*mknod) (struct user_namespace *, struct inode *,struct dentry *,
2131:	                      umode_t,dev_t);
2132:	        int (*rename) (struct user_namespace *, struct inode *, struct dentry *,
2133:	                        struct inode *, struct dentry *, unsigned int);
2134:	        int (*setattr) (struct user_namespace *, struct dentry *,
2135:	                        struct iattr *);
2136:	        int (*getattr) (struct user_namespace *, const struct path *,
2137:	                        struct kstat *, u32, unsigned int);
2138:	        ssize_t (*listxattr) (struct dentry *, char *, size_t);
2139:	        int (*fiemap)(struct inode *, struct fiemap_extent_info *, u64 start,
2140:	                      u64 len);
2141:	        int (*update_time)(struct inode *, struct timespec64 *, int);
2142:	        int (*atomic_open)(struct inode *, struct dentry *,
2143:	                           struct file *, unsigned open_flag,
2144:	                           umode_t create_mode);
2145:	        int (*tmpfile) (struct user_namespace *, struct inode *,
2146:	                        struct dentry *, umode_t);
2147:	        int (*set_acl)(struct user_namespace *, struct inode *,
2148:	                       struct posix_acl *, int);
2149:	        int (*fileattr_set)(struct user_namespace *mnt_userns,
2150:	                            struct dentry *dentry, struct fileattr *fa);
2151:	        int (*fileattr_get)(struct dentry *dentry, struct fileattr *fa);
2152:	} ____cacheline_aligned;

◆struct super_block

スーパーブロックは、ファイルシステムの起点となるデータ構造。そのファイ ル・システムに含まれている inode や dentry を管理する。
linux-5.15.12/include/linux/fs.h
1465:	struct super_block {
...
1470:	        loff_t                  s_maxbytes;     /* Max file size */
...
1472:	        const struct super_operations   *s_op;
...
1479:	        struct dentry           *s_root;
...
1514:	        void                    *s_fs_info;     /* Filesystem private info */
...
1584:	        struct list_lru         s_dentry_lru;
1585:	        struct list_lru         s_inode_lru;
...
1598:	        struct list_head        s_inodes;       /* all inodes */
1602:	} __randomize_layout;

◆task_struct と struct file

struct task_struct から指される。ファイル記述子fdは、struct task_struct *p; の時、p->files->fdt->fd[fd] の struct file を表 す。開いているファイルの数が小さい時は、 p->files->fd_array[fd]と同じ。 多くのファイルを開くプロセスでは、 p->files->fd_array[NR_OPEN_DEFAULT]で足りなくなった時は、 expand_files(), expand_fdtable() で拡張する。 これらの関数では、kmalloc() 等でメモリを割り当てる。
linux-5.15.12/include/linux/sched.h
 723:	struct task_struct {
...
1070:	        struct files_struct             *files;
...
1506:	};

linux-5.15.12/include/linux/fdtable.h
  24:	#define NR_OPEN_DEFAULT BITS_PER_LONG

  49:	struct files_struct {
...
  57:	        struct fdtable __rcu *fdt;
  58:	        struct fdtable fdtab;
...
  67:	        struct file __rcu * fd_array[NR_OPEN_DEFAULT];
  68:	};

  27:	struct fdtable {
...
  29:	        struct file __rcu **fd;      /* current fd array */
...
  34:	};

linux-5.15.12/include/asm-generic/bitsperlong.h
   8:	#ifdef CONFIG_64BIT
   9:	#define BITS_PER_LONG 64
  10:	#else
  11:	#define BITS_PER_LONG 32
  12:	#endif /* CONFIG_64BIT */

図? p->files->fd_array[fd]
図? task_struct、ファイル記述子、file構造体、その他

■read() システムコールと Ext4 ファイルシステム

read() システム・コールの実装。

◆read() システム・コール

linux-5.15.12/fs/read_write.c
 631:	SYSCALL_DEFINE3(read, unsigned int, fd, char __user *, buf, size_t, count)
 632:	{
 633:	        return ksys_read(fd, buf, count);
 634:	}

 612:	ssize_t ksys_read(unsigned int fd, char __user *buf, size_t count)
 613:	{
 614:	        struct fd f = fdget_pos(fd);
 615:	        ssize_t ret = -EBADF;
 616:	
 617:	        if (f.file) {
 618:	                loff_t pos, *ppos = file_ppos(f.file);
 619:	                if (ppos) {
 620:	                        pos = *ppos;
 621:	                        ppos = &pos;
 622:	                }
 623:	                ret = vfs_read(f.file, buf, count, ppos);
 624:	                if (ret >= 0 && ppos)
 625:	                        f.file->f_pos = pos;
 626:	                fdput_pos(f);
 627:	        }
 628:	        return ret;
 629:	}

linux-5.15.12/include/linux/file.h
  36:	struct fd {
  37:	        struct file *file;
  38:	        unsigned int flags;
  39:	};

◆vfs_read()

 465:	ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
 466:	{
 467:	        ssize_t ret;
 468:	
 469:	        if (!(file->f_mode & FMODE_READ))
 470:	                return -EBADF;
 471:	        if (!(file->f_mode & FMODE_CAN_READ))
 472:	                return -EINVAL;
 473:	        if (unlikely(!access_ok(buf, count)))
 474:	                return -EFAULT;
 475:	
 476:	        ret = rw_verify_area(READ, file, pos, count);
 477:	        if (ret)
 478:	                return ret;
 479:	        if (count > MAX_RW_COUNT)
 480:	                count =  MAX_RW_COUNT;
 481:	
 482:	        if (file->f_op->read)
 483:	                ret = file->f_op->read(file, buf, count, pos);
 484:	        else if (file->f_op->read_iter)
 485:	                ret = new_sync_read(file, buf, count, pos);
 486:	        else
 487:	                ret = -EINVAL;
 488:	        if (ret > 0) {
 489:	                fsnotify_access(file);
 490:	                add_rchar(current, ret);
 491:	        }
 492:	        inc_syscr(current);
 493:	        return ret;
 494:	}

 393:	static ssize_t new_sync_read(struct file *filp, char __user *buf, size_t len, loff_t *ppos)
 394:	{
 395:	        struct iovec iov = { .iov_base = buf, .iov_len = len };
...
 401:	        kiocb.ki_pos = (ppos ? *ppos : 0);
 402:	        iov_iter_init(&iter, READ, &iov, 1, len);
 403:	
 404:	        ret = call_read_iter(filp, &kiocb, &iter);
...
 406:	        if (ppos)
 407:	                *ppos = kiocb.ki_pos;
 408:	        return ret;
 409:	}

linux-5.15.12/include/linux/fs.h
2154:	static inline ssize_t call_read_iter(struct file *file, struct kiocb *kio,
2155:	                                     struct iov_iter *iter)
2156:	{
2157:	        return file->f_op->read_iter(kio, iter);
2158:	}
vfs_read() は、次のように最終的には file->f_op->read() か file->f_op->read_iter() を呼び出す。

◆Ext4 の file_operations

linux-5.15.12/fs/ext4/file.c
 914:	const struct file_operations ext4_file_operations = {
 915:	        .llseek         = ext4_llseek,
 916:	        .read_iter      = ext4_file_read_iter,
 917:	        .write_iter     = ext4_file_write_iter,
 918:	        .iopoll         = iomap_dio_iopoll,
 919:	        .unlocked_ioctl = ext4_ioctl,
 920:	#ifdef CONFIG_COMPAT
 921:	        .compat_ioctl   = ext4_compat_ioctl,
 922:	#endif
 923:	        .mmap           = ext4_file_mmap,
 924:	        .mmap_supported_flags = MAP_SYNC,
 925:	        .open           = ext4_file_open,
 926:	        .release        = ext4_release_file,
 927:	        .fsync          = ext4_sync_file,
 928:	        .get_unmapped_area = thp_get_unmapped_area,
 929:	        .splice_read    = generic_file_splice_read,
 930:	        .splice_write   = iter_file_splice_write,
 931:	        .fallocate      = ext4_fallocate,
 932:	};

 934:	const struct inode_operations ext4_file_inode_operations = {
 935:	        .setattr        = ext4_setattr,
 936:	        .getattr        = ext4_file_getattr,
...
 943:	};

linux-5.15.12/fs/ext4/super.c
1630:	static const struct super_operations ext4_sops = {
1631:	        .alloc_inode    = ext4_alloc_inode,
...
1650:	};

◆Ext4 の inode

Ext4 では、メモリ中のデータを構造体 struct ext4_inode_info で表す。
linux-5.15.12/fs/ext4/ext4.h
1009:	struct ext4_inode_info {
...
1096:	        struct inode vfs_inode;
...
1171:	};

1760:	static inline struct ext4_inode_info *EXT4_I(struct inode *inode)
1761:	{
1762:	        return container_of(inode, struct ext4_inode_info, vfs_inode);
1763:	}

linux-5.15.12/include/linux/kernel.h
 493:	#define container_of(ptr, type, member) ({                              \
 494:	        void *__mptr = (void *)(ptr);                                   \
...
 498:	        ((type *)(__mptr - offsetof(type, member))); })

図? struct ext4_inode_info、struct inode、container_of()
図? Ext4 ファイルシステムで使う構造体 ext4_inode_info での struct inode の保持

◆Ext4のext4_file_read_iter()

linux-5.15.12/fs/ext4/file.c
 113:	static ssize_t ext4_file_read_iter(struct kiocb *iocb, struct iov_iter *to)
 114:	{
...
 130:	        return generic_file_read_iter(iocb, to);
 131:	}

◆汎用のgeneric_file_read_iter()

linux-5.15.12/mm/filemap.c
2726:	ssize_t
2727:	generic_file_read_iter(struct kiocb *iocb, struct iov_iter *iter)
2728:	{
...
2778:	        return filemap_read(iocb, iter, retval);
2779:	}

◆汎用のfilemap_read()

linux-5.15.12/include/linux/pagevec.h
  15:	#define PAGEVEC_SIZE    15
...
  20:	struct pagevec {
  21:	        unsigned char nr;
  22:	        bool percpu_pvec_drained;
  23:	        struct page *pages[PAGEVEC_SIZE];
  24:	};


linux-5.15.12/mm/filemap.c
2596:	ssize_t filemap_read(struct kiocb *iocb, struct iov_iter *iter,
2597:	                ssize_t already_read)
2598:	{
2599:	        struct file *filp = iocb->ki_filp;
2600:	        struct file_ra_state *ra = &filp->f_ra;
2601:	        struct address_space *mapping = filp->f_mapping;
2602:	        struct inode *inode = mapping->host;
2603:	        struct pagevec pvec;
2604:	        int i, error = 0;
2605:	        bool writably_mapped;
2606:	        loff_t isize, end_offset;
...
2614:	        pagevec_init(&pvec);
2615:	
2616:	        do {
...
2627:	                error = filemap_get_pages(iocb, iter, &pvec);
...
2658:	                for (i = 0; i < pagevec_count(&pvec); i++) {
2659:	                        struct page *page = pvec.pages[i];
...
2682:	                        copied = copy_page_to_iter(page, offset, bytes, iter);
...
2683:	
2684:	                        already_read += copied;
2685:	                        iocb->ki_pos += copied;
2686:	                        ra->prev_pos = iocb->ki_pos;
...
2692:	                }
...
2696:	                pagevec_reinit(&pvec);
2697:	        } while (iov_iter_count(iter) && iocb->ki_pos < isize && !error);
...
2701:	        return already_read ? already_read : error;
2702:	}

■mkdir() システムコール

mkdir() システム・コールの実装。

◆mkdir() システム・コール

linux-5.15.12/fs/namei.c
3929:	SYSCALL_DEFINE2(mkdir, const char __user *, pathname, umode_t, mode)
3930:	{
3931:	        return do_mkdirat(AT_FDCWD, getname(pathname), mode);
3932:	}

3892:	int do_mkdirat(int dfd, struct filename *name, umode_t mode)
3893:	{
3894:	        struct dentry *dentry;
3895:	        struct path path;
3896:	        int error;
3897:	        unsigned int lookup_flags = LOOKUP_DIRECTORY;
3898:	
3899:	retry:
3900:	        dentry = filename_create(dfd, name, &path, lookup_flags);
...
3911:	                error = vfs_mkdir(mnt_userns, path.dentry->d_inode, dentry,
3912:	                                  mode);
...
3921:	        return error;
3922:	}

◆vfs_mkdir()

linux-5.15.12/fs/namei.c
3865:	int vfs_mkdir(struct user_namespace *mnt_userns, struct inode *dir,
3866:	              struct dentry *dentry, umode_t mode)
3867:	{
...
3874:	        if (!dir->i_op->mkdir)
3875:	                return -EPERM;
...
3885:	        error = dir->i_op->mkdir(mnt_userns, dir, dentry, mode);
...
3888:	        return error;
3889:	}

■授業評価アンケート

情報科学類では、教育の改善のために、学生の皆さんに授業評価アンケートを 実施していますので、ご協力をお願いします。

アンケートはTwinsから回答してください。

なお、皆さんの評価が成績に影響することは一切ありません。 また、評価結果を教育の改善以外の目的に利用することはありませんし、 評価結果を公開する場合には個人を特定できるような情報は含めません。

2月27日までに回答して下さい。

■課題5 割り込みの後半部、ファイルシステム

★問題(501) Tasklet

Tasklet を使って次の2引数の関数 f() を、割り込み処理の後半で呼び出したい。
void f(int arg1, int arg2) {
   省略;
}
これを実現するために、どのような Tasklet のハンドラと初期化コードを書け ばよいか。以下の空欄を埋めなさい。

static struct tasklet_struct tl1;

void tasklet_handler(unsigned long data) { /* Tasklet ハンドラ */
    int arg1, arg2;
    arg1 = 省略;
    arg2 = 省略;
    /*空欄(a)*/
}

初期化
{
	/*空欄(b)*/(&tl1, /*空欄(c)*/);
}
次のコードは、割り込みの前半部分(ハードウェアの割り込み)の一部である。 上で定義した定義した Tasklet のハンドラを呼ぶように、空欄を埋めなさい。
irqreturn_t irq_handler(int irq, void *dev) {
    /*空欄(d)*/
    return IRQ_HANDLED;
}

★問題(502) オブジェクト指向

Linux の VFS の実装では、C言語であるが、オブジェクト指向的な考え方が取 り入れられ、手続きのデータをカプセル化している。VFS における重要なオブ ジェクトを1つ選びなさい。そのオブジェクトについて、次の項目を答えなさ い。

★問題(503) 期末試験

期末試験について、次の事柄を答えなさい。
Last updated: 2022/01/31 22:13:56
Yasushi Shinjo / <yas@cs.tsukuba.ac.jp>