/* 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_開頭的函數(shù)名可能都應(yīng)該作為服務(wù)器(OSS)函數(shù)。
6.1OSS 和OST
OST以內(nèi)核模塊的形式加載。它和obdfilter緊密合作,完成了服務(wù)器/OST端的大部分工作。在這兩層中,OSS是交換層(switch layer),或者薄層(thin layer),它解釋從Portal RPC來(lái)的請(qǐng)求,為請(qǐng)求做準(zhǔn)備,然后將請(qǐng)求傳遞到obdfilter做進(jìn)一步處理。在接下來(lái)的討論中,我們集中討論它的兩個(gè)方面:初始建立和交換結(jié)構(gòu),它們分別由ost_setup()和ost_handle()完成。
初始建立
- 首先,OST檢查OSS的線程數(shù)是否確定了。如果沒有,則根據(jù)CPU和內(nèi)存計(jì)算最小線程數(shù),確保最大和最小線程數(shù)之間有四倍的動(dòng)態(tài)范圍。
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設(shè)備,使用了如下的函數(shù)調(diào)用:
struct ost_obd*ost = &obd->u.ost;
- 然后服務(wù)器端初始化RPC服務(wù),如下:
ost->ost_service= ptlrpc_init_svc( , , , , , , ost_handle, , , , "ll_ost");
這個(gè)函數(shù)返回指向結(jié)構(gòu)體ptlrpc_service的指針。這里需要指出的一個(gè)重要的事情是,我們已經(jīng)提供了一個(gè)處理函數(shù)ost_handle。一旦像下面所示的一樣,服務(wù)啟動(dòng)了,Portal RPC將向這個(gè)處理函數(shù)派遣(dispatch)請(qǐng)求來(lái)做進(jìn)一步處理。這是下一節(jié)中的內(nèi)容。
- prtrpc線程開始,如下:
rc =ptlrpc_start_threads(obd, ost->ost_service);
重復(fù)執(zhí)行類似的調(diào)用序列,創(chuàng)建ost create線程,而服務(wù)處理函數(shù)設(shè)置為ost->ost_create_service。這個(gè)流程還為創(chuàng)建ost io線程而重復(fù)執(zhí)行,而服務(wù)處理函數(shù)設(shè)置為ost->ost_io_service。
最后,ping驅(qū)逐(eviction?)服務(wù)開始了。
派遣(dispatching)
處理函數(shù)使用一個(gè)輸入?yún)?shù)struct ptlrpc_request *req,而它大部分由請(qǐng)求的類型驅(qū)動(dòng)。解碼請(qǐng)求的類型是通過將req->rq_reqmsg(它指向結(jié)構(gòu)體lustre_msg)傳遞到一個(gè)由Portal RPC提供的幫助函數(shù)lustre_msg_get_opc()來(lái)完成的。所以派遣的結(jié)構(gòu)類似于:
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);
}
意外處理包括可能出現(xiàn)的恢復(fù),這種恢復(fù)可能在除了OST_CONNECT之外的任何請(qǐng)求中出現(xiàn)。另外,我們需要通過檢查req->rq_export是否為NULL來(lái)檢查連接是否從未知客戶端而來(lái)。
6.2 OSS 目錄布局
這節(jié)介紹當(dāng)?shù)卿浀揭粋€(gè)OST節(jié)點(diǎn)上時(shí),你將在磁盤中觀察到那些東西。現(xiàn)在為止,磁盤中的文件系統(tǒng)最有可能是ldiskfs。這意味著后端數(shù)據(jù)實(shí)際上以普通文件的方式存儲(chǔ),以一種Lustre特有的方法組織著:
組號(hào)
在OST的頂層目錄下是以各組命名的子目錄。這種布局容許了集群MDS的存在,而每個(gè)組對(duì)應(yīng)一個(gè)MDS。就目前來(lái)說,只使用一個(gè)MDS,所以只有第零組是有效的。
對(duì)象ID
在每個(gè)組里,創(chuàng)建了32個(gè)子目錄。對(duì)每個(gè)文件對(duì)象,它的最后五位用來(lái)表明這個(gè)文件應(yīng)該放置在哪個(gè)子目錄中。這里,文件名是對(duì)象ID。
6.3 obdfilter
obdfilter設(shè)備是在OST服務(wù)初始化的時(shí)候創(chuàng)建的。對(duì)每個(gè)OST我們有一個(gè)相對(duì)應(yīng)的obdfilter設(shè)備。對(duì)每個(gè)客戶端連接,obdfilter創(chuàng)建一個(gè)輸出口(export)作為輸出的管道。所有的輸出口都維護(hù)在一個(gè)全局哈希表中,哈希關(guān)鍵字稱為UUID,在Figure 10和11中都給出了。Portal RPC層使用UUID來(lái)快速確定到來(lái)的請(qǐng)求應(yīng)當(dāng)送去哪個(gè)輸出口(和obdfilter設(shè)備)。另外,每個(gè)obdfilter設(shè)備維護(hù)一個(gè)它服務(wù)的輸出口鏈表。這種關(guān)系在Figure 10中畫出來(lái)了。
obdfilter提供了如下函數(shù):
- 處理創(chuàng)建請(qǐng)求,該請(qǐng)求可能是來(lái)自于MDS的對(duì)文件數(shù)據(jù)對(duì)象的請(qǐng)求。
- 處理讀取和限額如請(qǐng)求,該請(qǐng)求來(lái)自于OSC客戶端。
- 處理連接和斷開連接請(qǐng)求,該請(qǐng)求是來(lái)自于低層Portal RPC層的,對(duì)已建立好的輸出口和輸入口的請(qǐng)求。
- 處理銷毀請(qǐng)求(牽涉到客戶端和MDS兩方面)。
6.3.1文件刪除
銷毀協(xié)議如下所述。首先,客戶端決定刪除一個(gè)文件,而這個(gè)請(qǐng)求傳送到了MDS。MDS檢查了EA分條,并用llog產(chǎn)生了一個(gè)事務(wù)日志。這個(gè)日志包含如下:<從OST1中unlink對(duì)象1,從OST2中unlink對(duì)象2,等等>。然后MDS將布局和事務(wù)日志傳輸?shù)娇蛻舳恕?蛻舳说玫竭@個(gè)日志,并與每個(gè)OST(實(shí)際上是obdfilter)聯(lián)系,刪除(unlink)每個(gè)文件對(duì)象。一旦所有在MDS上的的刪除(unlink)llog記錄都已經(jīng)被告知,那么文件刪除過程就結(jié)束了。
6.3.2文件創(chuàng)建
正如早先在第5節(jié)所講的,所有的請(qǐng)求都由OST和obdfilter一起處理。現(xiàn)在我們來(lái)理順處理創(chuàng)建請(qǐng)求的流程。處理請(qǐng)求的第一部分是在ost_create()進(jìn)行了如下操作:
1. 準(zhǔn)備回復(fù)消息的大小。這由兩個(gè)記錄組成,所以需要兩塊緩沖。第一個(gè)記錄是為portalrpc body準(zhǔn)備的,而第二個(gè)是為ostreply body準(zhǔn)備的。
__u32 size[2] ={ sizeof(struct ptlrpc_body), sizeof(*repbody)};
2. 從原始請(qǐng)求中取得一個(gè)請(qǐng)求體的指針,并在需要的時(shí)候進(jìn)行字節(jié)交換。
struct ost_body*body = lustre_swab_reqbuf(req, REQ_REC_OFF,
sizeof(*body),lustre_swab_ost_body);
最后一個(gè)參數(shù)是swab處理函數(shù),只有在需要交換的時(shí)候才調(diào)用它。客戶端的請(qǐng)求使用本地字節(jié)序,里面包含一個(gè)預(yù)先商量好的魔數(shù)。服務(wù)器端讀取這個(gè)魔數(shù),并用以確定是否需要進(jìn)行交換。
3. 進(jìn)行實(shí)際的空間分配,并填入初步的頭信息。
rc =lustre_pack_replay(req, 2, size, NULL);
repbody =lustre_msg_buf(req->rq_repmsg, REPLY_REC_OFF,sizeof(*repbody));
在第一個(gè)調(diào)用之后,req->rq_repmsg()指向新分配的空間。第二個(gè)調(diào)用為回復(fù)體緩沖設(shè)置了開始地址的repbody。
4. 最后,它用與請(qǐng)求體完全一樣內(nèi)容填充回復(fù)體,然后傳給obdfilter做進(jìn)一步處理。
memcpy(&repbody->oa,&body->oa, sizeof(body->oa));
req-rq_status =obd_create(exp, &repbody->oa, NULL, oti);
對(duì)于創(chuàng)建請(qǐng)求,obdfilter的入口點(diǎn)是通過filter_create():
static intfilter_create(struct obd_export *exp, struct obdo *oa ..).
我們忽略了牽涉到struct lov_stripe_md **ea和struct obd_trans_info *oti的處理過程,這是因?yàn)榍罢呤沁z留代碼,將來(lái)不大可能使用。
1. 首先,保存當(dāng)前上下文,并指定客戶端的上下文為它自己的操作上下文。這是為讓線程在想要訪問后端文件系統(tǒng)時(shí),為其確定必須的信息。這就像一個(gè)sandbox(?),限制在處理客戶端請(qǐng)求時(shí),服務(wù)線程的reach(?)。它為服務(wù)線程存儲(chǔ)“文件系統(tǒng)根”和“當(dāng)前工作目錄”(當(dāng)然,不是從客戶端取得,而是取決于我們正在哪個(gè)個(gè)OST上工作)。
obd =exp->exp_obd;
put_ctxt(&saved,&obd->obd_lvfs_ctxt, NULL);
2. 如果請(qǐng)求的目的是重新創(chuàng)建一個(gè)對(duì)象,那么我們?nèi)∠屑釉谥亟▽?duì)象上的extent鎖,取消方法是要求所有加在對(duì)象上的鎖調(diào)用filter_recreate()來(lái)做實(shí)際的工作。否則,我們按照正常的重建對(duì)象的流程執(zhí)行。重建的原因是,從概念上講,當(dāng)MDS指示OST創(chuàng)建一個(gè)對(duì)象,OST并不單單創(chuàng)建一個(gè)對(duì)象,而是創(chuàng)建多個(gè)已指定了對(duì)象ID的對(duì)象。創(chuàng)建的這一組對(duì)象的磁盤大小是零。這樣做的目的是,當(dāng)下一次MDS回復(fù)客戶端創(chuàng)建新文件的請(qǐng)求時(shí),它不用再向OST發(fā)送請(qǐng)求,就能為客戶端呈現(xiàn)布局信息。通過查看每個(gè)OST中預(yù)先創(chuàng)建的對(duì)象池,MDS就可能已經(jīng)擁有所有用來(lái)回復(fù)客戶端所需的信息。
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);
}
這里,從預(yù)先創(chuàng)建處理函數(shù)中返回的rc要么是一個(gè)負(fù)數(shù),表明錯(cuò)誤,要么是一個(gè)非負(fù)數(shù),表明創(chuàng)建的文件數(shù)目。
3. 現(xiàn)在,我們更近一點(diǎn)看看預(yù)先創(chuàng)建函數(shù):
當(dāng)客戶端通過一個(gè)預(yù)先創(chuàng)建對(duì)象ID與一個(gè)OST聯(lián)系時(shí),OST知道這個(gè)對(duì)象ID現(xiàn)在被激活了。但是這里出現(xiàn)一個(gè)問題:如果MDS掛了,它關(guān)于預(yù)先創(chuàng)建對(duì)象的信息就過時(shí)了。為了解決這個(gè)沖突,當(dāng)MDS重啟時(shí),它檢查未使用的預(yù)先創(chuàng)建對(duì)象的記錄,向OST發(fā)送請(qǐng)求,刪除那些對(duì)象(刪除孤兒)。obdfilter接收這些請(qǐng)求,跳過那些實(shí)際上已經(jīng)被使用(但是沒有和MDS自己的記錄同步)的對(duì)象,將剩下的對(duì)象刪除。這就是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. 最終,創(chuàng)建請(qǐng)求傳遞到fsfilt,由VFS調(diào)用完成。這個(gè)進(jìn)程稍后將經(jīng)過更多的步驟,例如取得父親索引節(jié)點(diǎn),事務(wù)創(chuàng)建等等。
rc =ll_vfs_create(dparent->d_inode, dchild,
S_IFREG | S_ISUID | S_ISGID | 0666, NULL);
本文章歡迎轉(zhuǎn)載,請(qǐng)保留原始博客鏈接http://blog.csdn.net/fsdev/article
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

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