/* Sigh -really, this is an OSS, the _server_, not the _target_ */
static intost_setup(struct obd_device *obd, obd_count len, void *buf)
{ ... }
from Lustre source tree b16
如果我們正確地理解了上述注釋,Lustre源碼樹lustre/ost和所有的以ost_開頭的函數名可能都應該作為服務器(OSS)函數。
6.1OSS 和OST
OST以內核模塊的形式加載。它和obdfilter緊密合作,完成了服務器/OST端的大部分工作。在這兩層中,OSS是交換層(switch layer),或者薄層(thin layer),它解釋從Portal RPC來的請求,為請求做準備,然后將請求傳遞到obdfilter做進一步處理。在接下來的討論中,我們集中討論它的兩個方面:初始建立和交換結構,它們分別由ost_setup()和ost_handle()完成。
初始建立
- 首先,OST檢查OSS的線程數是否確定了。如果沒有,則根據CPU和內存計算最小線程數,確保最大和最小線程數之間有四倍的動態范圍。
oss_min_threads= num_possible_cpus() * num_physpages >> (27 - CFS_PAGE_SHIFT);
if(oss_min_threads < OSS_THREADS_MIN)
oss_min_threads = OSS_THREADS_MIN;
/* Insure a 4xrange for dynamic threads */
if(oss_min_threads > OSS_THREADS_MAX / 4)
oss_min_threads = OSS_THREADS_MAX / 4;
oss_max_threads= min(OSS_THREADS_MAX, oss_min_threads * 4 + 1);
為了得到OST的obd設備,使用了如下的函數調用:
struct ost_obd*ost = &obd->u.ost;
- 然后服務器端初始化RPC服務,如下:
ost->ost_service= ptlrpc_init_svc( , , , , , , ost_handle, , , , "ll_ost");
這個函數返回指向結構體ptlrpc_service的指針。這里需要指出的一個重要的事情是,我們已經提供了一個處理函數ost_handle。一旦像下面所示的一樣,服務啟動了,Portal RPC將向這個處理函數派遣(dispatch)請求來做進一步處理。這是下一節中的內容。
- prtrpc線程開始,如下:
rc =ptlrpc_start_threads(obd, ost->ost_service);
重復執行類似的調用序列,創建ost create線程,而服務處理函數設置為ost->ost_create_service。這個流程還為創建ost io線程而重復執行,而服務處理函數設置為ost->ost_io_service。
最后,ping驅逐(eviction?)服務開始了。
派遣(dispatching)
處理函數使用一個輸入參數struct ptlrpc_request *req,而它大部分由請求的類型驅動。解碼請求的類型是通過將req->rq_reqmsg(它指向結構體lustre_msg)傳遞到一個由Portal RPC提供的幫助函數lustre_msg_get_opc()來完成的。所以派遣的結構類似于:
swtich(lsutre_msg_get_opc(req->rq_reqmsg)) {
case OST_CONNECT:
...
rc = target_handle_connect(req, ost_handle);
break;
case OST_CREATE:
...
rc = ost_create(req->rq_export, req, oti);
break;
case OST_WRITE:
...
rc = ost_brw_write(req, oti);
RETURN (rc);
case OST_READ:
...
rc = ost_brw_read(req, oti);
RETURN(rc);
}
意外處理包括可能出現的恢復,這種恢復可能在除了OST_CONNECT之外的任何請求中出現。另外,我們需要通過檢查req->rq_export是否為NULL來檢查連接是否從未知客戶端而來。
6.2 OSS 目錄布局
這節介紹當登錄到一個OST節點上時,你將在磁盤中觀察到那些東西。現在為止,磁盤中的文件系統最有可能是ldiskfs。這意味著后端數據實際上以普通文件的方式存儲,以一種Lustre特有的方法組織著:
組號
在OST的頂層目錄下是以各組命名的子目錄。這種布局容許了集群MDS的存在,而每個組對應一個MDS。就目前來說,只使用一個MDS,所以只有第零組是有效的。
對象ID
在每個組里,創建了32個子目錄。對每個文件對象,它的最后五位用來表明這個文件應該放置在哪個子目錄中。這里,文件名是對象ID。
6.3 obdfilter
obdfilter設備是在OST服務初始化的時候創建的。對每個OST我們有一個相對應的obdfilter設備。對每個客戶端連接,obdfilter創建一個輸出口(export)作為輸出的管道。所有的輸出口都維護在一個全局哈希表中,哈希關鍵字稱為UUID,在Figure 10和11中都給出了。Portal RPC層使用UUID來快速確定到來的請求應當送去哪個輸出口(和obdfilter設備)。另外,每個obdfilter設備維護一個它服務的輸出口鏈表。這種關系在Figure 10中畫出來了。
obdfilter提供了如下函數:
- 處理創建請求,該請求可能是來自于MDS的對文件數據對象的請求。
- 處理讀取和限額如請求,該請求來自于OSC客戶端。
- 處理連接和斷開連接請求,該請求是來自于低層Portal RPC層的,對已建立好的輸出口和輸入口的請求。
- 處理銷毀請求(牽涉到客戶端和MDS兩方面)。
6.3.1文件刪除
銷毀協議如下所述。首先,客戶端決定刪除一個文件,而這個請求傳送到了MDS。MDS檢查了EA分條,并用llog產生了一個事務日志。這個日志包含如下:<從OST1中unlink對象1,從OST2中unlink對象2,等等>。然后MDS將布局和事務日志傳輸到客戶端。客戶端得到這個日志,并與每個OST(實際上是obdfilter)聯系,刪除(unlink)每個文件對象。一旦所有在MDS上的的刪除(unlink)llog記錄都已經被告知,那么文件刪除過程就結束了。
6.3.2文件創建
正如早先在第5節所講的,所有的請求都由OST和obdfilter一起處理。現在我們來理順處理創建請求的流程。處理請求的第一部分是在ost_create()進行了如下操作:
1. 準備回復消息的大小。這由兩個記錄組成,所以需要兩塊緩沖。第一個記錄是為portalrpc body準備的,而第二個是為ostreply body準備的。
__u32 size[2] ={ sizeof(struct ptlrpc_body), sizeof(*repbody)};
2. 從原始請求中取得一個請求體的指針,并在需要的時候進行字節交換。
struct ost_body*body = lustre_swab_reqbuf(req, REQ_REC_OFF,
sizeof(*body),lustre_swab_ost_body);
最后一個參數是swab處理函數,只有在需要交換的時候才調用它。客戶端的請求使用本地字節序,里面包含一個預先商量好的魔數。服務器端讀取這個魔數,并用以確定是否需要進行交換。
3. 進行實際的空間分配,并填入初步的頭信息。
rc =lustre_pack_replay(req, 2, size, NULL);
repbody =lustre_msg_buf(req->rq_repmsg, REPLY_REC_OFF,sizeof(*repbody));
在第一個調用之后,req->rq_repmsg()指向新分配的空間。第二個調用為回復體緩沖設置了開始地址的repbody。
4. 最后,它用與請求體完全一樣內容填充回復體,然后傳給obdfilter做進一步處理。
memcpy(&repbody->oa,&body->oa, sizeof(body->oa));
req-rq_status =obd_create(exp, &repbody->oa, NULL, oti);
對于創建請求,obdfilter的入口點是通過filter_create():
static intfilter_create(struct obd_export *exp, struct obdo *oa ..).
我們忽略了牽涉到struct lov_stripe_md **ea和struct obd_trans_info *oti的處理過程,這是因為前者是遺留代碼,將來不大可能使用。
1. 首先,保存當前上下文,并指定客戶端的上下文為它自己的操作上下文。這是為讓線程在想要訪問后端文件系統時,為其確定必須的信息。這就像一個sandbox(?),限制在處理客戶端請求時,服務線程的reach(?)。它為服務線程存儲“文件系統根”和“當前工作目錄”(當然,不是從客戶端取得,而是取決于我們正在哪個個OST上工作)。
obd =exp->exp_obd;
put_ctxt(&saved,&obd->obd_lvfs_ctxt, NULL);
2. 如果請求的目的是重新創建一個對象,那么我們取消所有加在重建對象上的extent鎖,取消方法是要求所有加在對象上的鎖調用filter_recreate()來做實際的工作。否則,我們按照正常的重建對象的流程執行。重建的原因是,從概念上講,當MDS指示OST創建一個對象,OST并不單單創建一個對象,而是創建多個已指定了對象ID的對象。創建的這一組對象的磁盤大小是零。這樣做的目的是,當下一次MDS回復客戶端創建新文件的請求時,它不用再向OST發送請求,就能為客戶端呈現布局信息。通過查看每個OST中預先創建的對象池,MDS就可能已經擁有所有用來回復客戶端所需的信息。
if(oa->o_valid & OBD_MD_FLFLAGS) &&
(oa->o_flags & OBD_FL_RECREATE_OBJS)){
rc =ldlm_cli_enqueue_local(obd->obd_namespace, &res_id, ... );
rc = filter_recreate(obd, oa);
ldlm_lock_deref(&lockh, LCK_PW);
} else {
rc = filter_handle_precreate(exp, oa,oa->o_gr, oti);
}
這里,從預先創建處理函數中返回的rc要么是一個負數,表明錯誤,要么是一個非負數,表明創建的文件數目。
3. 現在,我們更近一點看看預先創建函數:
當客戶端通過一個預先創建對象ID與一個OST聯系時,OST知道這個對象ID現在被激活了。但是這里出現一個問題:如果MDS掛了,它關于預先創建對象的信息就過時了。為了解決這個沖突,當MDS重啟時,它檢查未使用的預先創建對象的記錄,向OST發送請求,刪除那些對象(刪除孤兒)。obdfilter接收這些請求,跳過那些實際上已經被使用(但是沒有和MDS自己的記錄同步)的對象,將剩下的對象刪除。這就是filter_handle_precreate()在第一部分中所需要做的:
if((oa->o_valid & OBD_MD_FLFLAGS) &&
(oa->o_flags & OBD_FL_DELORPHAN)) {
down(&filter->fo_create_lock);
rc = filter_destroy_precreated(exp,oa,filter);
...
} else {
rc = filter_precreate(obd, oa, group, &diff);
...
}
4. 最終,創建請求傳遞到fsfilt,由VFS調用完成。這個進程稍后將經過更多的步驟,例如取得父親索引節點,事務創建等等。
rc =ll_vfs_create(dparent->d_inode, dchild,
S_IFREG | S_ISUID | S_ISGID | 0666, NULL);
本文章歡迎轉載,請保留原始博客鏈接http://blog.csdn.net/fsdev/article
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
