天天看點

cgroup----devices子系統

devices子系統

    使用devices 子系統可以允許或者拒絕cgroup中的程序通路裝置。devices子系統有三個控制檔案:devices.allow,devices.deny,devices.list。devices.allow用于指定cgroup中的程序可以通路的裝置,devices.deny用于指定cgroup中的程序不能通路的裝置,devices.list用于報告cgroup中的程序通路的裝置。devices.allow檔案中包含若幹條目,每個條目有四個字段:type、major、minor 和 access。type、major 和 minor 字段中使用的值對應 Linux 配置設定的裝置。

type指定裝置類型:

a - 應用所有裝置,可以是字元裝置,也可以是塊裝置

b- 指定塊裝置

c - 指定字元裝置

major和minor指定裝置的主次裝置号。

access 則指定相應的權限:

r - 允許任務從指定裝置中讀取

w - 允許任務寫入指定裝置

m - 允許任務生成還不存在的裝置檔案

devices子系統是通過提供device whilelist 來實作的。與其他子系統一樣,devices子系統也有一個内嵌了cgroup_subsystem_state的結構來管理資源。在devices子系統中,這個結構是:

struct dev_cgroup {

struct cgroup_subsys_state css;

struct list_head whitelist;

};

這個結構體除了通用的cgroup_subsystem_state之外,就隻有一個連結清單指針,而這個連結清單指針指向了該cgroup中的程序可以通路的devices whilelist。

下面我們來看一下devices子系統如何管理whilelist。在devices子系統中,定義了一個叫dev_whitelist_item的結構來管理可以通路的device,對應于devices.allow中的一個條目。這個結構體的定義如下:

struct dev_whitelist_item {

u32 major, minor;

short type;

short access;

struct list_head list;

struct rcu_head rcu;

};

major,minor用于指定裝置的主次裝置号,type用于指定裝置類型,type取值可以是:#define DEV_BLOCK 1

#define DEV_CHAR  2

#define DEV_ALL   4 

對應于之前devices.allow檔案中三種情況。

access用于相應的通路權限,access取值可以是:

#define ACC_MKNOD 1

#define ACC_READ  2

#define ACC_WRITE 4

也和之前devices.allow檔案中的情況對應。

List字段用于将該結構體連到相應的dev_cgroup中whitelist指向的連結清單。

通過以上資料結構,devices子系統就能管理一個cgroup的程序可以通路的devices了。 光有資料結構還不行,還要有具體實作才行。devices子系統通過實作兩個函數供核心調用來實作控制cgroup中的程序能夠通路的devices。首先我們來第一個函數:

int devcgroup_inode_permission(struct inode *inode, int mask)

{

struct dev_cgroup *dev_cgroup;

struct dev_whitelist_item *wh;

dev_t device = inode->i_rdev;

if (!device)

return 0;

if (!S_ISBLK(inode->i_mode) && !S_ISCHR(inode->i_mode))

return 0;

rcu_read_lock();

dev_cgroup = task_devcgroup(current);

list_for_each_entry_rcu(wh, &dev_cgroup->whitelist, list) {

if (wh->type & DEV_ALL)

goto found;

if ((wh->type & DEV_BLOCK) && !S_ISBLK(inode->i_mode))

continue;

if ((wh->type & DEV_CHAR) && !S_ISCHR(inode->i_mode))

continue;

if (wh->major != ~0 && wh->major != imajor(inode))

continue;

if (wh->minor != ~0 && wh->minor != iminor(inode))

continue;

if ((mask & MAY_WRITE) && !(wh->access & ACC_WRITE))

continue;

if ((mask & MAY_READ) && !(wh->access & ACC_READ))

continue;

found:

rcu_read_unlock();

return 0;

}

rcu_read_unlock();

return -EPERM;

}

    我們來簡單分析一下這個函數,首先如果該inode對應的不是devices,直接傳回0,如果既不是塊裝置也不是字元裝置,也傳回0,因為devices隻控制塊裝置和字元裝置的通路,其他情況不管。接着獲得目前程序的dev_cgroup,然後在dev_cgroup中whitelist指針的連結清單中查找,如果找到對應裝置而且mask指定的權限和裝置的權限一緻就傳回0,如果沒有找到就傳回錯誤。

    這個函數是針對inode節點存在的情況,通過對比權限來控制cgroup中的程序能夠通路的devices。還有一個情況是inode不存在,在這種情況下,一個程序要通路一個裝置就必須通過mknod建立相應的裝置檔案。為了達到對這種情況的控制,devices子系統導出了第二個函數:

int devcgroup_inode_mknod(int mode, dev_t dev)

{

struct dev_cgroup *dev_cgroup;

struct dev_whitelist_item *wh;

if (!S_ISBLK(mode) && !S_ISCHR(mode))

return 0;

rcu_read_lock();

dev_cgroup = task_devcgroup(current);

list_for_each_entry_rcu(wh, &dev_cgroup->whitelist, list) {

if (wh->type & DEV_ALL)

goto found;

if ((wh->type & DEV_BLOCK) && !S_ISBLK(mode))

continue;

if ((wh->type & DEV_CHAR) && !S_ISCHR(mode))

continue;

if (wh->major != ~0 && wh->major != MAJOR(dev))

continue;

if (wh->minor != ~0 && wh->minor != MINOR(dev))

continue;

if (!(wh->access & ACC_MKNOD))

continue;

found:

rcu_read_unlock();

return 0;

}

rcu_read_unlock();

return -EPERM;

}

這個函數的實作跟第一個函數類似,這裡就不贅述了。

下面我們再來看一下devices子系統本身的一些東西。跟其他子系統一樣,devices同樣實作了一個cgroup_subsys:

struct cgroup_subsys devices_subsys = {

.name = "devices",

.can_attach = devcgroup_can_attach,

.create = devcgroup_create,

.destroy = devcgroup_destroy,

.populate = devcgroup_populate,

.subsys_id = devices_subsys_id,

};

devices相應的三個控制檔案:

static struct cftype dev_cgroup_files[] = {

{

.name = "allow",

.write_string  = devcgroup_access_write,

.private = DEVCG_ALLOW,

},

{

.name = "deny",

.write_string = devcgroup_access_write,

.private = DEVCG_DENY,

},

{

.name = "list",

.read_seq_string = devcgroup_seq_read,

.private = DEVCG_LIST,

},

};

其中allow和deny都是通過devcgroup_access_write實作的,隻是通過private字段區分,因為二者的實作邏輯有相同的地方。devcgroup_access_write最終通過調用devcgroup_update_access來實作。在devcgroup_update_access根據寫入的内容構造一個dev_whitelist_item ,然後根據檔案類型做不同的處理:

switch (filetype) {

case DEVCG_ALLOW:

if (!parent_has_perm(devcgroup, &wh))

return -EPERM;

return dev_whitelist_add(devcgroup, &wh);

case DEVCG_DENY:

dev_whitelist_rm(devcgroup, &wh);

break;

default:

return -EINVAL;

}

allow的話,就将item加入whitelist,deny的話,就将item從whitelist中删去。

作者曰:devices子系統的實作相對比較簡單,通過在核心對裝置通路的時候加入額外的檢查來實作。而devices子系統本身隻需要管理好可以通路的裝置清單就行了。

繼續閱讀