ORANGEFS

OrangeFS是一個LGPL使用者空間橫向擴充套件的並行儲存系統。它非常適合HPC,大資料,流影片,基因組學,生物資訊學面臨的大型儲存問題。

Orangefs,最初稱為PVFS,於1993年由Walt Ligon和Eric Blumer首次開發,作為並行虛擬機器(PVM)的並行檔案系統,作為NASA授予的研究並行程式I/O模式的資金的一部分。

Orangefs的特性包括

  • 在多個檔案伺服器之間分配檔案資料

  • 支援多個客戶端同時訪問

  • 使用本地檔案系統和訪問方法在伺服器上儲存檔案資料和元資料

  • 使用者空間實現易於安裝和維護

  • 直接MPI支援

  • 無狀態

郵件列表存檔

http://lists.orangefs.org/pipermail/devel_lists.orangefs.org/

郵件列表提交

devel@lists.orangefs.org

文件

http://www.orangefs.org/documentation/

在單伺服器上執行ORANGEFS

OrangeFS通常在具有多個伺服器和客戶端的大型安裝中使用,但是可以在單個機器上執行完整的檔案系統以進行開發和測試。

在Fedora上,安裝orangefs和orangefs-server

dnf -y install orangefs orangefs-server

在/etc/orangefs/orangefs.conf中有一個示例伺服器配置檔案。如有必要,將localhost更改為您的主機名。

要生成一個檔案系統來執行xfstests,請參見下文。

在/etc/pvfs2tab中有一個示例客戶端配置檔案。它是一行。取消註釋並在必要時更改主機名。這控制使用libpvfs2的客戶端。這不控制pvfs2-client-core。

建立檔案系統

pvfs2-server -f /etc/orangefs/orangefs.conf

啟動伺服器

systemctl start orangefs-server

測試伺服器

pvfs2-ping -m /pvfsmnt

啟動客戶端。必須在此之前編譯或載入該模組

systemctl start orangefs-client

掛載檔案系統

mount -t pvfs2 tcp://:3334/orangefs /pvfsmnt

使用者空間檔案系統原始碼

http://www.orangefs.org/download

2.9.3之前的Orangefs版本與上游版本的核心客戶端不相容。

在單伺服器上構建ORANGEFS

如果無法從發行包安裝OrangeFS,則可以從原始碼構建它。

如果您不關心事物分散在/usr/local中,則可以省略--prefix。 從2.9.6版開始,OrangeFS預設使用Berkeley DB,我們可能會很快將預設值更改為LMDB。

./configure --prefix=/opt/ofs --with-db-backend=lmdb --disable-usrint

make

make install

透過執行pvfs2-genconfig並指定目標配置檔案來建立一個orangefs配置檔案。 Pvfs2-genconfig將提示您完成。 通常,採用預設值是可以的,但是當出現問題時,應該使用伺服器的主機名,而不是“localhost”

/opt/ofs/bin/pvfs2-genconfig /etc/pvfs2.conf

建立一個/etc/pvfs2tab檔案(localhost是可以的)

echo tcp://:3334/orangefs /pvfsmnt pvfs2 defaults,noauto 0 0 > \
    /etc/pvfs2tab

如果需要,建立在tab檔案中指定的掛載點

mkdir /pvfsmnt

引導伺服器

/opt/ofs/sbin/pvfs2-server -f /etc/pvfs2.conf

啟動伺服器

/opt/ofs/sbin/pvfs2-server /etc/pvfs2.conf

現在伺服器應該正在執行。 Pvfs2-ls是一個簡單的測試,以驗證伺服器是否正在執行

/opt/ofs/bin/pvfs2-ls /pvfsmnt

如果一切似乎都在工作,請載入核心模組並開啟客戶端核心

/opt/ofs/sbin/pvfs2-client -p /opt/ofs/sbin/pvfs2-client-core

掛載你的檔案系統

mount -t pvfs2 tcp://`hostname`:3334/orangefs /pvfsmnt

執行xfstests

使用具有xfstests的臨時檔案系統很有用。 這可以用一個伺服器完成。

在伺服器配置檔案/etc/orangefs/orangefs.conf中建立FileSystem部分的第二個副本。 將名稱更改為scratch。 將ID更改為與第一個FileSystem部分的ID不同的值(通常2是一個不錯的選擇)。

然後有兩個FileSystem部分:orangefs和scratch。

應在建立檔案系統之前進行此更改。

pvfs2-server -f /etc/orangefs/orangefs.conf

要執行xfstests,請建立/etc/xfsqa.config

TEST_DIR=/orangefs
TEST_DEV=tcp://:3334/orangefs
SCRATCH_MNT=/scratch
SCRATCH_DEV=tcp://:3334/scratch

然後可以執行xfstests

./check -pvfs2

選項

接受以下掛載選項

acl

允許在檔案和目錄上使用訪問控制列表。

intr

核心客戶端和使用者空間檔案系統之間的一些操作可能是可中斷的,例如除錯級別的更改和可調引數的設定。

local_lock

從“此”核心的角度啟用posix鎖定。 預設的file_operations鎖定操作是返回ENOSYS。 如果檔案系統掛載了-o local_lock,則會啟動Posix鎖定。 分散式鎖定正在為將來努力。

除錯

如果您想要特定原始檔(例如inode.c)中的除錯(GOSSIP)語句轉到syslog

echo inode > /sys/kernel/debug/orangefs/kernel-debug

無除錯(預設)

echo none > /sys/kernel/debug/orangefs/kernel-debug

來自多個原始檔的除錯

echo inode,dir > /sys/kernel/debug/orangefs/kernel-debug

所有除錯

echo all > /sys/kernel/debug/orangefs/kernel-debug

獲取所有除錯關鍵字的列表

cat /sys/kernel/debug/orangefs/debug-help

核心模組和使用者空間之間的協議

Orangefs是一個使用者空間檔案系統和一個關聯的核心模組。 從現在開始,我們將僅將Orangefs的使用者空間部分稱為“使用者空間”。 Orangefs來自PVFS,使用者空間程式碼仍然使用PVFS作為函式和變數名稱。 使用者空間typedef許多重要結構。 核心模組中的函式和變數名稱已轉換為“orangefs”,並且Linux編碼風格避免使用typedef,因此與使用者空間結構相對應的核心模組結構未進行typedef。

核心模組實現了一個偽裝置,使用者空間可以從中讀取和寫入。 使用者空間還可以透過ioctl操作偽裝置來操縱核心模組。

Bufmap

在啟動時,使用者空間分配兩個頁面大小對齊(posix_memalign)的mlocked記憶體緩衝區,一個用於IO,一個用於readdir操作。 IO緩衝區為41943040位元組,readdir緩衝區為4194304位元組。 每個緩衝區都包含邏輯塊或分割槽,並將指向每個緩衝區的指標新增到其自己的PVFS_dev_map_desc結構中,該結構還描述了其總大小,以及分割槽的大小和數量。

透過ioctl將指向IO緩衝區的PVFS_dev_map_desc結構的指標傳送到核心模組中的對映例程。 該結構使用copy_from_user從使用者空間複製到核心空間,並用於初始化核心模組的“bufmap”(struct orangefs_bufmap),然後其中包含

  • refcnt - 一個引用計數器

  • desc_size - PVFS2_BUFMAP_DEFAULT_DESC_SIZE(4194304) - IO緩衝區的分割槽大小,表示檔案系統的塊大小,用於超級塊中的s_blocksize。

  • desc_count - PVFS2_BUFMAP_DEFAULT_DESC_COUNT(10) - IO緩衝區中分割槽的數量。

  • desc_shift - log2(desc_size),用於超級塊中的s_blocksize_bits。

  • total_size - IO緩衝區的總大小。

  • page_count - IO緩衝區中4096位元組頁面的數量。

  • page_array - 指向 page_count * (sizeof(struct page*)) 位元組kcalloced記憶體的指標。 透過呼叫get_user_pages,此記憶體用作指向IO緩衝區中每個頁面的指標陣列。

  • desc_array - 指向 desc_count * (sizeof(struct orangefs_bufmap_desc)) 位元組kcalloced記憶體的指標。 此記憶體進一步初始化

    user_desc是核心的IO緩衝區ORANGEFS_dev_map_desc結構的副本。 user_desc->ptr指向IO緩衝區。

    pages_per_desc = bufmap->desc_size / PAGE_SIZE
    offset = 0
    
    bufmap->desc_array[0].page_array = &bufmap->page_array[offset]
    bufmap->desc_array[0].array_count = pages_per_desc = 1024
    bufmap->desc_array[0].uaddr = (user_desc->ptr) + (0 * 1024 * 4096)
    offset += 1024
                       .
                       .
                       .
    bufmap->desc_array[9].page_array = &bufmap->page_array[offset]
    bufmap->desc_array[9].array_count = pages_per_desc = 1024
    bufmap->desc_array[9].uaddr = (user_desc->ptr) +
                                           (9 * 1024 * 4096)
    offset += 1024
    
  • buffer_index_array - 一個desc_count大小的整數陣列,用於指示IO緩衝區的哪些分割槽可用。

  • buffer_index_lock - 一個自旋鎖,用於在更新期間保護buffer_index_array。

  • readdir_index_array - 一個五個(ORANGEFS_READDIR_DEFAULT_DESC_COUNT)元素整數陣列,用於指示readdir緩衝區的哪些分割槽可用。

  • readdir_index_lock - 一個自旋鎖,用於在更新期間保護readdir_index_array。

操作

當核心模組需要與使用者空間通訊時,它會構建一個“op”(struct orangefs_kernel_op_s)。 op的一部分包含“upcall”,它表示對使用者空間的請求。 op的一部分最終包含“downcall”,它表示請求的結果。

slab分配器用於保持op結構快取方便。

在初始化時,核心模組定義並初始化一個請求列表和一個in_progress雜湊表,以跟蹤在任何給定時間正在進行的所有操作。

操作是有狀態的

  • 未知
    • 操作剛剛初始化

  • 等待
    • 操作位於request_list上(向上繫結)

  • 進行中
    • 操作正在進行中(等待downcall)

  • 已服務
    • 操作具有匹配的downcall; 好的

  • 已清除
    • 操作必須啟動一個計時器,因為客戶端核心在服務操作之前不乾淨地退出

  • 放棄
    • 提交者已放棄等待它

當一些任意使用者空間程式需要在Orangefs上執行檔案系統操作(readdir,I/O,create等)時,初始化一個op結構並標記一個區分ID號。 填寫op的upcall部分,並將op傳遞給“service_operation”函式。

Service_operation將op的狀態更改為“waiting”,將其放在請求列表中,並透過等待佇列向Orangefs file_operations.poll函式發出訊號。 使用者空間正在輪詢偽裝置,因此意識到需要讀取的upcall請求。

當觸發Orangefs file_operations.read函式時,會在請求列表中搜索似乎已準備好處理的操作。 從請求列表中刪除該操作。 op中的標記和已填寫的upcall結構透過copy_to_user複製回用戶空間。

如果任何這些(以及一些附加協議)copy_to_users失敗,則將op的狀態設定為“waiting”,並將op添加回請求列表。 否則,將op的狀態更改為“in progress”,並將其雜湊到其標記上,並將其放在in_progress雜湊表中標記雜湊到的索引處的列表末尾。

當用戶空間組裝好對upcall的響應時,它會將響應(包括區分標記)以一系列io_vecs的形式寫回到偽裝置。 這會觸發Orangefs file_operations.write_iter函式來查詢具有關聯標記的操作並將其從in_progress雜湊表中刪除。 只要op的狀態不是“canceled”或“given up”,其狀態就會設定為“serviced”。 file_operations.write_iter函式返回到等待的vfs,並透過wait_for_matching_downcall返回到service_operation。

Service operation返回其呼叫方,並填寫op的downcall部分(對upcall的響應)。

“client-core”是核心模組和使用者空間之間的橋樑。 client-core是一個守護程式。 client-core具有關聯的監視程式守護程式。 如果client-core被訊號通知死亡,則監視程式守護程式會重新啟動client-core。 即使client-core“立即”重新啟動,在此類事件期間,client-core也會死亡一段時間。 無法透過Orangefs file_operations.poll函式觸發死亡的client-core。 在“死亡時間”期間透過service_operation的操作可能會在等待佇列上超時,並且會嘗試回收它們。 顯然,如果client-core死亡時間太長,則嘗試使用Orangefs的任意使用者空間程序將受到不利影響。 無法服務的等待操作將從請求列表中刪除,並將其狀態設定為“given up”。 無法服務的進行中操作將從in_progress雜湊表中刪除,並將其狀態設定為“given up”。

Readdir和I/O操作在其有效負載方面是非典型的。

  • readdir操作使用兩個預先分配的預先分割槽記憶體緩衝區中較小的一個。 readdir緩衝區僅適用於使用者空間。 核心模組在啟動readdir操作之前獲取空閒分割槽的索引。 使用者空間將結果存入索引分割槽中,然後將它們寫回pvfs裝置。

  • io(讀取和寫入)操作使用兩個預先分配的預先分割槽記憶體緩衝區中較大的一個。 IO緩衝區可從使用者空間和核心模組訪問。 核心模組在啟動io操作之前獲取空閒分割槽的索引。 核心模組將寫入資料存入索引分割槽中,以供使用者空間直接使用。 使用者空間將讀取請求的結果存入索引分割槽中,以供核心模組直接使用。

對核心請求的響應都封裝在pvfs2_downcall_t結構中。 除了其他一些成員外,pvfs2_downcall_t還包含一個結構聯合,每個結構都與特定的響應型別相關聯。

聯合之外的幾個成員是

int32_t 型別
  • 操作型別。

int32_t 狀態
  • 操作的返回程式碼。

int64_t trailer_size
  • 除非是readdir操作,否則為0。

char *trailer_buf
  • 初始化為NULL,在readdir操作期間使用。

對於任何特定響應,都會填寫聯合內部的相應成員。

PVFS2_VFS_OP_FILE_IO

填寫一個pvfs2_io_response_t

PVFS2_VFS_OP_LOOKUP

填寫一個PVFS_object_kref

PVFS2_VFS_OP_CREATE

填寫一個PVFS_object_kref

PVFS2_VFS_OP_SYMLINK

填寫一個PVFS_object_kref

PVFS2_VFS_OP_GETATTR

填寫一個PVFS_sys_attr_s(核心不需要的大量內容)當物件是符號連結時,用連結目標填寫一個字串。

PVFS2_VFS_OP_MKDIR

填寫一個PVFS_object_kref

PVFS2_VFS_OP_STATFS

用無用的資訊填寫一個pvfs2_statfs_response_t <g>。 我們很難及時瞭解有關我們分散式網路檔案系統的這些統計資訊。

PVFS2_VFS_OP_FS_MOUNT

填寫一個pvfs2_fs_mount_response_t,它就像一個PVFS_object_kref,只是其成員的順序不同,並且“__pad1”替換為“id”。

PVFS2_VFS_OP_GETXATTR

填寫一個pvfs2_getxattr_response_t

PVFS2_VFS_OP_LISTXATTR

填寫一個pvfs2_listxattr_response_t

PVFS2_VFS_OP_PARAM

填寫一個pvfs2_param_response_t

PVFS2_VFS_OP_PERF_COUNT

填寫一個pvfs2_perf_count_response_t

PVFS2_VFS_OP_FSKEY

檔案一個pvfs2_fs_key_response_t

PVFS2_VFS_OP_READDIR

將所有需要的內容塞入readdir緩衝區描述符中,該描述符在upcall中指定,以表示pvfs2_readdir_response_t。

使用者空間使用/dev/pvfs2-req上的writev()將響應傳遞給核心端發出的請求。

一個buffer_list包含

  • 指向核心請求的準備好的響應的指標(struct pvfs2_downcall_t)。

  • 並且,在readdir請求的情況下,指向包含目標目錄中物件描述符的緩衝區的指標。

...被髮送到執行writev的函式(PINT_dev_write_list)。

PINT_dev_write_list具有一個本地iovec陣列:struct iovec io_array[10];

io_array的前四個元素以這種方式初始化以用於所有響應

io_array[0].iov_base = address of local variable "proto_ver" (int32_t)
io_array[0].iov_len = sizeof(int32_t)

io_array[1].iov_base = address of global variable "pdev_magic" (int32_t)
io_array[1].iov_len = sizeof(int32_t)

io_array[2].iov_base = address of parameter "tag" (PVFS_id_gen_t)
io_array[2].iov_len = sizeof(int64_t)

io_array[3].iov_base = address of out_downcall member (pvfs2_downcall_t)
                       of global variable vfs_request (vfs_request_t)
io_array[3].iov_len = sizeof(pvfs2_downcall_t)

Readdir響應像這樣初始化第五個元素io_array

io_array[4].iov_base = contents of member trailer_buf (char *)
                       from out_downcall member of global variable
                       vfs_request
io_array[4].iov_len = contents of member trailer_size (PVFS_size)
                      from out_downcall member of global variable
                      vfs_request

Orangefs利用dcache來避免向用戶空間傳送冗餘請求。 我們使用orangefs_inode_getattr保持物件inode屬性最新。 Orangefs_inode_getattr使用兩個引數來幫助它決定是否更新inode:“new”和“bypass”。 Orangefs將私有資料儲存在物件的inode中,其中包含一個短超時值getattr_time,該值允許orangefs_inode_getattr的任何迭代都知道自inode更新以來已經過了多長時間。 當物件不是新的(new == 0)並且未設定bypass標誌(bypass == 0)時,如果getattr_time未超時,orangefs_inode_getattr將返回而不更新inode。 每次更新inode時,getattr_time都會更新。

建立新物件(檔案,目錄,符號連結)包括評估其路徑名,從而導致物件的否定目錄條目。 分配一個新的inode並將其與dentry關聯,從而將其從否定dentry變為“富有成效的完整社會成員”。 Orangefs使用 new_inode() 從Linux獲取新的inode,並透過 d_instantiate() 將該對傳送回Linux,將inode與dentry關聯。

評估物件的路徑名會解析為其相應的dentry。 如果沒有相應的dentry,則會在dcache中為其建立一個。 每當修改或驗證dentry時,Orangefs都會在dentry的d_time中儲存一個短超時值,並且在該時間段內將信任dentry。 Orangefs是一個網路檔案系統,物件可能會與任何特定的Orangefs核心模組例項脫節,因此信任dentry是有風險的。 信任dentry的替代方法是始終從使用者空間獲取所需的資訊-至少到客戶端核心的一次行程,可能到伺服器。 從dentry獲取資訊很便宜,從使用者空間獲取資訊相對昂貴,因此,在可能的情況下使用dentry的動機。

超時值d_time和getattr_time基於jiffy,並且程式碼旨在避免jiffy-wrap問題

"In general, if the clock may have wrapped around more than once, there
is no way to tell how much time has elapsed. However, if the times t1
and t2 are known to be fairly close, we can reliably compute the
difference in a way that takes into account the possibility that the
clock may have wrapped between times."

來自講師Andy Wang的課程筆記