Tommy 碎碎念

Tommy Wu's blog

« 上一篇 | 下一篇 »

xfsdump 的 page allocation failure 問題
post by tommy @ 02 二月, 2012 22:00

最近在備份時發現常常出現類似下面的訊息:

/usr/sbin/xfsdump: WARNING: could not get list of non-root attributes for nondir ino 234123: Cannot allocate memory (12)

然後在 syslog 裡頭可以看到 kernel 有 page allocation failure 的錯誤訊息.

看起來是與這個問題類似. 通常在剛開機沒多久的時候都很正常, 但是運作久了之後, 就容易出現這個問題. 依據該討論串的說明, 應該是 kmalloc() 在運作一陣子之後, 可能會沒有那麼大的連續可用空間可以使用, 所以 xfs code 裡頭用到這個的時候會發生錯誤. (我沒有把這個 dump 還原來比較, 所以不確定是不是備份會不完整)

如果在發生這問題時, 改用 vmalloc() 再試一次, 應該就可以避免了.

不過, 說歸說, 似乎沒有人真的去改這部份的程式碼.... 所以, 我就自己試著弄了一個 patch, 用了一陣子, 應該是沒什麼問題, 至少在我這兒沒再看到這個錯誤.

--- linux/fs/xfs/xfs_acl.c.orig  2012-01-12 14:56:18.463983793 +0800
+++ linux/fs/xfs/xfs_acl.c 2012-01-12 14:55:36.203689806 +0800
@@ -111,6 +111,7 @@
int len = sizeof(struct xfs_acl);
unsigned char *ea_name;
int error;
+ int bUseKmem = 1;
 
acl = get_cached_acl(inode, type);
if (acl != ACL_NOT_CACHED)
@@ -135,8 +136,13 @@
*/
 
xfs_acl = kzalloc(sizeof(struct xfs_acl), GFP_KERNEL|__GFP_NOWARN);
- if (!xfs_acl)
- return ERR_PTR(-ENOMEM);
+ if (!xfs_acl) {
+ xfs_acl = vmalloc(sizeof(struct xfs_acl));
+ if (!xfs_acl)
+ return ERR_PTR(-ENOMEM);
+ bUseKmem = 0;
+ memset(xfs_acl, 0, sizeof(struct xfs_acl));
+ }
 
error = -xfs_attr_get(ip, ea_name, (unsigned char *)xfs_acl,
&len, ATTR_ROOT);
@@ -160,7 +166,10 @@
out_update_cache:
set_cached_acl(inode, type, acl);
out:
- kfree(xfs_acl);
+ if (bUseKmem)
+ kfree(xfs_acl);
+ else
+ vfree(xfs_acl);
return acl;
}
 
@@ -190,10 +199,16 @@
if (acl) {
struct xfs_acl *xfs_acl;
int len;
+ int bUseKmem = 1;
 
xfs_acl = kzalloc(sizeof(struct xfs_acl), GFP_KERNEL|__GFP_NOWARN);
- if (!xfs_acl)
- return -ENOMEM;
+ if (!xfs_acl) {
+ xfs_acl = vmalloc(sizeof(struct xfs_acl));
+ if (!xfs_acl)
+ return -ENOMEM;
+ bUseKmem = 0;
+ memset(xfs_acl, 0, sizeof(struct xfs_acl));
+ }
 
xfs_acl_to_disk(xfs_acl, acl);
len = sizeof(struct xfs_acl) -
@@ -203,7 +218,10 @@
error = -xfs_attr_set(ip, ea_name, (unsigned char *)xfs_acl,
len, ATTR_ROOT);
 
- kfree(xfs_acl);
+ if (bUseKmem)
+ kfree(xfs_acl);
+ else
+ vfree(xfs_acl);
} else {
/*
* A NULL ACL argument means we want to remove the ACL.
--- linux/fs/xfs/xfs_ioctl.c.orig 2012-01-12 14:28:58.315962419 +0800
+++ linux/fs/xfs/xfs_ioctl.c 2012-01-12 14:51:46.195428847 +0800
@@ -313,6 +313,7 @@
__u32 olen;
void *link;
int error;
+ int bUseKmem = 1;
 
if (!capable(CAP_SYS_ADMIN))
return -XFS_ERROR(EPERM);
@@ -334,8 +335,12 @@
 
link = kmalloc(MAXPATHLEN+1, GFP_KERNEL|__GFP_NOWARN);
if (!link) {
- error = -XFS_ERROR(ENOMEM);
- goto out_dput;
+ link = vmalloc(MAXPATHLEN+1);
+ if (!link) {
+ error = -XFS_ERROR(ENOMEM);
+ goto out_dput;
+ }
+ bUseKmem = 0;
}
 
error = -xfs_readlink(XFS_I(dentry->d_inode), link);
@@ -346,7 +351,10 @@
goto out_kfree;
 
out_kfree:
- kfree(link);
+ if (bUseKmem)
+ kfree(link);
+ else
+ vfree(link);
out_dput:
dput(dentry);
return error;
@@ -399,6 +407,7 @@
xfs_fsop_attrlist_handlereq_t al_hreq;
struct dentry *dentry;
char *kbuf;
+ int bUseKmem = 1;
 
if (!capable(CAP_SYS_ADMIN))
return -XFS_ERROR(EPERM);
@@ -418,8 +427,13 @@
return PTR_ERR(dentry);
 
kbuf = kzalloc(al_hreq.buflen, GFP_KERNEL|__GFP_NOWARN);
- if (!kbuf)
- goto out_dput;
+ if (!kbuf) {
+ kbuf = vmalloc(al_hreq.buflen);
+ if (!kbuf)
+ goto out_dput;
+ bUseKmem = 0;
+ memset(kbuf, 0, al_hreq.buflen);
+ }
 
cursor = (attrlist_cursor_kern_t *)&al_hreq.pos;
error = -xfs_attr_list(XFS_I(dentry->d_inode), kbuf, al_hreq.buflen,
@@ -431,7 +445,10 @@
error = -EFAULT;
 
out_kfree:
- kfree(kbuf);
+ if (bUseKmem)
+ kfree(kbuf);
+ else
+ vfree(kbuf);
out_dput:
dput(dentry);
return error;
@@ -447,12 +464,17 @@
{
unsigned char *kbuf;
int error = EFAULT;
+ int bUseKmem = 1;
 
if (*len > XATTR_SIZE_MAX)
return EINVAL;
kbuf = kmalloc(*len, GFP_KERNEL|__GFP_NOWARN);
- if (!kbuf)
- return ENOMEM;
+ if (!kbuf) {
+ kbuf = vmalloc(*len);
+ if (!kbuf)
+ return ENOMEM;
+ bUseKmem = 0;
+ }
 
error = xfs_attr_get(XFS_I(inode), name, kbuf, (int *)len, flags);
if (error)
@@ -462,7 +484,10 @@
error = EFAULT;
 
out_kfree:
- kfree(kbuf);
+ if (bUseKmem)
+ kfree(kbuf);
+ else
+ vfree(kbuf);
return error;
}
 
@@ -513,6 +538,7 @@
struct dentry *dentry;
unsigned int i, size;
unsigned char *attr_name;
+ int bUseKmem = 1;
 
if (!capable(CAP_SYS_ADMIN))
return -XFS_ERROR(EPERM);
@@ -539,8 +565,12 @@
}
 
attr_name = kmalloc(MAXNAMELEN, GFP_KERNEL|__GFP_NOWARN);
- if (!attr_name)
- goto out_kfree_ops;
+ if (!attr_name) {
+ attr_name = vmalloc(MAXNAMELEN);
+ if (!attr_name)
+ goto out_kfree_ops;
+ bUseKmem = 0;
+ }
 
error = 0;
for (i = 0; i < am_hreq.opcount; i++) {
@@ -585,7 +615,10 @@
if (copy_to_user(am_hreq.ops, ops, size))
error = XFS_ERROR(EFAULT);
 
- kfree(attr_name);
+ if (bUseKmem)
+ kfree(attr_name);
+ else
+ vfree(attr_name);
out_kfree_ops:
kfree(ops);
out_dput:
--- linux/fs/xfs/xfs_ioctl32.c.orig 2012-01-12 14:42:07.971467210 +0800
+++ linux/fs/xfs/xfs_ioctl32.c 2012-01-12 14:46:03.483072418 +0800
@@ -355,6 +355,7 @@
compat_xfs_fsop_attrlist_handlereq_t al_hreq;
struct dentry *dentry;
char *kbuf;
+ int bUseKmem = 1;
 
if (!capable(CAP_SYS_ADMIN))
return -XFS_ERROR(EPERM);
@@ -376,8 +377,12 @@
 
error = -ENOMEM;
kbuf = kmalloc(al_hreq.buflen, GFP_KERNEL|__GFP_NOWARN);
- if (!kbuf)
- goto out_dput;
+ if (!kbuf) {
+ kbuf = vmalloc(al_hreq.buflen);
+ if (!kbuf)
+ goto out_dput;
+ bUseKmem = 0;
+ }
 
cursor = (attrlist_cursor_kern_t *)&al_hreq.pos;
error = -xfs_attr_list(XFS_I(dentry->d_inode), kbuf, al_hreq.buflen,
@@ -389,7 +394,10 @@
error = -EFAULT;
 
out_kfree:
- kfree(kbuf);
+ if (bUseKmem)
+ kfree(kbuf);
+ else
+ vfree(kbuf);
out_dput:
dput(dentry);
return error;
@@ -406,6 +414,7 @@
struct dentry *dentry;
unsigned int i, size;
unsigned char *attr_name;
+ int bUseKmem = 1;
 
if (!capable(CAP_SYS_ADMIN))
return -XFS_ERROR(EPERM);
@@ -433,8 +442,12 @@
}
 
attr_name = kmalloc(MAXNAMELEN, GFP_KERNEL|__GFP_NOWARN);
- if (!attr_name)
- goto out_kfree_ops;
+ if (!attr_name) {
+ attr_name = vmalloc(MAXNAMELEN);
+ if (!attr_name)
+ goto out_kfree_ops;
+ bUseKmem = 0;
+ }
 
error = 0;
for (i = 0; i < am_hreq.opcount; i++) {
@@ -480,7 +493,10 @@
if (copy_to_user(compat_ptr(am_hreq.ops), ops, size))
error = XFS_ERROR(EFAULT);
 
- kfree(attr_name);
+ if (bUseKmem)
+ kfree(attr_name);
+ else
+ vfree(attr_name);
out_kfree_ops:
kfree(ops);
out_dput:
--- linux/fs/xfs/xfs_iops.c.orig 2012-01-12 14:38:40.016726689 +0800
+++ linux/fs/xfs/xfs_iops.c 2012-01-12 14:39:52.597216706 +0800
@@ -422,10 +422,15 @@
{
char *link;
int error = -ENOMEM;
+ int bUseKmem = 1;
 
link = kmalloc(MAXPATHLEN+1, GFP_KERNEL|__GFP_NOWARN);
- if (!link)
- goto out_err;
+ if (!link) {
+ link = vmalloc(MAXPATHLEN+1);
+ if (!link)
+ goto out_err;
+ bUseKmem = 0;
+ }
 
error = -xfs_readlink(XFS_I(dentry->d_inode), link);
if (unlikely(error))
@@ -435,7 +440,10 @@
return NULL;
 
out_kfree:
- kfree(link);
+ if (bUseKmem)
+ kfree(link);
+ else
+ vfree(link);
out_err:
nd_set_link(nd, ERR_PTR(error));
return NULL;
--- linux/fs/xfs/xfs_super.c.orig 2012-01-12 14:57:24.341109381 +0800
+++ linux/fs/xfs/xfs_super.c 2012-01-12 14:58:45.871678412 +0800
@@ -1302,10 +1302,16 @@
struct inode *root;
struct xfs_mount *mp = NULL;
int flags = 0, error = ENOMEM;
+ int bUseKmem = 1;
 
mp = kzalloc(sizeof(struct xfs_mount), GFP_KERNEL|__GFP_NOWARN);
- if (!mp)
- goto out;
+ if (!mp) {
+ mp = vmalloc(sizeof(struct xfs_mount));
+ if (!mp)
+ goto out;
+ bUseKmem = 0;
+ memset(mp, 0, sizeof(struct xfs_mount));
+ }
 
spin_lock_init(&mp->m_sb_lock);
mutex_init(&mp->m_growlock);
@@ -1403,7 +1409,10 @@
xfs_close_devices(mp);
out_free_fsname:
xfs_free_fsname(mp);
- kfree(mp);
+ if (bUseKmem)
+ kfree(mp);
+ else
+ vfree(mp);
out:
return -error;

有使用 xfsdump 做備份的, 可以考慮自己加上這個 patch.... 不然, 等 xfs 的 team 以後有人修正吧.

Del.icio.us Furl HEMiDEMi Technorati MyShare
迴響
暱稱:
標題:
個人網頁:
電子郵件:
迴響

  

Bad Behavior 已經阻擋了 95 個過去 7 天試圖闖關的垃圾迴響與引用。
Power by LifeType. Template design by JamesHuang. Valid XHTML and CSS