Netdev List
 help / color / mirror / Atom feed
* [PATCH v2 12/12] sunrpc: make rpc_pipefs be mountable multiple times
From: Kirill A. Shutemov @ 2010-12-29 13:14 UTC (permalink / raw)
  To: Trond Myklebust, J. Bruce Fields, Neil Brown
  Cc: Pavel Emelyanov, linux-nfs, David S. Miller, netdev, linux-kernel,
	Kirill A. Shutemov
In-Reply-To: <1293628470-28386-1-git-send-email-kas@openvz.org>

To support containers, allow multiple independent instances of
rpc_pipefs. Use '-o newinstance' to create new of the filesystem.
The same semantics as with devpts.

Signed-off-by: Kirill A. Shutemov <kas@openvz.org>
---
 net/sunrpc/rpc_pipe.c |   80 ++++++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 79 insertions(+), 1 deletions(-)

diff --git a/net/sunrpc/rpc_pipe.c b/net/sunrpc/rpc_pipe.c
index fec6b2d..7b693db 100644
--- a/net/sunrpc/rpc_pipe.c
+++ b/net/sunrpc/rpc_pipe.c
@@ -17,6 +17,7 @@
 #include <linux/fsnotify.h>
 #include <linux/kernel.h>
 #include <linux/nsproxy.h>
+#include <linux/parser.h>
 
 #include <asm/ioctls.h>
 #include <linux/fs.h>
@@ -39,6 +40,49 @@ static struct kmem_cache *rpc_inode_cachep __read_mostly;
 
 #define RPC_UPCALL_TIMEOUT (30*HZ)
 
+struct rpc_mount_opts {
+	int newinstance;
+};
+
+enum {
+	Opt_newinstance,
+
+	Opt_err
+};
+
+static const match_table_t tokens = {
+	{Opt_newinstance, "newinstance"},
+
+	{Opt_err, NULL}
+};
+
+static int
+parse_mount_options(char *data, struct rpc_mount_opts *opts)
+{
+	char *p;
+
+	opts->newinstance = 0;
+
+	while ((p = strsep(&data, ",")) != NULL) {
+		substring_t args[MAX_OPT_ARGS];
+		int token;
+
+		if (!*p)
+			continue;
+
+		token = match_token(p, tokens, args);
+		switch (token) {
+		case Opt_newinstance:
+			opts->newinstance = 1;
+			break;
+		default:
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
 static void rpc_purge_list(struct rpc_inode *rpci, struct list_head *head,
 		void (*destroy_msg)(struct rpc_pipe_msg *), int err)
 {
@@ -1039,11 +1083,45 @@ rpc_fill_super(struct super_block *sb, void *data, int silent)
 	return 0;
 }
 
+static int
+compare_rpc_mnt_sb(struct super_block *s, void *p)
+{
+	if (init_rpc_pipefs)
+		return init_rpc_pipefs->mnt_sb == s;
+	return 0;
+}
+
 static struct dentry *
 rpc_mount(struct file_system_type *fs_type,
 		int flags, const char *dev_name, void *data)
 {
-	return mount_single(fs_type, flags, data, rpc_fill_super);
+	int error;
+	struct rpc_mount_opts opts;
+	struct super_block *s;
+
+	error = parse_mount_options(data, &opts);
+	if (error)
+		return ERR_PTR(error);
+
+	if (opts.newinstance)
+		s = sget(fs_type, NULL, set_anon_super, NULL);
+	else
+		s = sget(fs_type, compare_rpc_mnt_sb, set_anon_super, NULL);
+
+	if (IS_ERR(s))
+		return ERR_CAST(s);
+
+	if (!s->s_root) {
+		s->s_flags = flags;
+		error = rpc_fill_super(s, data, flags & MS_SILENT ? 1 : 0);
+		if (error) {
+			deactivate_locked_super(s);
+			return ERR_PTR(error);
+		}
+		s->s_flags |= MS_ACTIVE;
+	}
+
+	return dget(s->s_root);
 }
 
 static struct file_system_type rpc_pipe_fs_type = {
-- 
1.7.3.4


^ permalink raw reply related

* [PATCH v2 08/12] sunrpc: tag pipefs field of cache_detail with rpc_pipefs mount point
From: Kirill A. Shutemov @ 2010-12-29 13:14 UTC (permalink / raw)
  To: Trond Myklebust, J. Bruce Fields, Neil Brown
  Cc: Pavel Emelyanov, linux-nfs, David S. Miller, netdev, linux-kernel,
	Kirill A. Shutemov
In-Reply-To: <1293628470-28386-1-git-send-email-kas@openvz.org>

Signed-off-by: Kirill A. Shutemov <kas@openvz.org>
---
 fs/nfs/cache_lib.c           |    3 +--
 include/linux/sunrpc/cache.h |    9 +++------
 net/sunrpc/cache.c           |   16 ++++++++++------
 3 files changed, 14 insertions(+), 14 deletions(-)

diff --git a/fs/nfs/cache_lib.c b/fs/nfs/cache_lib.c
index dd7ca5f..0944d4e 100644
--- a/fs/nfs/cache_lib.c
+++ b/fs/nfs/cache_lib.c
@@ -123,7 +123,7 @@ int nfs_cache_register(struct cache_detail *cd)
 	ret = vfs_path_lookup(mnt->mnt_root, mnt, "/cache", 0, &nd);
 	if (ret)
 		goto err;
-	ret = sunrpc_cache_register_pipefs(nd.path.dentry,
+	ret = sunrpc_cache_register_pipefs(mnt, nd.path.dentry,
 			cd->name, 0600, cd);
 	path_put(&nd.path);
 	if (!ret)
@@ -136,6 +136,5 @@ err:
 void nfs_cache_unregister(struct cache_detail *cd)
 {
 	sunrpc_cache_unregister_pipefs(cd);
-	mntput(init_rpc_pipefs);
 }
 
diff --git a/include/linux/sunrpc/cache.h b/include/linux/sunrpc/cache.h
index 6950c98..d34a621 100644
--- a/include/linux/sunrpc/cache.h
+++ b/include/linux/sunrpc/cache.h
@@ -64,10 +64,6 @@ struct cache_detail_procfs {
 	struct proc_dir_entry   *flush_ent, *channel_ent, *content_ent;
 };
 
-struct cache_detail_pipefs {
-	struct dentry *dir;
-};
-
 struct cache_detail {
 	struct module *		owner;
 	int			hash_size;
@@ -114,7 +110,7 @@ struct cache_detail {
 
 	union {
 		struct cache_detail_procfs procfs;
-		struct cache_detail_pipefs pipefs;
+		struct path pipefs;
 	} u;
 };
 
@@ -201,7 +197,8 @@ extern int cache_register_net(struct cache_detail *cd, struct net *net);
 extern void cache_unregister(struct cache_detail *cd);
 extern void cache_unregister_net(struct cache_detail *cd, struct net *net);
 
-extern int sunrpc_cache_register_pipefs(struct dentry *parent, const char *,
+extern int sunrpc_cache_register_pipefs(struct vfsmount *rpcmount,
+					struct dentry *parent, const char *,
 					mode_t, struct cache_detail *);
 extern void sunrpc_cache_unregister_pipefs(struct cache_detail *);
 
diff --git a/net/sunrpc/cache.c b/net/sunrpc/cache.c
index e433e75..ed50d49 100644
--- a/net/sunrpc/cache.c
+++ b/net/sunrpc/cache.c
@@ -28,6 +28,7 @@
 #include <linux/workqueue.h>
 #include <linux/mutex.h>
 #include <linux/pagemap.h>
+#include <linux/mount.h>
 #include <asm/ioctls.h>
 #include <linux/sunrpc/types.h>
 #include <linux/sunrpc/cache.h>
@@ -1753,7 +1754,8 @@ const struct file_operations cache_flush_operations_pipefs = {
 	.llseek		= no_llseek,
 };
 
-int sunrpc_cache_register_pipefs(struct dentry *parent,
+int sunrpc_cache_register_pipefs(struct vfsmount *rpcmount,
+				 struct dentry *parent,
 				 const char *name, mode_t umode,
 				 struct cache_detail *cd)
 {
@@ -1766,9 +1768,10 @@ int sunrpc_cache_register_pipefs(struct dentry *parent,
 	q.len = strlen(name);
 	q.hash = full_name_hash(q.name, q.len);
 	dir = rpc_create_cache_dir(parent, &q, umode, cd);
-	if (!IS_ERR(dir))
-		cd->u.pipefs.dir = dir;
-	else {
+	if (!IS_ERR(dir)) {
+		cd->u.pipefs.mnt = mntget(rpcmount);
+		cd->u.pipefs.dentry = dir;
+	} else {
 		sunrpc_destroy_cache_detail(cd);
 		ret = PTR_ERR(dir);
 	}
@@ -1778,8 +1781,9 @@ EXPORT_SYMBOL_GPL(sunrpc_cache_register_pipefs);
 
 void sunrpc_cache_unregister_pipefs(struct cache_detail *cd)
 {
-	rpc_remove_cache_dir(cd->u.pipefs.dir);
-	cd->u.pipefs.dir = NULL;
+	rpc_remove_cache_dir(cd->u.pipefs.dentry);
+	cd->u.pipefs.dentry = NULL;
+	mntput(cd->u.pipefs.mnt);
 	sunrpc_destroy_cache_detail(cd);
 }
 EXPORT_SYMBOL_GPL(sunrpc_cache_unregister_pipefs);
-- 
1.7.3.4


^ permalink raw reply related

* [PATCH v2 02/12] sunrpc: introduce init_rpc_pipefs
From: Kirill A. Shutemov @ 2010-12-29 13:14 UTC (permalink / raw)
  To: Trond Myklebust, J. Bruce Fields, Neil Brown
  Cc: Pavel Emelyanov, linux-nfs, David S. Miller, netdev, linux-kernel,
	Kirill A. Shutemov
In-Reply-To: <1293628470-28386-1-git-send-email-kas@openvz.org>

Introduce global variable init_rpc_pipefs and use it instead of
rpc_get_mount()/rpc_put_mount().

Signed-off-by: Kirill A. Shutemov <kas@openvz.org>
---
 fs/nfs/cache_lib.c                 |    6 +++---
 include/linux/sunrpc/rpc_pipe_fs.h |    4 ++--
 net/sunrpc/clnt.c                  |   10 ++++------
 net/sunrpc/rpc_pipe.c              |   23 ++++++-----------------
 4 files changed, 15 insertions(+), 28 deletions(-)

diff --git a/fs/nfs/cache_lib.c b/fs/nfs/cache_lib.c
index 8469031..dd7ca5f 100644
--- a/fs/nfs/cache_lib.c
+++ b/fs/nfs/cache_lib.c
@@ -117,7 +117,7 @@ int nfs_cache_register(struct cache_detail *cd)
 	struct vfsmount *mnt;
 	int ret;
 
-	mnt = rpc_get_mount();
+	mnt = mntget(init_rpc_pipefs);
 	if (IS_ERR(mnt))
 		return PTR_ERR(mnt);
 	ret = vfs_path_lookup(mnt->mnt_root, mnt, "/cache", 0, &nd);
@@ -129,13 +129,13 @@ int nfs_cache_register(struct cache_detail *cd)
 	if (!ret)
 		return ret;
 err:
-	rpc_put_mount();
+	mntput(mnt);
 	return ret;
 }
 
 void nfs_cache_unregister(struct cache_detail *cd)
 {
 	sunrpc_cache_unregister_pipefs(cd);
-	rpc_put_mount();
+	mntput(init_rpc_pipefs);
 }
 
diff --git a/include/linux/sunrpc/rpc_pipe_fs.h b/include/linux/sunrpc/rpc_pipe_fs.h
index cf14db9..b09bfa5 100644
--- a/include/linux/sunrpc/rpc_pipe_fs.h
+++ b/include/linux/sunrpc/rpc_pipe_fs.h
@@ -44,6 +44,8 @@ RPC_I(struct inode *inode)
 	return container_of(inode, struct rpc_inode, vfs_inode);
 }
 
+extern struct vfsmount *init_rpc_pipefs;
+
 extern int rpc_queue_upcall(struct inode *, struct rpc_pipe_msg *);
 
 struct rpc_clnt;
@@ -60,8 +62,6 @@ extern void rpc_remove_cache_dir(struct dentry *);
 extern struct dentry *rpc_mkpipe(struct dentry *, const char *, void *,
 				 const struct rpc_pipe_ops *, int flags);
 extern int rpc_unlink(struct dentry *);
-extern struct vfsmount *rpc_get_mount(void);
-extern void rpc_put_mount(void);
 extern int register_rpc_pipefs(void);
 extern void unregister_rpc_pipefs(void);
 
diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c
index 92ce94f..da2507a 100644
--- a/net/sunrpc/clnt.c
+++ b/net/sunrpc/clnt.c
@@ -112,9 +112,7 @@ rpc_setup_pipedir(struct rpc_clnt *clnt, char *dir_name)
 	if (dir_name == NULL)
 		return 0;
 
-	path.mnt = rpc_get_mount();
-	if (IS_ERR(path.mnt))
-		return PTR_ERR(path.mnt);
+	path.mnt = mntget(init_rpc_pipefs);
 	error = vfs_path_lookup(path.mnt->mnt_root, path.mnt, dir_name, 0, &nd);
 	if (error)
 		goto err;
@@ -140,7 +138,7 @@ rpc_setup_pipedir(struct rpc_clnt *clnt, char *dir_name)
 err_path_put:
 	path_put(&nd.path);
 err:
-	rpc_put_mount();
+	mntput(path.mnt);
 	return error;
 }
 
@@ -251,7 +249,7 @@ static struct rpc_clnt * rpc_new_client(const struct rpc_create_args *args, stru
 out_no_auth:
 	if (!IS_ERR(clnt->cl_path.dentry)) {
 		rpc_remove_client_dir(clnt->cl_path.dentry);
-		rpc_put_mount();
+		mntput(clnt->cl_path.mnt);
 	}
 out_no_path:
 	kfree(clnt->cl_principal);
@@ -472,7 +470,7 @@ rpc_free_client(struct rpc_clnt *clnt)
 			clnt->cl_protname, clnt->cl_server);
 	if (!IS_ERR(clnt->cl_path.dentry)) {
 		rpc_remove_client_dir(clnt->cl_path.dentry);
-		rpc_put_mount();
+		mntput(clnt->cl_path.mnt);
 	}
 	if (clnt->cl_parent != clnt) {
 		rpc_release_client(clnt->cl_parent);
diff --git a/net/sunrpc/rpc_pipe.c b/net/sunrpc/rpc_pipe.c
index 7f3fbdd..b1e299b 100644
--- a/net/sunrpc/rpc_pipe.c
+++ b/net/sunrpc/rpc_pipe.c
@@ -28,9 +28,10 @@
 #include <linux/sunrpc/rpc_pipe_fs.h>
 #include <linux/sunrpc/cache.h>
 
-static struct vfsmount *rpc_mnt __read_mostly;
+struct vfsmount *init_rpc_pipefs __read_mostly;
 
 static struct file_system_type rpc_pipe_fs_type;
+EXPORT_SYMBOL_GPL(init_rpc_pipefs);
 
 
 static struct kmem_cache *rpc_inode_cachep __read_mostly;
@@ -412,18 +413,6 @@ struct rpc_filelist {
 	umode_t mode;
 };
 
-struct vfsmount *rpc_get_mount(void)
-{
-	return mntget(rpc_mnt);
-}
-EXPORT_SYMBOL_GPL(rpc_get_mount);
-
-void rpc_put_mount(void)
-{
-	mntput(rpc_mnt);
-}
-EXPORT_SYMBOL_GPL(rpc_put_mount);
-
 static int rpc_delete_dentry(struct dentry *dentry)
 {
 	return 1;
@@ -1060,9 +1049,9 @@ int register_rpc_pipefs(void)
 	if (err)
 		goto destroy_cache;
 
-	rpc_mnt = kern_mount(&rpc_pipe_fs_type);
-	if (IS_ERR(rpc_mnt)) {
-		err = PTR_ERR(rpc_mnt);
+	init_rpc_pipefs = kern_mount(&rpc_pipe_fs_type);
+	if (IS_ERR(init_rpc_pipefs)) {
+		err = PTR_ERR(init_rpc_pipefs);
 		goto unregister_fs;
 	}
 
@@ -1077,7 +1066,7 @@ destroy_cache:
 
 void unregister_rpc_pipefs(void)
 {
-	mntput(rpc_mnt);
+	mntput(init_rpc_pipefs);
 	kmem_cache_destroy(rpc_inode_cachep);
 	unregister_filesystem(&rpc_pipe_fs_type);
 }
-- 
1.7.3.4


^ permalink raw reply related

* [PATCH v2 11/12] nfs: introduce mount option 'rpcmount'
From: Kirill A. Shutemov @ 2010-12-29 13:14 UTC (permalink / raw)
  To: Trond Myklebust, J. Bruce Fields, Neil Brown
  Cc: Pavel Emelyanov, linux-nfs-u79uwXL29TY76Z2rM5mHXA,
	David S. Miller, netdev-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, Kirill A. Shutemov
In-Reply-To: <1293628470-28386-1-git-send-email-kas-GEFAQzZX7r8dnm+yROfE0A@public.gmane.org>

It specifies rpc_pipefs to use. /var/lib/nfs/rpc_pipefs, by default.

Signed-off-by: Kirill A. Shutemov <kas-GEFAQzZX7r8dnm+yROfE0A@public.gmane.org>
---
 fs/nfs/callback.c         |    6 ++--
 fs/nfs/callback.h         |    3 +-
 fs/nfs/client.c           |   46 ++++++++++++++++++++++++++++++++++++--------
 fs/nfs/internal.h         |   10 +++++++-
 fs/nfs/mount_clnt.c       |    3 +-
 fs/nfs/namespace.c        |    3 +-
 fs/nfs/nfs4namespace.c    |   22 +++++++++++---------
 fs/nfs/super.c            |   20 +++++++++++++++++++
 include/linux/nfs_fs_sb.h |    1 +
 9 files changed, 86 insertions(+), 28 deletions(-)

diff --git a/fs/nfs/callback.c b/fs/nfs/callback.c
index bef6abd..ef6d206 100644
--- a/fs/nfs/callback.c
+++ b/fs/nfs/callback.c
@@ -16,7 +16,6 @@
 #include <linux/freezer.h>
 #include <linux/kthread.h>
 #include <linux/sunrpc/svcauth_gss.h>
-#include <linux/sunrpc/rpc_pipe_fs.h>
 #if defined(CONFIG_NFS_V4_1)
 #include <linux/sunrpc/bc_xprt.h>
 #endif
@@ -239,7 +238,8 @@ static inline void nfs_callback_bc_serv(u32 minorversion, struct rpc_xprt *xprt,
 /*
  * Bring up the callback thread if it is not already up.
  */
-int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt)
+int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt,
+		struct vfsmount *rpcmount)
 {
 	struct svc_serv *serv = NULL;
 	struct svc_rqst *rqstp;
@@ -254,7 +254,7 @@ int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt)
 		nfs_callback_bc_serv(minorversion, xprt, cb_info);
 		goto out;
 	}
-	serv = svc_create(&nfs4_callback_program, init_rpc_pipefs,
+	serv = svc_create(&nfs4_callback_program, rpcmount,
 			NFS4_CALLBACK_BUFSIZE, NULL);
 	if (!serv) {
 		ret = -ENOMEM;
diff --git a/fs/nfs/callback.h b/fs/nfs/callback.h
index 85a7cfd..ae27385 100644
--- a/fs/nfs/callback.h
+++ b/fs/nfs/callback.h
@@ -133,7 +133,8 @@ extern __be32 nfs4_callback_getattr(struct cb_getattrargs *args, struct cb_getat
 extern __be32 nfs4_callback_recall(struct cb_recallargs *args, void *dummy);
 
 #ifdef CONFIG_NFS_V4
-extern int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt);
+extern int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt,
+		struct vfsmount *rpcmount);
 extern void nfs_callback_down(int minorversion);
 extern int nfs4_validate_delegation_stateid(struct nfs_delegation *delegation,
 					    const nfs4_stateid *stateid);
diff --git a/fs/nfs/client.c b/fs/nfs/client.c
index fbc013d..ccc400a 100644
--- a/fs/nfs/client.c
+++ b/fs/nfs/client.c
@@ -107,6 +107,7 @@ struct nfs_client_initdata {
 	const struct nfs_rpc_ops *rpc_ops;
 	int proto;
 	u32 minorversion;
+	struct vfsmount *rpcmount;
 };
 
 /*
@@ -143,6 +144,7 @@ static struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_
 	clp->cl_rpcclient = ERR_PTR(-EINVAL);
 
 	clp->cl_proto = cl_init->proto;
+	clp->cl_rpcmount = mntget(cl_init->rpcmount);
 
 #ifdef CONFIG_NFS_V4
 	INIT_LIST_HEAD(&clp->cl_delegations);
@@ -231,6 +233,7 @@ static void nfs_free_client(struct nfs_client *clp)
 	if (clp->cl_machine_cred != NULL)
 		put_rpccred(clp->cl_machine_cred);
 
+	mntput(clp->cl_rpcmount);
 	kfree(clp->cl_hostname);
 	kfree(clp);
 
@@ -457,6 +460,9 @@ static struct nfs_client *nfs_match_client(const struct nfs_client_initdata *dat
 		/* Match the full socket address */
 		if (!nfs_sockaddr_cmp(sap, clap))
 			continue;
+		/* Match rpc_pipefs mount point */
+		if (clp->cl_rpcmount->mnt_sb != data->rpcmount->mnt_sb)
+			continue;
 
 		atomic_inc(&clp->cl_count);
 		return clp;
@@ -615,7 +621,7 @@ static int nfs_create_rpc_client(struct nfs_client *clp,
 		.program	= &nfs_program,
 		.version	= clp->rpc_ops->version,
 		.authflavor	= flavor,
-		.rpcmount	= init_rpc_pipefs,
+		.rpcmount	= clp->cl_rpcmount,
 	};
 
 	if (discrtry)
@@ -650,7 +656,7 @@ static void nfs_destroy_server(struct nfs_server *server)
 /*
  * Version 2 or 3 lockd setup
  */
-static int nfs_start_lockd(struct nfs_server *server)
+static int nfs_start_lockd(struct nfs_server *server, struct vfsmount *rpcmount)
 {
 	struct nlm_host *host;
 	struct nfs_client *clp = server->nfs_client;
@@ -661,7 +667,7 @@ static int nfs_start_lockd(struct nfs_server *server)
 		.nfs_version	= clp->rpc_ops->version,
 		.noresvport	= server->flags & NFS_MOUNT_NORESVPORT ?
 					1 : 0,
-		.rpcmount	= init_rpc_pipefs,
+		.rpcmount	= rpcmount,
 	};
 
 	if (nlm_init.nfs_version > 3)
@@ -809,8 +815,16 @@ static int nfs_init_server(struct nfs_server *server,
 		cl_init.rpc_ops = &nfs_v3_clientops;
 #endif
 
+	cl_init.rpcmount = get_rpc_pipefs(data->rpcmount);
+	if (IS_ERR(cl_init.rpcmount)) {
+		dprintk("<-- nfs_init_server() = error %ld\n",
+				PTR_ERR(cl_init.rpcmount));
+		return PTR_ERR(cl_init.rpcmount);
+	}
+
 	/* Allocate or find a client reference we can use */
 	clp = nfs_get_client(&cl_init);
+	mntput(cl_init.rpcmount);
 	if (IS_ERR(clp)) {
 		dprintk("<-- nfs_init_server() = error %ld\n", PTR_ERR(clp));
 		return PTR_ERR(clp);
@@ -842,7 +856,7 @@ static int nfs_init_server(struct nfs_server *server,
 	server->acdirmax = data->acdirmax * HZ;
 
 	/* Start lockd here, before we might error out */
-	error = nfs_start_lockd(server);
+	error = nfs_start_lockd(server, clp->cl_rpcmount);
 	if (error < 0)
 		goto error;
 
@@ -1144,7 +1158,8 @@ static int nfs4_init_callback(struct nfs_client *clp)
 		}
 
 		error = nfs_callback_up(clp->cl_mvops->minor_version,
-					clp->cl_rpcclient->cl_xprt);
+					clp->cl_rpcclient->cl_xprt,
+					clp->cl_rpcmount);
 		if (error < 0) {
 			dprintk("%s: failed to start callback. Error = %d\n",
 				__func__, error);
@@ -1244,7 +1259,8 @@ static int nfs4_set_client(struct nfs_server *server,
 		const char *ip_addr,
 		rpc_authflavor_t authflavour,
 		int proto, const struct rpc_timeout *timeparms,
-		u32 minorversion)
+		u32 minorversion,
+		struct vfsmount *rpcmount)
 {
 	struct nfs_client_initdata cl_init = {
 		.hostname = hostname,
@@ -1253,6 +1269,7 @@ static int nfs4_set_client(struct nfs_server *server,
 		.rpc_ops = &nfs_v4_clientops,
 		.proto = proto,
 		.minorversion = minorversion,
+		.rpcmount = rpcmount,
 	};
 	struct nfs_client *clp;
 	int error;
@@ -1363,6 +1380,7 @@ static int nfs4_init_server(struct nfs_server *server,
 		const struct nfs_parsed_mount_data *data)
 {
 	struct rpc_timeout timeparms;
+	struct vfsmount *rpcmount;
 	int error;
 
 	dprintk("--> nfs4_init_server()\n");
@@ -1377,6 +1395,11 @@ static int nfs4_init_server(struct nfs_server *server,
 			server->caps |= NFS_CAP_READDIRPLUS;
 	server->options = data->options;
 
+	rpcmount = get_rpc_pipefs(data->rpcmount);
+	if (IS_ERR(rpcmount)) {
+		error = PTR_ERR(rpcmount);
+		goto error;
+	}
 	/* Get a client record */
 	error = nfs4_set_client(server,
 			data->nfs_server.hostname,
@@ -1386,7 +1409,9 @@ static int nfs4_init_server(struct nfs_server *server,
 			data->auth_flavors[0],
 			data->nfs_server.protocol,
 			&timeparms,
-			data->minorversion);
+			data->minorversion,
+			rpcmount);
+	mntput(rpcmount);
 	if (error < 0)
 		goto error;
 
@@ -1476,7 +1501,10 @@ struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *data,
 				data->authflavor,
 				parent_server->client->cl_xprt->prot,
 				parent_server->client->cl_timeout,
-				parent_client->cl_mvops->minor_version);
+				parent_client->cl_mvops->minor_version,
+				parent_client->cl_rpcmount);
+
+
 	if (error < 0)
 		goto error;
 
@@ -1550,7 +1578,7 @@ struct nfs_server *nfs_clone_server(struct nfs_server *source,
 		(unsigned long long) server->fsid.major,
 		(unsigned long long) server->fsid.minor);
 
-	error = nfs_start_lockd(server);
+	error = nfs_start_lockd(server, server->nfs_client->cl_rpcmount);
 	if (error < 0)
 		goto out_free_server;
 
diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h
index e6356b7..cb31fd9 100644
--- a/fs/nfs/internal.h
+++ b/fs/nfs/internal.h
@@ -86,6 +86,7 @@ struct nfs_parsed_mount_data {
 	unsigned int		version;
 	unsigned int		minorversion;
 	char			*fscache_uniq;
+	char			*rpcmount;
 
 	struct {
 		struct sockaddr_storage	address;
@@ -120,6 +121,7 @@ struct nfs_mount_request {
 	int			noresvport;
 	unsigned int		*auth_flav_len;
 	rpc_authflavor_t	*auth_flavs;
+	struct vfsmount		*rpcmount;
 };
 
 extern int nfs_mount(struct nfs_mount_request *info);
@@ -160,10 +162,14 @@ static inline void nfs_fs_proc_exit(void)
 
 /* nfs4namespace.c */
 #ifdef CONFIG_NFS_V4
-extern struct vfsmount *nfs_do_refmount(const struct vfsmount *mnt_parent, struct dentry *dentry);
+extern struct vfsmount *nfs_do_refmount(const struct vfsmount *mnt_parent,
+		struct dentry *dentry,
+		struct vfsmount *rpcmount);
 #else
 static inline
-struct vfsmount *nfs_do_refmount(const struct vfsmount *mnt_parent, struct dentry *dentry)
+struct vfsmount *nfs_do_refmount(const struct vfsmount *mnt_parent,
+		struct dentry *dentry,
+		struct vfsmount *rpcmount)
 {
 	return ERR_PTR(-ENOENT);
 }
diff --git a/fs/nfs/mount_clnt.c b/fs/nfs/mount_clnt.c
index 67b4b8d..9fd4157 100644
--- a/fs/nfs/mount_clnt.c
+++ b/fs/nfs/mount_clnt.c
@@ -13,7 +13,6 @@
 #include <linux/in.h>
 #include <linux/sunrpc/clnt.h>
 #include <linux/sunrpc/sched.h>
-#include <linux/sunrpc/rpc_pipe_fs.h>
 #include <linux/nfs_fs.h>
 #include "internal.h"
 
@@ -162,7 +161,7 @@ int nfs_mount(struct nfs_mount_request *info)
 		.program	= &mnt_program,
 		.version	= info->version,
 		.authflavor	= RPC_AUTH_UNIX,
-		.rpcmount	= init_rpc_pipefs,
+		.rpcmount	= info->rpcmount,
 	};
 	struct rpc_clnt		*mnt_clnt;
 	int			status;
diff --git a/fs/nfs/namespace.c b/fs/nfs/namespace.c
index db6aa36..d47f6f5 100644
--- a/fs/nfs/namespace.c
+++ b/fs/nfs/namespace.c
@@ -135,7 +135,8 @@ static void * nfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd)
 		goto out_err;
 
 	if (fattr->valid & NFS_ATTR_FATTR_V4_REFERRAL)
-		mnt = nfs_do_refmount(nd->path.mnt, nd->path.dentry);
+		mnt = nfs_do_refmount(nd->path.mnt, nd->path.dentry,
+				server->nfs_client->cl_rpcmount);
 	else
 		mnt = nfs_do_submount(nd->path.mnt, nd->path.dentry, fh,
 				      fattr);
diff --git a/fs/nfs/nfs4namespace.c b/fs/nfs/nfs4namespace.c
index 7a61fdb..92d5d63 100644
--- a/fs/nfs/nfs4namespace.c
+++ b/fs/nfs/nfs4namespace.c
@@ -14,7 +14,6 @@
 #include <linux/slab.h>
 #include <linux/string.h>
 #include <linux/sunrpc/clnt.h>
-#include <linux/sunrpc/rpc_pipe_fs.h>
 #include <linux/vfs.h>
 #include <linux/inet.h>
 #include "internal.h"
@@ -99,14 +98,13 @@ static int nfs4_validate_fspath(const struct vfsmount *mnt_parent,
 }
 
 static size_t nfs_parse_server_name(char *string, size_t len,
-		struct sockaddr *sa, size_t salen)
+		struct sockaddr *sa, size_t salen, struct vfsmount *rpcmount)
 {
 	ssize_t ret;
 
 	ret = rpc_pton(string, len, sa, salen);
 	if (ret == 0) {
-		ret = nfs_dns_resolve_name(string, len, sa, salen,
-				init_rpc_pipefs);
+		ret = nfs_dns_resolve_name(string, len, sa, salen, rpcmount);
 		if (ret < 0)
 			ret = 0;
 	}
@@ -115,7 +113,8 @@ static size_t nfs_parse_server_name(char *string, size_t len,
 
 static struct vfsmount *try_location(struct nfs_clone_mount *mountdata,
 				     char *page, char *page2,
-				     const struct nfs4_fs_location *location)
+				     const struct nfs4_fs_location *location,
+				     struct vfsmount *rpcmount)
 {
 	const size_t addr_bufsize = sizeof(struct sockaddr_storage);
 	struct vfsmount *mnt = ERR_PTR(-ENOENT);
@@ -143,7 +142,7 @@ static struct vfsmount *try_location(struct nfs_clone_mount *mountdata,
 			continue;
 
 		mountdata->addrlen = nfs_parse_server_name(buf->data, buf->len,
-				mountdata->addr, addr_bufsize);
+				mountdata->addr, addr_bufsize, rpcmount);
 		if (mountdata->addrlen == 0)
 			continue;
 
@@ -174,7 +173,8 @@ static struct vfsmount *try_location(struct nfs_clone_mount *mountdata,
  */
 static struct vfsmount *nfs_follow_referral(const struct vfsmount *mnt_parent,
 					    const struct dentry *dentry,
-					    const struct nfs4_fs_locations *locations)
+					    const struct nfs4_fs_locations *locations,
+					    struct vfsmount *rpcmount)
 {
 	struct vfsmount *mnt = ERR_PTR(-ENOENT);
 	struct nfs_clone_mount mountdata = {
@@ -213,7 +213,7 @@ static struct vfsmount *nfs_follow_referral(const struct vfsmount *mnt_parent,
 		    location->rootpath.ncomponents == 0)
 			continue;
 
-		mnt = try_location(&mountdata, page, page2, location);
+		mnt = try_location(&mountdata, page, page2, location, rpcmount);
 		if (!IS_ERR(mnt))
 			break;
 	}
@@ -231,7 +231,9 @@ out:
  * @dentry - dentry of referral
  *
  */
-struct vfsmount *nfs_do_refmount(const struct vfsmount *mnt_parent, struct dentry *dentry)
+struct vfsmount *nfs_do_refmount(const struct vfsmount *mnt_parent,
+		struct dentry *dentry,
+		struct vfsmount *rpcmount)
 {
 	struct vfsmount *mnt = ERR_PTR(-ENOMEM);
 	struct dentry *parent;
@@ -264,7 +266,7 @@ struct vfsmount *nfs_do_refmount(const struct vfsmount *mnt_parent, struct dentr
 	    fs_locations->fs_path.ncomponents <= 0)
 		goto out_free;
 
-	mnt = nfs_follow_referral(mnt_parent, dentry, fs_locations);
+	mnt = nfs_follow_referral(mnt_parent, dentry, fs_locations, rpcmount);
 out_free:
 	__free_page(page);
 	kfree(fs_locations);
diff --git a/fs/nfs/super.c b/fs/nfs/super.c
index 4100630..32b7e35 100644
--- a/fs/nfs/super.c
+++ b/fs/nfs/super.c
@@ -35,6 +35,7 @@
 #include <linux/sunrpc/metrics.h>
 #include <linux/sunrpc/xprtsock.h>
 #include <linux/sunrpc/xprtrdma.h>
+#include <linux/sunrpc/rpc_pipe_fs.h>
 #include <linux/nfs_fs.h>
 #include <linux/nfs_mount.h>
 #include <linux/nfs4_mount.h>
@@ -106,6 +107,7 @@ enum {
 	Opt_lookupcache,
 	Opt_fscache_uniq,
 	Opt_local_lock,
+	Opt_rpcmount,
 
 	/* Special mount options */
 	Opt_userspace, Opt_deprecated, Opt_sloppy,
@@ -178,6 +180,7 @@ static const match_table_t nfs_mount_option_tokens = {
 	{ Opt_lookupcache, "lookupcache=%s" },
 	{ Opt_fscache_uniq, "fsc=%s" },
 	{ Opt_local_lock, "local_lock=%s" },
+	{ Opt_rpcmount, "rpcmount=%s" },
 
 	{ Opt_err, NULL }
 };
@@ -1484,6 +1487,13 @@ static int nfs_parse_mount_options(char *raw,
 				return 0;
 			};
 			break;
+		case Opt_rpcmount:
+			string = match_strdup(args);
+			if (string == NULL)
+				goto out_nomem;
+			kfree(mnt->rpcmount);
+			mnt->rpcmount = string;
+			break;
 
 		/*
 		 * Special options
@@ -1644,11 +1654,19 @@ static int nfs_try_mount(struct nfs_parsed_mount_data *args,
 	request.salen = args->mount_server.addrlen;
 	nfs_set_port(request.sap, &args->mount_server.port, 0);
 
+	request.rpcmount = get_rpc_pipefs(args->rpcmount);
+	if (IS_ERR(request.rpcmount)) {
+		dfprintk(MOUNT,	"NFS: unable get rpc_pipefs mount point, "
+				"error %ld\n", PTR_ERR(request.rpcmount));
+		return PTR_ERR(request.rpcmount);
+	}
+
 	/*
 	 * Now ask the mount server to map our export path
 	 * to a file handle.
 	 */
 	status = nfs_mount(&request);
+	mntput(request.rpcmount);
 	if (status != 0) {
 		dfprintk(MOUNT, "NFS: unable to mount server %s, error %d\n",
 				request.hostname, status);
@@ -2352,6 +2370,7 @@ out:
 	kfree(data->nfs_server.hostname);
 	kfree(data->mount_server.hostname);
 	kfree(data->fscache_uniq);
+	kfree(data->rpcmount);
 	security_free_mnt_opts(&data->lsm_opts);
 out_free_fh:
 	nfs_free_fhandle(mntfh);
@@ -2947,6 +2966,7 @@ out:
 	kfree(data->nfs_server.export_path);
 	kfree(data->nfs_server.hostname);
 	kfree(data->fscache_uniq);
+	kfree(data->rpcmount);
 out_free_data:
 	kfree(data);
 	dprintk("<-- nfs4_get_sb() = %d%s\n", error,
diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h
index 452d964..ee417c9 100644
--- a/include/linux/nfs_fs_sb.h
+++ b/include/linux/nfs_fs_sb.h
@@ -36,6 +36,7 @@ struct nfs_client {
 	struct list_head	cl_share_link;	/* link in global client list */
 	struct list_head	cl_superblocks;	/* List of nfs_server structs */
 
+	struct vfsmount		*cl_rpcmount;	/* rpc_pipefs mount point */
 	struct rpc_clnt *	cl_rpcclient;
 	const struct nfs_rpc_ops *rpc_ops;	/* NFS protocol vector */
 	int			cl_proto;	/* Network transport protocol */
-- 
1.7.3.4

--
To unsubscribe from this list: send the line "unsubscribe linux-nfs" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related

* [PATCH v2 10/12] sunrpc: introduce get_rpc_pipefs()
From: Kirill A. Shutemov @ 2010-12-29 13:14 UTC (permalink / raw)
  To: Trond Myklebust, J. Bruce Fields, Neil Brown
  Cc: Pavel Emelyanov, linux-nfs, David S. Miller, netdev, linux-kernel,
	Kirill A. Shutemov
In-Reply-To: <1293628470-28386-1-git-send-email-kas@openvz.org>

Get rpc_pipefs mount point by path.

Signed-off-by: Kirill A. Shutemov <kas@openvz.org>
---
 include/linux/sunrpc/rpc_pipe_fs.h |    2 +
 net/sunrpc/rpc_pipe.c              |   38 ++++++++++++++++++++++++++++++++++++
 2 files changed, 40 insertions(+), 0 deletions(-)

diff --git a/include/linux/sunrpc/rpc_pipe_fs.h b/include/linux/sunrpc/rpc_pipe_fs.h
index b09bfa5..922057c 100644
--- a/include/linux/sunrpc/rpc_pipe_fs.h
+++ b/include/linux/sunrpc/rpc_pipe_fs.h
@@ -46,6 +46,8 @@ RPC_I(struct inode *inode)
 
 extern struct vfsmount *init_rpc_pipefs;
 
+struct vfsmount *get_rpc_pipefs(const char *path);
+
 extern int rpc_queue_upcall(struct inode *, struct rpc_pipe_msg *);
 
 struct rpc_clnt;
diff --git a/net/sunrpc/rpc_pipe.c b/net/sunrpc/rpc_pipe.c
index b1e299b..fec6b2d 100644
--- a/net/sunrpc/rpc_pipe.c
+++ b/net/sunrpc/rpc_pipe.c
@@ -16,6 +16,7 @@
 #include <linux/namei.h>
 #include <linux/fsnotify.h>
 #include <linux/kernel.h>
+#include <linux/nsproxy.h>
 
 #include <asm/ioctls.h>
 #include <linux/fs.h>
@@ -931,6 +932,43 @@ static const struct super_operations s_ops = {
 
 #define RPCAUTH_GSSMAGIC 0x67596969
 
+struct vfsmount *get_rpc_pipefs(const char *p)
+{
+	int error;
+	struct vfsmount *rpcmount;
+	struct path path;
+
+	if (!p) {
+		/* Try to get with default rpcmount mount point */
+		rpcmount = get_rpc_pipefs("/var/lib/nfs/rpc_pipefs");
+
+		/*
+		 * If nothing was found at default mount point and init's
+		 * mount namespace is in use, use init_rpc_pipefs
+		 */
+		if (IS_ERR(rpcmount) &&	(current->nsproxy->mnt_ns ==
+					init_task.nsproxy->mnt_ns))
+			return mntget(init_rpc_pipefs);
+
+		return rpcmount;
+	}
+
+	error = kern_path(p, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &path);
+	if (error)
+		return ERR_PTR(error);
+
+	if (path.mnt->mnt_sb->s_magic != RPCAUTH_GSSMAGIC) {
+		path_put(&path);
+		return ERR_PTR(-EINVAL);
+	}
+
+	rpcmount = mntget(path.mnt);
+	path_put(&path);
+
+	return rpcmount;
+}
+EXPORT_SYMBOL_GPL(get_rpc_pipefs);
+
 /*
  * We have a single directory with 1 node in it.
  */
-- 
1.7.3.4

^ permalink raw reply related

* [PATCH v2 09/12] nfs: per-rpc_pipefs dns cache
From: Kirill A. Shutemov @ 2010-12-29 13:14 UTC (permalink / raw)
  To: Trond Myklebust, J. Bruce Fields, Neil Brown
  Cc: Pavel Emelyanov, linux-nfs-u79uwXL29TY76Z2rM5mHXA,
	David S. Miller, netdev-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, Kirill A. Shutemov
In-Reply-To: <1293628470-28386-1-git-send-email-kas-GEFAQzZX7r8dnm+yROfE0A@public.gmane.org>

Lazy initialization of dns cache: on first call nfs_dns_resolve_name().
Every rpc_pipefs has separate dns cache now.

Signed-off-by: Kirill A. Shutemov <kas-GEFAQzZX7r8dnm+yROfE0A@public.gmane.org>
---
 fs/nfs/cache_lib.c     |   17 ++-----
 fs/nfs/cache_lib.h     |    3 +-
 fs/nfs/dns_resolve.c   |  128 ++++++++++++++++++++++++++++++++++++++----------
 fs/nfs/dns_resolve.h   |    8 +---
 fs/nfs/inode.c         |    8 +---
 fs/nfs/nfs4namespace.c |    4 +-
 6 files changed, 113 insertions(+), 55 deletions(-)

diff --git a/fs/nfs/cache_lib.c b/fs/nfs/cache_lib.c
index 0944d4e..9b99d9e 100644
--- a/fs/nfs/cache_lib.c
+++ b/fs/nfs/cache_lib.c
@@ -12,7 +12,6 @@
 #include <linux/namei.h>
 #include <linux/slab.h>
 #include <linux/sunrpc/cache.h>
-#include <linux/sunrpc/rpc_pipe_fs.h>
 
 #include "cache_lib.h"
 
@@ -111,25 +110,17 @@ int nfs_cache_wait_for_upcall(struct nfs_cache_defer_req *dreq)
 	return 0;
 }
 
-int nfs_cache_register(struct cache_detail *cd)
+int nfs_cache_register(struct cache_detail *cd, struct vfsmount *rpcmount)
 {
 	struct nameidata nd;
-	struct vfsmount *mnt;
 	int ret;
 
-	mnt = mntget(init_rpc_pipefs);
-	if (IS_ERR(mnt))
-		return PTR_ERR(mnt);
-	ret = vfs_path_lookup(mnt->mnt_root, mnt, "/cache", 0, &nd);
+	ret = vfs_path_lookup(rpcmount->mnt_root, rpcmount, "/cache", 0, &nd);
 	if (ret)
-		goto err;
-	ret = sunrpc_cache_register_pipefs(mnt, nd.path.dentry,
+		return ret;
+	ret = sunrpc_cache_register_pipefs(rpcmount, nd.path.dentry,
 			cd->name, 0600, cd);
 	path_put(&nd.path);
-	if (!ret)
-		return ret;
-err:
-	mntput(mnt);
 	return ret;
 }
 
diff --git a/fs/nfs/cache_lib.h b/fs/nfs/cache_lib.h
index 76f856e..1d4a0a5 100644
--- a/fs/nfs/cache_lib.h
+++ b/fs/nfs/cache_lib.h
@@ -23,5 +23,6 @@ extern struct nfs_cache_defer_req *nfs_cache_defer_req_alloc(void);
 extern void nfs_cache_defer_req_put(struct nfs_cache_defer_req *dreq);
 extern int nfs_cache_wait_for_upcall(struct nfs_cache_defer_req *dreq);
 
-extern int nfs_cache_register(struct cache_detail *cd);
+extern int nfs_cache_register(struct cache_detail *cd,
+		struct vfsmount *rpcmount);
 extern void nfs_cache_unregister(struct cache_detail *cd);
diff --git a/fs/nfs/dns_resolve.c b/fs/nfs/dns_resolve.c
index a6e711a..4b35323 100644
--- a/fs/nfs/dns_resolve.c
+++ b/fs/nfs/dns_resolve.c
@@ -12,7 +12,7 @@
 #include <linux/dns_resolver.h>
 
 ssize_t nfs_dns_resolve_name(char *name, size_t namelen,
-		struct sockaddr *sa, size_t salen)
+		struct sockaddr *sa, size_t salen, struct vfsmount *rpcmount)
 {
 	ssize_t ret;
 	char *ip_addr = NULL;
@@ -37,6 +37,7 @@ ssize_t nfs_dns_resolve_name(char *name, size_t namelen,
 #include <linux/socket.h>
 #include <linux/seq_file.h>
 #include <linux/inet.h>
+#include <linux/mount.h>
 #include <linux/sunrpc/clnt.h>
 #include <linux/sunrpc/cache.h>
 #include <linux/sunrpc/svcauth.h>
@@ -47,7 +48,13 @@ ssize_t nfs_dns_resolve_name(char *name, size_t namelen,
 #define NFS_DNS_HASHBITS 4
 #define NFS_DNS_HASHTBL_SIZE (1 << NFS_DNS_HASHBITS)
 
-static struct cache_head *nfs_dns_table[NFS_DNS_HASHTBL_SIZE];
+static DEFINE_SPINLOCK(nfs_dns_resolve_lock);
+static LIST_HEAD(nfs_dns_resolve_list);
+
+struct nfs_dns_resolve_list {
+	struct list_head list;
+	struct cache_detail *cd;
+};
 
 struct nfs_dns_ent {
 	struct cache_head h;
@@ -259,21 +266,6 @@ out:
 	return ret;
 }
 
-static struct cache_detail nfs_dns_resolve = {
-	.owner = THIS_MODULE,
-	.hash_size = NFS_DNS_HASHTBL_SIZE,
-	.hash_table = nfs_dns_table,
-	.name = "dns_resolve",
-	.cache_put = nfs_dns_ent_put,
-	.cache_upcall = nfs_dns_upcall,
-	.cache_parse = nfs_dns_parse,
-	.cache_show = nfs_dns_show,
-	.match = nfs_dns_match,
-	.init = nfs_dns_ent_init,
-	.update = nfs_dns_ent_update,
-	.alloc = nfs_dns_ent_alloc,
-};
-
 static int do_cache_lookup(struct cache_detail *cd,
 		struct nfs_dns_ent *key,
 		struct nfs_dns_ent **item,
@@ -336,37 +328,121 @@ out:
 	return ret;
 }
 
+static struct cache_detail *nfs_alloc_dns_resolve(void)
+{
+	struct cache_detail *dns_resolve;
+	struct cache_head **hash_table;
+
+	dns_resolve = kmalloc(sizeof(*dns_resolve), GFP_KERNEL);
+	if (!dns_resolve)
+		return NULL;
+
+	hash_table = kmalloc(sizeof(*hash_table) * NFS_DNS_HASHTBL_SIZE,
+			GFP_KERNEL);
+	if (!hash_table) {
+		kfree(dns_resolve);
+		return NULL;
+	}
+
+	dns_resolve->owner = THIS_MODULE;
+	dns_resolve->hash_size = NFS_DNS_HASHTBL_SIZE;
+	dns_resolve->hash_table = hash_table;
+	dns_resolve->name = "dns_resolve";
+	dns_resolve->cache_put = nfs_dns_ent_put;
+	dns_resolve->cache_upcall = nfs_dns_upcall;
+	dns_resolve->cache_parse = nfs_dns_parse;
+	dns_resolve->cache_show = nfs_dns_show;
+	dns_resolve->match = nfs_dns_match;
+	dns_resolve->init = nfs_dns_ent_init;
+	dns_resolve->update = nfs_dns_ent_update;
+	dns_resolve->alloc = nfs_dns_ent_alloc;
+
+	return dns_resolve;
+}
+
+static void nfs_free_dns_resolve(struct cache_detail *dns_resolve)
+{
+	kfree(dns_resolve->hash_table);
+	kfree(dns_resolve);
+}
+
+static struct cache_detail *nfs_get_dns_resolve(struct vfsmount *rpcmount)
+{
+	struct nfs_dns_resolve_list *dns_resolve;
+	int error = 0;
+
+	spin_lock(&nfs_dns_resolve_lock);
+	list_for_each_entry(dns_resolve, &nfs_dns_resolve_list, list) {
+		if (dns_resolve->cd->u.pipefs.mnt->mnt_sb != rpcmount->mnt_sb)
+			continue;
+
+		spin_unlock(&nfs_dns_resolve_lock);
+		return dns_resolve->cd;
+	}
+
+	dns_resolve = kmalloc(sizeof(*dns_resolve), GFP_KERNEL);
+	if (dns_resolve)
+		dns_resolve->cd = nfs_alloc_dns_resolve();
+	if (!dns_resolve || !dns_resolve->cd) {
+		error = -ENOMEM;
+		goto err;
+	}
+
+	error = nfs_cache_register(dns_resolve->cd, rpcmount);
+	if (error)
+		goto err;
+
+	INIT_LIST_HEAD(&dns_resolve->list);
+	list_add(&dns_resolve->list, &nfs_dns_resolve_list);
+	spin_unlock(&nfs_dns_resolve_lock);
+
+	return dns_resolve->cd;
+err:
+	spin_unlock(&nfs_dns_resolve_lock);
+	if (dns_resolve)
+		kfree(dns_resolve->cd);
+	kfree(dns_resolve);
+	return dns_resolve->cd;
+}
+
 ssize_t nfs_dns_resolve_name(char *name, size_t namelen,
-		struct sockaddr *sa, size_t salen)
+		struct sockaddr *sa, size_t salen, struct vfsmount *rpcmount)
 {
 	struct nfs_dns_ent key = {
 		.hostname = name,
 		.namelen = namelen,
 	};
+	struct cache_detail *dns_resolve;
 	struct nfs_dns_ent *item = NULL;
 	ssize_t ret;
 
-	ret = do_cache_lookup_wait(&nfs_dns_resolve, &key, &item);
+	dns_resolve = nfs_get_dns_resolve(rpcmount);
+	ret = do_cache_lookup_wait(dns_resolve, &key, &item);
 	if (ret == 0) {
 		if (salen >= item->addrlen) {
 			memcpy(sa, &item->addr, item->addrlen);
 			ret = item->addrlen;
 		} else
 			ret = -EOVERFLOW;
-		cache_put(&item->h, &nfs_dns_resolve);
+		cache_put(&item->h, dns_resolve);
 	} else if (ret == -ENOENT)
 		ret = -ESRCH;
 	return ret;
 }
 
-int nfs_dns_resolver_init(void)
-{
-	return nfs_cache_register(&nfs_dns_resolve);
-}
-
 void nfs_dns_resolver_destroy(void)
 {
-	nfs_cache_unregister(&nfs_dns_resolve);
+	struct nfs_dns_resolve_list *dns_resolve, *tmp;
+
+	spin_lock(&nfs_dns_resolve_lock);
+	list_for_each_entry_safe(dns_resolve, tmp, &nfs_dns_resolve_list,
+			list) {
+		nfs_cache_unregister(dns_resolve->cd);
+		nfs_free_dns_resolve(dns_resolve->cd);
+		list_del(&dns_resolve->list);
+		kfree(dns_resolve);
+	}
+	spin_unlock(&nfs_dns_resolve_lock);
 }
 
 #endif
diff --git a/fs/nfs/dns_resolve.h b/fs/nfs/dns_resolve.h
index 199bb55..a9ae700 100644
--- a/fs/nfs/dns_resolve.h
+++ b/fs/nfs/dns_resolve.h
@@ -8,19 +8,13 @@
 
 
 #ifdef CONFIG_NFS_USE_KERNEL_DNS
-static inline int nfs_dns_resolver_init(void)
-{
-	return 0;
-}
-
 static inline void nfs_dns_resolver_destroy(void)
 {}
 #else
-extern int nfs_dns_resolver_init(void);
 extern void nfs_dns_resolver_destroy(void);
 #endif
 
 extern ssize_t nfs_dns_resolve_name(char *name, size_t namelen,
-		struct sockaddr *sa, size_t salen);
+		struct sockaddr *sa, size_t salen, struct vfsmount *rpcmount);
 
 #endif
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
index e67e31c..9fed17c 100644
--- a/fs/nfs/inode.c
+++ b/fs/nfs/inode.c
@@ -1528,10 +1528,6 @@ static int __init init_nfs_fs(void)
 
 	err = nfs_idmap_init();
 	if (err < 0)
-		goto out9;
-
-	err = nfs_dns_resolver_init();
-	if (err < 0)
 		goto out8;
 
 	err = nfs_fscache_register();
@@ -1592,10 +1588,8 @@ out5:
 out6:
 	nfs_fscache_unregister();
 out7:
-	nfs_dns_resolver_destroy();
-out8:
 	nfs_idmap_quit();
-out9:
+out8:
 	return err;
 }
 
diff --git a/fs/nfs/nfs4namespace.c b/fs/nfs/nfs4namespace.c
index 3c2a172..7a61fdb 100644
--- a/fs/nfs/nfs4namespace.c
+++ b/fs/nfs/nfs4namespace.c
@@ -14,6 +14,7 @@
 #include <linux/slab.h>
 #include <linux/string.h>
 #include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/rpc_pipe_fs.h>
 #include <linux/vfs.h>
 #include <linux/inet.h>
 #include "internal.h"
@@ -104,7 +105,8 @@ static size_t nfs_parse_server_name(char *string, size_t len,
 
 	ret = rpc_pton(string, len, sa, salen);
 	if (ret == 0) {
-		ret = nfs_dns_resolve_name(string, len, sa, salen);
+		ret = nfs_dns_resolve_name(string, len, sa, salen,
+				init_rpc_pipefs);
 		if (ret < 0)
 			ret = 0;
 	}
-- 
1.7.3.4

--
To unsubscribe from this list: send the line "unsubscribe linux-nfs" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related

* [PATCH v2 07/12] sunrpc: get rpc_pipefs mount point for rpcb_create[_local] from callers
From: Kirill A. Shutemov @ 2010-12-29 13:14 UTC (permalink / raw)
  To: Trond Myklebust, J. Bruce Fields, Neil Brown
  Cc: Pavel Emelyanov, linux-nfs, David S. Miller, netdev, linux-kernel,
	Kirill A. Shutemov
In-Reply-To: <1293628470-28386-1-git-send-email-kas@openvz.org>

Signed-off-by: Kirill A. Shutemov <kas@openvz.org>
---
 include/linux/sunrpc/clnt.h |    4 ++--
 net/sunrpc/rpcb_clnt.c      |   22 ++++++++++++----------
 net/sunrpc/svc.c            |   34 +++++++++++++++++++++-------------
 3 files changed, 35 insertions(+), 25 deletions(-)

diff --git a/include/linux/sunrpc/clnt.h b/include/linux/sunrpc/clnt.h
index f052712..59eda38 100644
--- a/include/linux/sunrpc/clnt.h
+++ b/include/linux/sunrpc/clnt.h
@@ -135,10 +135,10 @@ void		rpc_shutdown_client(struct rpc_clnt *);
 void		rpc_release_client(struct rpc_clnt *);
 void		rpc_task_release_client(struct rpc_task *);
 
-int		rpcb_register(u32, u32, int, unsigned short);
+int		rpcb_register(u32, u32, int, unsigned short, struct vfsmount *);
 int		rpcb_v4_register(const u32 program, const u32 version,
 				 const struct sockaddr *address,
-				 const char *netid);
+				 const char *netid, struct vfsmount *rpcmount);
 void		rpcb_getport_async(struct rpc_task *);
 
 void		rpc_call_start(struct rpc_task *);
diff --git a/net/sunrpc/rpcb_clnt.c b/net/sunrpc/rpcb_clnt.c
index 8d04380..867d177 100644
--- a/net/sunrpc/rpcb_clnt.c
+++ b/net/sunrpc/rpcb_clnt.c
@@ -27,7 +27,6 @@
 #include <linux/sunrpc/clnt.h>
 #include <linux/sunrpc/sched.h>
 #include <linux/sunrpc/xprtsock.h>
-#include <linux/sunrpc/rpc_pipe_fs.h>
 
 #ifdef RPC_DEBUG
 # define RPCDBG_FACILITY	RPCDBG_BIND
@@ -175,7 +174,7 @@ static DEFINE_MUTEX(rpcb_create_local_mutex);
  * Returns zero on success, otherwise a negative errno value
  * is returned.
  */
-static int rpcb_create_local(void)
+static int rpcb_create_local(struct vfsmount *rpcmount)
 {
 	struct rpc_create_args args = {
 		.net		= &init_net,
@@ -187,7 +186,7 @@ static int rpcb_create_local(void)
 		.version	= RPCBVERS_2,
 		.authflavor	= RPC_AUTH_UNIX,
 		.flags		= RPC_CLNT_CREATE_NOPING,
-		.rpcmount	= init_rpc_pipefs,
+		.rpcmount	= rpcmount,
 	};
 	struct rpc_clnt *clnt, *clnt4;
 	int result = 0;
@@ -229,7 +228,8 @@ out:
 }
 
 static struct rpc_clnt *rpcb_create(char *hostname, struct sockaddr *srvaddr,
-				    size_t salen, int proto, u32 version)
+				    size_t salen, int proto, u32 version,
+				    struct vfsmount *rpcmount)
 {
 	struct rpc_create_args args = {
 		.net		= &init_net,
@@ -242,7 +242,7 @@ static struct rpc_clnt *rpcb_create(char *hostname, struct sockaddr *srvaddr,
 		.authflavor	= RPC_AUTH_UNIX,
 		.flags		= (RPC_CLNT_CREATE_NOPING |
 					RPC_CLNT_CREATE_NONPRIVPORT),
-		.rpcmount	= init_rpc_pipefs,
+		.rpcmount	= rpcmount,
 	};
 
 	switch (srvaddr->sa_family) {
@@ -309,7 +309,8 @@ static int rpcb_register_call(struct rpc_clnt *clnt, struct rpc_message *msg)
  * IN6ADDR_ANY (ie available for all AF_INET and AF_INET6
  * addresses).
  */
-int rpcb_register(u32 prog, u32 vers, int prot, unsigned short port)
+int rpcb_register(u32 prog, u32 vers, int prot, unsigned short port,
+		struct vfsmount *rpcmount)
 {
 	struct rpcbind_args map = {
 		.r_prog		= prog,
@@ -322,7 +323,7 @@ int rpcb_register(u32 prog, u32 vers, int prot, unsigned short port)
 	};
 	int error;
 
-	error = rpcb_create_local();
+	error = rpcb_create_local(rpcmount);
 	if (error)
 		return error;
 
@@ -449,7 +450,8 @@ static int rpcb_unregister_all_protofamilies(struct rpc_message *msg)
  * advertises the service on all IPv4 and IPv6 addresses.
  */
 int rpcb_v4_register(const u32 program, const u32 version,
-		     const struct sockaddr *address, const char *netid)
+		     const struct sockaddr *address, const char *netid,
+		     struct vfsmount *rpcmount)
 {
 	struct rpcbind_args map = {
 		.r_prog		= program,
@@ -462,7 +464,7 @@ int rpcb_v4_register(const u32 program, const u32 version,
 	};
 	int error;
 
-	error = rpcb_create_local();
+	error = rpcb_create_local(rpcmount);
 	if (error)
 		return error;
 	if (rpcb_local_clnt4 == NULL)
@@ -598,7 +600,7 @@ void rpcb_getport_async(struct rpc_task *task)
 		task->tk_pid, __func__, bind_version);
 
 	rpcb_clnt = rpcb_create(clnt->cl_server, sap, salen, xprt->prot,
-				bind_version);
+				bind_version, clnt->cl_path.mnt);
 	if (IS_ERR(rpcb_clnt)) {
 		status = PTR_ERR(rpcb_clnt);
 		dprintk("RPC: %5u %s: rpcb_create failed, error %ld\n",
diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c
index 0bd6088..e0ae040 100644
--- a/net/sunrpc/svc.c
+++ b/net/sunrpc/svc.c
@@ -743,7 +743,8 @@ EXPORT_SYMBOL_GPL(svc_exit_thread);
  */
 static int __svc_rpcb_register4(const u32 program, const u32 version,
 				const unsigned short protocol,
-				const unsigned short port)
+				const unsigned short port,
+				struct vfsmount *rpcmount)
 {
 	const struct sockaddr_in sin = {
 		.sin_family		= AF_INET,
@@ -765,14 +766,16 @@ static int __svc_rpcb_register4(const u32 program, const u32 version,
 	}
 
 	error = rpcb_v4_register(program, version,
-					(const struct sockaddr *)&sin, netid);
+					(const struct sockaddr *)&sin, netid,
+					rpcmount);
 
 	/*
 	 * User space didn't support rpcbind v4, so retry this
 	 * registration request with the legacy rpcbind v2 protocol.
 	 */
 	if (error == -EPROTONOSUPPORT)
-		error = rpcb_register(program, version, protocol, port);
+		error = rpcb_register(program, version, protocol, port,
+				rpcmount);
 
 	return error;
 }
@@ -790,7 +793,8 @@ static int __svc_rpcb_register4(const u32 program, const u32 version,
  */
 static int __svc_rpcb_register6(const u32 program, const u32 version,
 				const unsigned short protocol,
-				const unsigned short port)
+				const unsigned short port,
+				struct vfsmount *rpcmount)
 {
 	const struct sockaddr_in6 sin6 = {
 		.sin6_family		= AF_INET6,
@@ -812,7 +816,8 @@ static int __svc_rpcb_register6(const u32 program, const u32 version,
 	}
 
 	error = rpcb_v4_register(program, version,
-					(const struct sockaddr *)&sin6, netid);
+					(const struct sockaddr *)&sin6, netid,
+					rpcmount);
 
 	/*
 	 * User space didn't support rpcbind version 4, so we won't
@@ -835,19 +840,20 @@ static int __svc_register(const char *progname,
 			  const u32 program, const u32 version,
 			  const int family,
 			  const unsigned short protocol,
-			  const unsigned short port)
+			  const unsigned short port,
+			  struct vfsmount *rpcmount)
 {
 	int error = -EAFNOSUPPORT;
 
 	switch (family) {
 	case PF_INET:
 		error = __svc_rpcb_register4(program, version,
-						protocol, port);
+						protocol, port, rpcmount);
 		break;
 #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
 	case PF_INET6:
 		error = __svc_rpcb_register6(program, version,
-						protocol, port);
+						protocol, port, rpcmount);
 #endif	/* defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) */
 	}
 
@@ -893,7 +899,8 @@ int svc_register(const struct svc_serv *serv, const int family,
 				continue;
 
 			error = __svc_register(progp->pg_name, progp->pg_prog,
-						i, family, proto, port);
+						i, family, proto, port,
+						serv->sv_rpcmount);
 			if (error < 0)
 				break;
 		}
@@ -910,18 +917,18 @@ int svc_register(const struct svc_serv *serv, const int family,
  * in this case to clear all existing entries for [program, version].
  */
 static void __svc_unregister(const u32 program, const u32 version,
-			     const char *progname)
+			     const char *progname, struct vfsmount *rpcmount)
 {
 	int error;
 
-	error = rpcb_v4_register(program, version, NULL, "");
+	error = rpcb_v4_register(program, version, NULL, "", rpcmount);
 
 	/*
 	 * User space didn't support rpcbind v4, so retry this
 	 * request with the legacy rpcbind v2 protocol.
 	 */
 	if (error == -EPROTONOSUPPORT)
-		error = rpcb_register(program, version, 0, 0);
+		error = rpcb_register(program, version, 0, 0, rpcmount);
 
 	dprintk("svc: %s(%sv%u), error %d\n",
 			__func__, progname, version, error);
@@ -950,7 +957,8 @@ static void svc_unregister(const struct svc_serv *serv)
 			if (progp->pg_vers[i]->vs_hidden)
 				continue;
 
-			__svc_unregister(progp->pg_prog, i, progp->pg_name);
+			__svc_unregister(progp->pg_prog, i, progp->pg_name,
+					serv->sv_rpcmount);
 		}
 	}
 
-- 
1.7.3.4

^ permalink raw reply related

* [PATCH v2 06/12] lockd: get rpc_pipefs mount point from callers
From: Kirill A. Shutemov @ 2010-12-29 13:14 UTC (permalink / raw)
  To: Trond Myklebust, J. Bruce Fields, Neil Brown
  Cc: Pavel Emelyanov, linux-nfs, David S. Miller, netdev, linux-kernel,
	Kirill A. Shutemov
In-Reply-To: <1293628470-28386-1-git-send-email-kas@openvz.org>

Signed-off-by: Kirill A. Shutemov <kas@openvz.org>
---
 fs/lockd/clntlock.c         |    8 +++++---
 fs/lockd/host.c             |   14 +++++++++++---
 fs/lockd/mon.c              |   15 ++++++++-------
 fs/lockd/svc.c              |    6 ++----
 fs/nfs/client.c             |    1 +
 fs/nfsd/nfssvc.c            |    2 +-
 include/linux/lockd/bind.h  |    3 ++-
 include/linux/lockd/lockd.h |    4 +++-
 8 files changed, 33 insertions(+), 20 deletions(-)

diff --git a/fs/lockd/clntlock.c b/fs/lockd/clntlock.c
index 25509eb..1179c18 100644
--- a/fs/lockd/clntlock.c
+++ b/fs/lockd/clntlock.c
@@ -56,13 +56,14 @@ struct nlm_host *nlmclnt_init(const struct nlmclnt_initdata *nlm_init)
 	u32 nlm_version = (nlm_init->nfs_version == 2) ? 1 : 4;
 	int status;
 
-	status = lockd_up();
+	status = lockd_up(nlm_init->rpcmount);
 	if (status < 0)
 		return ERR_PTR(status);
 
 	host = nlmclnt_lookup_host(nlm_init->address, nlm_init->addrlen,
 				   nlm_init->protocol, nlm_version,
-				   nlm_init->hostname, nlm_init->noresvport);
+				   nlm_init->hostname, nlm_init->noresvport,
+				   nlm_init->rpcmount);
 	if (host == NULL) {
 		lockd_down();
 		return ERR_PTR(-ENOLCK);
@@ -223,7 +224,8 @@ reclaimer(void *ptr)
 	allow_signal(SIGKILL);
 
 	down_write(&host->h_rwsem);
-	lockd_up();	/* note: this cannot fail as lockd is already running */
+	/* note: this cannot fail as lockd is already running */
+	lockd_up(host->h_rpcmount);
 
 	dprintk("lockd: reclaiming locks for host %s\n", host->h_name);
 
diff --git a/fs/lockd/host.c b/fs/lockd/host.c
index b033a2d..757d1d3 100644
--- a/fs/lockd/host.c
+++ b/fs/lockd/host.c
@@ -14,9 +14,9 @@
 #include <linux/in6.h>
 #include <linux/sunrpc/clnt.h>
 #include <linux/sunrpc/svc.h>
-#include <linux/sunrpc/rpc_pipe_fs.h>
 #include <linux/lockd/lockd.h>
 #include <linux/mutex.h>
+#include <linux/mount.h>
 
 #include <net/ipv6.h>
 
@@ -44,6 +44,7 @@ struct nlm_lookup_host_info {
 	const struct sockaddr	*src_sap;	/* our address (optional) */
 	const size_t		src_len;	/* it's length */
 	const int		noresvport;	/* use non-priv port */
+	struct vfsmount		*rpcmount;	/* rpc_pipefs mount point */
 };
 
 /*
@@ -128,6 +129,8 @@ static struct nlm_host *nlm_lookup_host(struct nlm_lookup_host_info *ni)
 		if (ni->server && ni->src_len != 0 &&
 		    !rpc_cmp_addr(nlm_srcaddr(host), ni->src_sap))
 			continue;
+		if (host->h_rpcmount->mnt_sb != ni->rpcmount->mnt_sb)
+			continue;
 
 		/* Move to head of hash chain. */
 		hlist_del(&host->h_hash);
@@ -171,6 +174,7 @@ static struct nlm_host *nlm_lookup_host(struct nlm_lookup_host_info *ni)
 	host->h_srcaddrlen = ni->src_len;
 	host->h_version    = ni->version;
 	host->h_proto      = ni->protocol;
+	host->h_rpcmount   = mntget(ni->rpcmount);
 	host->h_rpcclnt    = NULL;
 	mutex_init(&host->h_mutex);
 	host->h_nextrebind = jiffies + NLM_HOST_REBIND;
@@ -212,6 +216,7 @@ nlm_destroy_host(struct nlm_host *host)
 
 	nsm_unmonitor(host);
 	nsm_release(host->h_nsmhandle);
+	mntput(host->h_rpcmount);
 
 	clnt = host->h_rpcclnt;
 	if (clnt != NULL)
@@ -238,7 +243,8 @@ struct nlm_host *nlmclnt_lookup_host(const struct sockaddr *sap,
 				     const unsigned short protocol,
 				     const u32 version,
 				     const char *hostname,
-				     int noresvport)
+				     int noresvport,
+				     struct vfsmount *rpcmount)
 {
 	struct nlm_lookup_host_info ni = {
 		.server		= 0,
@@ -249,6 +255,7 @@ struct nlm_host *nlmclnt_lookup_host(const struct sockaddr *sap,
 		.hostname	= hostname,
 		.hostname_len	= strlen(hostname),
 		.noresvport	= noresvport,
+		.rpcmount	= rpcmount,
 	};
 
 	dprintk("lockd: %s(host='%s', vers=%u, proto=%s)\n", __func__,
@@ -295,6 +302,7 @@ struct nlm_host *nlmsvc_lookup_host(const struct svc_rqst *rqstp,
 		.hostname	= hostname,
 		.hostname_len	= hostname_len,
 		.src_len	= rqstp->rq_addrlen,
+		.rpcmount	= rqstp->rq_server->sv_rpcmount,
 	};
 
 	dprintk("lockd: %s(host='%*s', vers=%u, proto=%s)\n", __func__,
@@ -361,7 +369,7 @@ nlm_bind_host(struct nlm_host *host)
 			.authflavor	= RPC_AUTH_UNIX,
 			.flags		= (RPC_CLNT_CREATE_NOPING |
 					   RPC_CLNT_CREATE_AUTOBIND),
-			.rpcmount	= init_rpc_pipefs,
+			.rpcmount	= host->h_rpcmount,
 		};
 
 		/*
diff --git a/fs/lockd/mon.c b/fs/lockd/mon.c
index 37e5328..526d486 100644
--- a/fs/lockd/mon.c
+++ b/fs/lockd/mon.c
@@ -15,7 +15,6 @@
 #include <linux/sunrpc/clnt.h>
 #include <linux/sunrpc/xprtsock.h>
 #include <linux/sunrpc/svc.h>
-#include <linux/sunrpc/rpc_pipe_fs.h>
 #include <linux/lockd/lockd.h>
 
 #include <asm/unaligned.h>
@@ -63,7 +62,7 @@ static inline struct sockaddr *nsm_addr(const struct nsm_handle *nsm)
 	return (struct sockaddr *)&nsm->sm_addr;
 }
 
-static struct rpc_clnt *nsm_create(void)
+static struct rpc_clnt *nsm_create(struct vfsmount *rpcmount)
 {
 	struct sockaddr_in sin = {
 		.sin_family		= AF_INET,
@@ -79,13 +78,14 @@ static struct rpc_clnt *nsm_create(void)
 		.version		= NSM_VERSION,
 		.authflavor		= RPC_AUTH_NULL,
 		.flags			= RPC_CLNT_CREATE_NOPING,
-		.rpcmount		= init_rpc_pipefs,
+		.rpcmount		= rpcmount,
 	};
 
 	return rpc_create(&args);
 }
 
-static int nsm_mon_unmon(struct nsm_handle *nsm, u32 proc, struct nsm_res *res)
+static int nsm_mon_unmon(struct nsm_handle *nsm, u32 proc, struct nsm_res *res,
+		struct vfsmount *rpcmount)
 {
 	struct rpc_clnt	*clnt;
 	int		status;
@@ -101,7 +101,7 @@ static int nsm_mon_unmon(struct nsm_handle *nsm, u32 proc, struct nsm_res *res)
 		.rpc_resp	= res,
 	};
 
-	clnt = nsm_create();
+	clnt = nsm_create(rpcmount);
 	if (IS_ERR(clnt)) {
 		status = PTR_ERR(clnt);
 		dprintk("lockd: failed to create NSM upcall transport, "
@@ -151,7 +151,7 @@ int nsm_monitor(const struct nlm_host *host)
 	 */
 	nsm->sm_mon_name = nsm_use_hostnames ? nsm->sm_name : nsm->sm_addrbuf;
 
-	status = nsm_mon_unmon(nsm, NSMPROC_MON, &res);
+	status = nsm_mon_unmon(nsm, NSMPROC_MON, &res, host->h_rpcmount);
 	if (unlikely(res.status != 0))
 		status = -EIO;
 	if (unlikely(status < 0)) {
@@ -185,7 +185,8 @@ void nsm_unmonitor(const struct nlm_host *host)
 	 && nsm->sm_monitored && !nsm->sm_sticky) {
 		dprintk("lockd: nsm_unmonitor(%s)\n", nsm->sm_name);
 
-		status = nsm_mon_unmon(nsm, NSMPROC_UNMON, &res);
+		status = nsm_mon_unmon(nsm, NSMPROC_UNMON, &res,
+				host->h_rpcmount);
 		if (res.status != 0)
 			status = -EIO;
 		if (status < 0)
diff --git a/fs/lockd/svc.c b/fs/lockd/svc.c
index 32310b1..7387b04 100644
--- a/fs/lockd/svc.c
+++ b/fs/lockd/svc.c
@@ -31,7 +31,6 @@
 #include <linux/sunrpc/clnt.h>
 #include <linux/sunrpc/svc.h>
 #include <linux/sunrpc/svcsock.h>
-#include <linux/sunrpc/rpc_pipe_fs.h>
 #include <net/ip.h>
 #include <linux/lockd/lockd.h>
 #include <linux/nfs.h>
@@ -249,7 +248,7 @@ out_err:
 /*
  * Bring up the lockd process if it's not already up.
  */
-int lockd_up(void)
+int lockd_up(struct vfsmount *rpcmount)
 {
 	struct svc_serv *serv;
 	int		error = 0;
@@ -270,8 +269,7 @@ int lockd_up(void)
 			"lockd_up: no pid, %d users??\n", nlmsvc_users);
 
 	error = -ENOMEM;
-	serv = svc_create(&nlmsvc_program, init_rpc_pipefs, LOCKD_BUFSIZE,
-			NULL);
+	serv = svc_create(&nlmsvc_program, rpcmount, LOCKD_BUFSIZE, NULL);
 	if (!serv) {
 		printk(KERN_WARNING "lockd_up: create service failed\n");
 		goto out;
diff --git a/fs/nfs/client.c b/fs/nfs/client.c
index e041f39..fbc013d 100644
--- a/fs/nfs/client.c
+++ b/fs/nfs/client.c
@@ -661,6 +661,7 @@ static int nfs_start_lockd(struct nfs_server *server)
 		.nfs_version	= clp->rpc_ops->version,
 		.noresvport	= server->flags & NFS_MOUNT_NORESVPORT ?
 					1 : 0,
+		.rpcmount	= init_rpc_pipefs,
 	};
 
 	if (nlm_init.nfs_version > 3)
diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c
index d96c32b..17d78d3 100644
--- a/fs/nfsd/nfssvc.c
+++ b/fs/nfsd/nfssvc.c
@@ -220,7 +220,7 @@ static int nfsd_startup(unsigned short port, int nrservs)
 	ret = nfsd_init_socks(port);
 	if (ret)
 		goto out_racache;
-	ret = lockd_up();
+	ret = lockd_up(init_rpc_pipefs);
 	if (ret)
 		goto out_racache;
 	ret = nfs4_state_start();
diff --git a/include/linux/lockd/bind.h b/include/linux/lockd/bind.h
index fbc48f8..97cd4bf 100644
--- a/include/linux/lockd/bind.h
+++ b/include/linux/lockd/bind.h
@@ -42,6 +42,7 @@ struct nlmclnt_initdata {
 	unsigned short		protocol;
 	u32			nfs_version;
 	int			noresvport;
+	struct vfsmount		*rpcmount;
 };
 
 /*
@@ -53,7 +54,7 @@ extern void	nlmclnt_done(struct nlm_host *host);
 
 extern int	nlmclnt_proc(struct nlm_host *host, int cmd,
 					struct file_lock *fl);
-extern int	lockd_up(void);
+extern int	lockd_up(struct vfsmount *rpcmount);
 extern void	lockd_down(void);
 
 #endif /* LINUX_LOCKD_BIND_H */
diff --git a/include/linux/lockd/lockd.h b/include/linux/lockd/lockd.h
index 2dee05e..e30b07d 100644
--- a/include/linux/lockd/lockd.h
+++ b/include/linux/lockd/lockd.h
@@ -44,6 +44,7 @@ struct nlm_host {
 	size_t			h_addrlen;
 	struct sockaddr_storage	h_srcaddr;	/* our address (optional) */
 	size_t			h_srcaddrlen;
+	struct vfsmount		*h_rpcmount;	/* rpc_pipefs mount point */
 	struct rpc_clnt		*h_rpcclnt;	/* RPC client to talk to peer */
 	char			*h_name;		/* remote hostname */
 	u32			h_version;	/* interface version */
@@ -222,7 +223,8 @@ struct nlm_host  *nlmclnt_lookup_host(const struct sockaddr *sap,
 					const unsigned short protocol,
 					const u32 version,
 					const char *hostname,
-					int noresvport);
+					int noresvport,
+					struct vfsmount *rpcmount);
 struct nlm_host  *nlmsvc_lookup_host(const struct svc_rqst *rqstp,
 					const char *hostname,
 					const size_t hostname_len);
-- 
1.7.3.4

^ permalink raw reply related

* [PATCH v2 05/12] sunrpc: get rpc_pipefs mount point for svc_serv from callers
From: Kirill A. Shutemov @ 2010-12-29 13:14 UTC (permalink / raw)
  To: Trond Myklebust, J. Bruce Fields, Neil Brown
  Cc: Pavel Emelyanov, linux-nfs, David S. Miller, netdev, linux-kernel,
	Kirill A. Shutemov
In-Reply-To: <1293628470-28386-1-git-send-email-kas@openvz.org>

Signed-off-by: Kirill A. Shutemov <kas@openvz.org>
---
 fs/lockd/svc.c             |    4 +++-
 fs/nfs/callback.c          |    4 +++-
 fs/nfsd/nfssvc.c           |    6 ++++--
 include/linux/sunrpc/svc.h |    8 ++++----
 net/sunrpc/svc.c           |   18 +++++++++---------
 5 files changed, 23 insertions(+), 17 deletions(-)

diff --git a/fs/lockd/svc.c b/fs/lockd/svc.c
index abfff9d..32310b1 100644
--- a/fs/lockd/svc.c
+++ b/fs/lockd/svc.c
@@ -31,6 +31,7 @@
 #include <linux/sunrpc/clnt.h>
 #include <linux/sunrpc/svc.h>
 #include <linux/sunrpc/svcsock.h>
+#include <linux/sunrpc/rpc_pipe_fs.h>
 #include <net/ip.h>
 #include <linux/lockd/lockd.h>
 #include <linux/nfs.h>
@@ -269,7 +270,8 @@ int lockd_up(void)
 			"lockd_up: no pid, %d users??\n", nlmsvc_users);
 
 	error = -ENOMEM;
-	serv = svc_create(&nlmsvc_program, LOCKD_BUFSIZE, NULL);
+	serv = svc_create(&nlmsvc_program, init_rpc_pipefs, LOCKD_BUFSIZE,
+			NULL);
 	if (!serv) {
 		printk(KERN_WARNING "lockd_up: create service failed\n");
 		goto out;
diff --git a/fs/nfs/callback.c b/fs/nfs/callback.c
index 93a8b3b..bef6abd 100644
--- a/fs/nfs/callback.c
+++ b/fs/nfs/callback.c
@@ -16,6 +16,7 @@
 #include <linux/freezer.h>
 #include <linux/kthread.h>
 #include <linux/sunrpc/svcauth_gss.h>
+#include <linux/sunrpc/rpc_pipe_fs.h>
 #if defined(CONFIG_NFS_V4_1)
 #include <linux/sunrpc/bc_xprt.h>
 #endif
@@ -253,7 +254,8 @@ int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt)
 		nfs_callback_bc_serv(minorversion, xprt, cb_info);
 		goto out;
 	}
-	serv = svc_create(&nfs4_callback_program, NFS4_CALLBACK_BUFSIZE, NULL);
+	serv = svc_create(&nfs4_callback_program, init_rpc_pipefs,
+			NFS4_CALLBACK_BUFSIZE, NULL);
 	if (!serv) {
 		ret = -ENOMEM;
 		goto out_err;
diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c
index 2bae1d8..d96c32b 100644
--- a/fs/nfsd/nfssvc.c
+++ b/fs/nfsd/nfssvc.c
@@ -13,6 +13,7 @@
 
 #include <linux/sunrpc/stats.h>
 #include <linux/sunrpc/svcsock.h>
+#include <linux/sunrpc/rpc_pipe_fs.h>
 #include <linux/lockd/bind.h>
 #include <linux/nfsacl.h>
 #include <linux/seq_file.h>
@@ -331,8 +332,9 @@ int nfsd_create_serv(void)
 	}
 	nfsd_reset_versions();
 
-	nfsd_serv = svc_create_pooled(&nfsd_program, nfsd_max_blksize,
-				      nfsd_last_thread, nfsd, THIS_MODULE);
+	nfsd_serv = svc_create_pooled(&nfsd_program, init_rpc_pipefs,
+				      nfsd_max_blksize, nfsd_last_thread, nfsd,
+				      THIS_MODULE);
 	if (nfsd_serv == NULL)
 		return -ENOMEM;
 
diff --git a/include/linux/sunrpc/svc.h b/include/linux/sunrpc/svc.h
index 3b6b26c..7f09411 100644
--- a/include/linux/sunrpc/svc.h
+++ b/include/linux/sunrpc/svc.h
@@ -400,13 +400,13 @@ struct svc_procedure {
 /*
  * Function prototypes.
  */
-struct svc_serv *svc_create(struct svc_program *, unsigned int,
-			    void (*shutdown)(struct svc_serv *));
+struct svc_serv *svc_create(struct svc_program *, struct vfsmount *,
+			    unsigned int, void (*shutdown)(struct svc_serv *));
 struct svc_rqst *svc_prepare_thread(struct svc_serv *serv,
 					struct svc_pool *pool);
 void		   svc_exit_thread(struct svc_rqst *);
-struct svc_serv *  svc_create_pooled(struct svc_program *, unsigned int,
-			void (*shutdown)(struct svc_serv *),
+struct svc_serv *  svc_create_pooled(struct svc_program *, struct vfsmount *,
+			unsigned int, void (*shutdown)(struct svc_serv *),
 			svc_thread_fn, struct module *);
 int		   svc_set_num_threads(struct svc_serv *, struct svc_pool *, int);
 int		   svc_pool_stats_open(struct svc_serv *serv, struct file *file);
diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c
index d2f7c03..0bd6088 100644
--- a/net/sunrpc/svc.c
+++ b/net/sunrpc/svc.c
@@ -28,7 +28,6 @@
 #include <linux/sunrpc/svcsock.h>
 #include <linux/sunrpc/clnt.h>
 #include <linux/sunrpc/bc_xprt.h>
-#include <linux/sunrpc/rpc_pipe_fs.h>
 
 #define RPCDBG_FACILITY	RPCDBG_SVCDSP
 
@@ -361,7 +360,8 @@ svc_pool_for_cpu(struct svc_serv *serv, int cpu)
  * Create an RPC service
  */
 static struct svc_serv *
-__svc_create(struct svc_program *prog, unsigned int bufsize, int npools,
+__svc_create(struct svc_program *prog, struct vfsmount *rpcmount,
+	     unsigned int bufsize, int npools,
 	     void (*shutdown)(struct svc_serv *serv))
 {
 	struct svc_serv	*serv;
@@ -373,7 +373,7 @@ __svc_create(struct svc_program *prog, unsigned int bufsize, int npools,
 		return NULL;
 	serv->sv_name      = prog->pg_name;
 	serv->sv_program   = prog;
-	serv->sv_rpcmount  = mntget(init_rpc_pipefs);
+	serv->sv_rpcmount  = mntget(rpcmount);
 	serv->sv_nrthreads = 1;
 	serv->sv_stats     = prog->pg_stats;
 	if (bufsize > RPCSVC_MAXPAYLOAD)
@@ -429,22 +429,22 @@ __svc_create(struct svc_program *prog, unsigned int bufsize, int npools,
 }
 
 struct svc_serv *
-svc_create(struct svc_program *prog, unsigned int bufsize,
-	   void (*shutdown)(struct svc_serv *serv))
+svc_create(struct svc_program *prog, struct vfsmount *rpcmount,
+	   unsigned int bufsize, void (*shutdown)(struct svc_serv *serv))
 {
-	return __svc_create(prog, bufsize, /*npools*/1, shutdown);
+	return __svc_create(prog, rpcmount, bufsize, /*npools*/1, shutdown);
 }
 EXPORT_SYMBOL_GPL(svc_create);
 
 struct svc_serv *
-svc_create_pooled(struct svc_program *prog, unsigned int bufsize,
-		  void (*shutdown)(struct svc_serv *serv),
+svc_create_pooled(struct svc_program *prog, struct vfsmount *rpcmount,
+		  unsigned int bufsize, void (*shutdown)(struct svc_serv *serv),
 		  svc_thread_fn func, struct module *mod)
 {
 	struct svc_serv *serv;
 	unsigned int npools = svc_pool_map_get();
 
-	serv = __svc_create(prog, bufsize, npools, shutdown);
+	serv = __svc_create(prog, rpcmount, bufsize, npools, shutdown);
 
 	if (serv != NULL) {
 		serv->sv_function = func;
-- 
1.7.3.4

^ permalink raw reply related

* [PATCH v2 04/12] sunrpc: tag svc_serv with rpc_pipefs mount point
From: Kirill A. Shutemov @ 2010-12-29 13:14 UTC (permalink / raw)
  To: Trond Myklebust, J. Bruce Fields, Neil Brown
  Cc: Pavel Emelyanov, linux-nfs, David S. Miller, netdev, linux-kernel,
	Kirill A. Shutemov
In-Reply-To: <1293628470-28386-1-git-send-email-kas@openvz.org>

Signed-off-by: Kirill A. Shutemov <kas@openvz.org>
---
 include/linux/sunrpc/svc.h |    1 +
 net/sunrpc/svc.c           |    4 ++++
 2 files changed, 5 insertions(+), 0 deletions(-)

diff --git a/include/linux/sunrpc/svc.h b/include/linux/sunrpc/svc.h
index 5a3085b..3b6b26c 100644
--- a/include/linux/sunrpc/svc.h
+++ b/include/linux/sunrpc/svc.h
@@ -64,6 +64,7 @@ struct svc_pool {
  */
 struct svc_serv {
 	struct svc_program *	sv_program;	/* RPC program */
+	struct vfsmount *	sv_rpcmount;	/* rpc_pipefs mount point*/
 	struct svc_stat *	sv_stats;	/* RPC statistics */
 	spinlock_t		sv_lock;
 	unsigned int		sv_nrthreads;	/* # of server threads */
diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c
index 6359c42..d2f7c03 100644
--- a/net/sunrpc/svc.c
+++ b/net/sunrpc/svc.c
@@ -20,6 +20,7 @@
 #include <linux/module.h>
 #include <linux/kthread.h>
 #include <linux/slab.h>
+#include <linux/mount.h>
 
 #include <linux/sunrpc/types.h>
 #include <linux/sunrpc/xdr.h>
@@ -27,6 +28,7 @@
 #include <linux/sunrpc/svcsock.h>
 #include <linux/sunrpc/clnt.h>
 #include <linux/sunrpc/bc_xprt.h>
+#include <linux/sunrpc/rpc_pipe_fs.h>
 
 #define RPCDBG_FACILITY	RPCDBG_SVCDSP
 
@@ -371,6 +373,7 @@ __svc_create(struct svc_program *prog, unsigned int bufsize, int npools,
 		return NULL;
 	serv->sv_name      = prog->pg_name;
 	serv->sv_program   = prog;
+	serv->sv_rpcmount  = mntget(init_rpc_pipefs);
 	serv->sv_nrthreads = 1;
 	serv->sv_stats     = prog->pg_stats;
 	if (bufsize > RPCSVC_MAXPAYLOAD)
@@ -492,6 +495,7 @@ svc_destroy(struct svc_serv *serv)
 	svc_sock_destroy(serv->bc_xprt);
 #endif /* CONFIG_NFS_V4_1 */
 
+	mntput(serv->sv_rpcmount);
 	svc_unregister(serv);
 	kfree(serv->sv_pools);
 	kfree(serv);
-- 
1.7.3.4

^ permalink raw reply related

* [PATCH v2 03/12] sunrpc: push init_rpc_pipefs up to rpc_create() callers
From: Kirill A. Shutemov @ 2010-12-29 13:14 UTC (permalink / raw)
  To: Trond Myklebust, J. Bruce Fields, Neil Brown
  Cc: Pavel Emelyanov, linux-nfs, David S. Miller, netdev, linux-kernel,
	Kirill A. Shutemov
In-Reply-To: <1293628470-28386-1-git-send-email-kas@openvz.org>

Signed-off-by: Kirill A. Shutemov <kas@openvz.org>
---
 fs/lockd/host.c             |    2 ++
 fs/lockd/mon.c              |    2 ++
 fs/nfs/client.c             |    2 ++
 fs/nfs/mount_clnt.c         |    2 ++
 fs/nfsd/nfs4callback.c      |    2 ++
 include/linux/sunrpc/clnt.h |    1 +
 net/sunrpc/clnt.c           |   11 +++++++----
 net/sunrpc/rpcb_clnt.c      |    3 +++
 8 files changed, 21 insertions(+), 4 deletions(-)

diff --git a/fs/lockd/host.c b/fs/lockd/host.c
index ed0c59f..b033a2d 100644
--- a/fs/lockd/host.c
+++ b/fs/lockd/host.c
@@ -14,6 +14,7 @@
 #include <linux/in6.h>
 #include <linux/sunrpc/clnt.h>
 #include <linux/sunrpc/svc.h>
+#include <linux/sunrpc/rpc_pipe_fs.h>
 #include <linux/lockd/lockd.h>
 #include <linux/mutex.h>
 
@@ -360,6 +361,7 @@ nlm_bind_host(struct nlm_host *host)
 			.authflavor	= RPC_AUTH_UNIX,
 			.flags		= (RPC_CLNT_CREATE_NOPING |
 					   RPC_CLNT_CREATE_AUTOBIND),
+			.rpcmount	= init_rpc_pipefs,
 		};
 
 		/*
diff --git a/fs/lockd/mon.c b/fs/lockd/mon.c
index e0c9189..37e5328 100644
--- a/fs/lockd/mon.c
+++ b/fs/lockd/mon.c
@@ -15,6 +15,7 @@
 #include <linux/sunrpc/clnt.h>
 #include <linux/sunrpc/xprtsock.h>
 #include <linux/sunrpc/svc.h>
+#include <linux/sunrpc/rpc_pipe_fs.h>
 #include <linux/lockd/lockd.h>
 
 #include <asm/unaligned.h>
@@ -78,6 +79,7 @@ static struct rpc_clnt *nsm_create(void)
 		.version		= NSM_VERSION,
 		.authflavor		= RPC_AUTH_NULL,
 		.flags			= RPC_CLNT_CREATE_NOPING,
+		.rpcmount		= init_rpc_pipefs,
 	};
 
 	return rpc_create(&args);
diff --git a/fs/nfs/client.c b/fs/nfs/client.c
index 0870d0d..e041f39 100644
--- a/fs/nfs/client.c
+++ b/fs/nfs/client.c
@@ -25,6 +25,7 @@
 #include <linux/sunrpc/metrics.h>
 #include <linux/sunrpc/xprtsock.h>
 #include <linux/sunrpc/xprtrdma.h>
+#include <linux/sunrpc/rpc_pipe_fs.h>
 #include <linux/nfs_fs.h>
 #include <linux/nfs_mount.h>
 #include <linux/nfs4_mount.h>
@@ -614,6 +615,7 @@ static int nfs_create_rpc_client(struct nfs_client *clp,
 		.program	= &nfs_program,
 		.version	= clp->rpc_ops->version,
 		.authflavor	= flavor,
+		.rpcmount	= init_rpc_pipefs,
 	};
 
 	if (discrtry)
diff --git a/fs/nfs/mount_clnt.c b/fs/nfs/mount_clnt.c
index 4f981f1..67b4b8d 100644
--- a/fs/nfs/mount_clnt.c
+++ b/fs/nfs/mount_clnt.c
@@ -13,6 +13,7 @@
 #include <linux/in.h>
 #include <linux/sunrpc/clnt.h>
 #include <linux/sunrpc/sched.h>
+#include <linux/sunrpc/rpc_pipe_fs.h>
 #include <linux/nfs_fs.h>
 #include "internal.h"
 
@@ -161,6 +162,7 @@ int nfs_mount(struct nfs_mount_request *info)
 		.program	= &mnt_program,
 		.version	= info->version,
 		.authflavor	= RPC_AUTH_UNIX,
+		.rpcmount	= init_rpc_pipefs,
 	};
 	struct rpc_clnt		*mnt_clnt;
 	int			status;
diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c
index 143da2e..a95150d 100644
--- a/fs/nfsd/nfs4callback.c
+++ b/fs/nfsd/nfs4callback.c
@@ -33,6 +33,7 @@
 
 #include <linux/sunrpc/clnt.h>
 #include <linux/sunrpc/svc_xprt.h>
+#include <linux/sunrpc/rpc_pipe_fs.h>
 #include <linux/slab.h>
 #include "nfsd.h"
 #include "state.h"
@@ -488,6 +489,7 @@ int setup_callback_client(struct nfs4_client *clp, struct nfs4_cb_conn *conn)
 		.version	= 0,
 		.authflavor	= clp->cl_flavor,
 		.flags		= (RPC_CLNT_CREATE_NOPING | RPC_CLNT_CREATE_QUIET),
+		.rpcmount	= init_rpc_pipefs,
 	};
 	struct rpc_clnt *client;
 
diff --git a/include/linux/sunrpc/clnt.h b/include/linux/sunrpc/clnt.h
index a5a55f2..f052712 100644
--- a/include/linux/sunrpc/clnt.h
+++ b/include/linux/sunrpc/clnt.h
@@ -116,6 +116,7 @@ struct rpc_create_args {
 	unsigned long		flags;
 	char			*client_name;
 	struct svc_xprt		*bc_xprt;	/* NFSv4.1 backchannel */
+	struct vfsmount		*rpcmount;
 };
 
 /* Values for "flags" field */
diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c
index da2507a..6d88fb7 100644
--- a/net/sunrpc/clnt.c
+++ b/net/sunrpc/clnt.c
@@ -96,7 +96,8 @@ static void rpc_unregister_client(struct rpc_clnt *clnt)
 }
 
 static int
-rpc_setup_pipedir(struct rpc_clnt *clnt, char *dir_name)
+rpc_setup_pipedir(struct rpc_clnt *clnt, struct vfsmount *rpcmount,
+		char *dir_name)
 {
 	static uint32_t clntid;
 	struct nameidata nd;
@@ -112,7 +113,7 @@ rpc_setup_pipedir(struct rpc_clnt *clnt, char *dir_name)
 	if (dir_name == NULL)
 		return 0;
 
-	path.mnt = mntget(init_rpc_pipefs);
+	path.mnt = mntget(rpcmount);
 	error = vfs_path_lookup(path.mnt->mnt_root, path.mnt, dir_name, 0, &nd);
 	if (error)
 		goto err;
@@ -226,7 +227,8 @@ static struct rpc_clnt * rpc_new_client(const struct rpc_create_args *args, stru
 
 	atomic_set(&clnt->cl_count, 1);
 
-	err = rpc_setup_pipedir(clnt, program->pipe_dir_name);
+	BUG_ON(!args->rpcmount);
+	err = rpc_setup_pipedir(clnt, args->rpcmount, program->pipe_dir_name);
 	if (err < 0)
 		goto out_no_path;
 
@@ -390,7 +392,8 @@ rpc_clone_client(struct rpc_clnt *clnt)
 			goto out_no_principal;
 	}
 	atomic_set(&new->cl_count, 1);
-	err = rpc_setup_pipedir(new, clnt->cl_program->pipe_dir_name);
+	err = rpc_setup_pipedir(new, clnt->cl_path.mnt,
+			clnt->cl_program->pipe_dir_name);
 	if (err != 0)
 		goto out_no_path;
 	if (new->cl_auth)
diff --git a/net/sunrpc/rpcb_clnt.c b/net/sunrpc/rpcb_clnt.c
index fa6d7ca..8d04380 100644
--- a/net/sunrpc/rpcb_clnt.c
+++ b/net/sunrpc/rpcb_clnt.c
@@ -27,6 +27,7 @@
 #include <linux/sunrpc/clnt.h>
 #include <linux/sunrpc/sched.h>
 #include <linux/sunrpc/xprtsock.h>
+#include <linux/sunrpc/rpc_pipe_fs.h>
 
 #ifdef RPC_DEBUG
 # define RPCDBG_FACILITY	RPCDBG_BIND
@@ -186,6 +187,7 @@ static int rpcb_create_local(void)
 		.version	= RPCBVERS_2,
 		.authflavor	= RPC_AUTH_UNIX,
 		.flags		= RPC_CLNT_CREATE_NOPING,
+		.rpcmount	= init_rpc_pipefs,
 	};
 	struct rpc_clnt *clnt, *clnt4;
 	int result = 0;
@@ -240,6 +242,7 @@ static struct rpc_clnt *rpcb_create(char *hostname, struct sockaddr *srvaddr,
 		.authflavor	= RPC_AUTH_UNIX,
 		.flags		= (RPC_CLNT_CREATE_NOPING |
 					RPC_CLNT_CREATE_NONPRIVPORT),
+		.rpcmount	= init_rpc_pipefs,
 	};
 
 	switch (srvaddr->sa_family) {
-- 
1.7.3.4

^ permalink raw reply related

* [PATCH v2 01/12] sunrpc: mount rpc_pipefs on initialization
From: Kirill A. Shutemov @ 2010-12-29 13:14 UTC (permalink / raw)
  To: Trond Myklebust, J. Bruce Fields, Neil Brown
  Cc: Pavel Emelyanov, linux-nfs, David S. Miller, netdev, linux-kernel,
	Kirill A. Shutemov
In-Reply-To: <1293628470-28386-1-git-send-email-kas@openvz.org>

Mount rpc_pipefs on register_rpc_pipefs() and replace
rpc_get_mount()/rpc_put_mount() implementation with mntget()/mntput().

Signed-off-by: Kirill A. Shutemov <kas@openvz.org>
---
 net/sunrpc/rpc_pipe.c |   27 ++++++++++++++++-----------
 1 files changed, 16 insertions(+), 11 deletions(-)

diff --git a/net/sunrpc/rpc_pipe.c b/net/sunrpc/rpc_pipe.c
index 10a17a3..7f3fbdd 100644
--- a/net/sunrpc/rpc_pipe.c
+++ b/net/sunrpc/rpc_pipe.c
@@ -29,7 +29,6 @@
 #include <linux/sunrpc/cache.h>
 
 static struct vfsmount *rpc_mnt __read_mostly;
-static int rpc_mount_count;
 
 static struct file_system_type rpc_pipe_fs_type;
 
@@ -415,18 +414,13 @@ struct rpc_filelist {
 
 struct vfsmount *rpc_get_mount(void)
 {
-	int err;
-
-	err = simple_pin_fs(&rpc_pipe_fs_type, &rpc_mnt, &rpc_mount_count);
-	if (err != 0)
-		return ERR_PTR(err);
-	return rpc_mnt;
+	return mntget(rpc_mnt);
 }
 EXPORT_SYMBOL_GPL(rpc_get_mount);
 
 void rpc_put_mount(void)
 {
-	simple_release_fs(&rpc_mnt, &rpc_mount_count);
+	mntput(rpc_mnt);
 }
 EXPORT_SYMBOL_GPL(rpc_put_mount);
 
@@ -1063,16 +1057,27 @@ int register_rpc_pipefs(void)
 	if (!rpc_inode_cachep)
 		return -ENOMEM;
 	err = register_filesystem(&rpc_pipe_fs_type);
-	if (err) {
-		kmem_cache_destroy(rpc_inode_cachep);
-		return err;
+	if (err)
+		goto destroy_cache;
+
+	rpc_mnt = kern_mount(&rpc_pipe_fs_type);
+	if (IS_ERR(rpc_mnt)) {
+		err = PTR_ERR(rpc_mnt);
+		goto unregister_fs;
 	}
 
 	return 0;
+
+unregister_fs:
+	unregister_filesystem(&rpc_pipe_fs_type);
+destroy_cache:
+	kmem_cache_destroy(rpc_inode_cachep);
+	return err;
 }
 
 void unregister_rpc_pipefs(void)
 {
+	mntput(rpc_mnt);
 	kmem_cache_destroy(rpc_inode_cachep);
 	unregister_filesystem(&rpc_pipe_fs_type);
 }
-- 
1.7.3.4

^ permalink raw reply related

* [PATCH v2 00/12] make rpc_pipefs be mountable multiple time
From: Kirill A. Shutemov @ 2010-12-29 13:14 UTC (permalink / raw)
  To: Trond Myklebust, J. Bruce Fields, Neil Brown
  Cc: Pavel Emelyanov, linux-nfs-u79uwXL29TY76Z2rM5mHXA,
	David S. Miller, netdev-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, Kirill A. Shutemov

Prepare nfs/sunrpc stack to use multiple instances of rpc_pipefs.
Only for client for now.

Changelog:

 v2:
  - one of rpc_create() calls was missed initially, fixed;
  - change logic for get_rpc_pipefs(NULL);
  - export get_rpc_pipefs() to be able to use from modules
    (tnx J. Bruce Field);
  - change "From:" and "Signed-off-by:" addresses.

 v1:
  - initial revision of the patchset.

Kirill A. Shutemov (12):
  sunrpc: mount rpc_pipefs on initialization
  sunrpc: introduce init_rpc_pipefs
  sunrpc: push init_rpc_pipefs up to rpc_create() callers
  sunrpc: tag svc_serv with rpc_pipefs mount point
  sunrpc: get rpc_pipefs mount point for svc_serv from callers
  lockd: get rpc_pipefs mount point from callers
  sunrpc: get rpc_pipefs mount point for rpcb_create[_local] from callers
  sunrpc: tag pipefs field of cache_detail with rpc_pipefs mount point
  nfs: per-rpc_pipefs dns cache
  sunrpc: introduce get_rpc_pipefs()
  nfs: introduce mount option 'rpcmount'
  sunrpc: make rpc_pipefs be mountable multiple times

 fs/lockd/clntlock.c                |    8 +-
 fs/lockd/host.c                    |   12 +++-
 fs/lockd/mon.c                     |   13 ++-
 fs/lockd/svc.c                     |    4 +-
 fs/nfs/cache_lib.c                 |   18 +---
 fs/nfs/cache_lib.h                 |    3 +-
 fs/nfs/callback.c                  |    6 +-
 fs/nfs/callback.h                  |    3 +-
 fs/nfs/client.c                    |   45 +++++++++--
 fs/nfs/dns_resolve.c               |  128 +++++++++++++++++++++++------
 fs/nfs/dns_resolve.h               |    8 +--
 fs/nfs/inode.c                     |    8 +--
 fs/nfs/internal.h                  |   10 ++-
 fs/nfs/mount_clnt.c                |    1 +
 fs/nfs/namespace.c                 |    3 +-
 fs/nfs/nfs4namespace.c             |   20 +++--
 fs/nfs/super.c                     |   20 +++++
 fs/nfsd/nfs4callback.c             |    2 +
 fs/nfsd/nfssvc.c                   |    8 +-
 include/linux/lockd/bind.h         |    3 +-
 include/linux/lockd/lockd.h        |    4 +-
 include/linux/nfs_fs_sb.h          |    1 +
 include/linux/sunrpc/cache.h       |    9 +--
 include/linux/sunrpc/clnt.h        |    5 +-
 include/linux/sunrpc/rpc_pipe_fs.h |    6 +-
 include/linux/sunrpc/svc.h         |    9 +-
 net/sunrpc/cache.c                 |   16 +++--
 net/sunrpc/clnt.c                  |   19 +++--
 net/sunrpc/rpc_pipe.c              |  156 ++++++++++++++++++++++++++++++-----
 net/sunrpc/rpcb_clnt.c             |   19 +++--
 net/sunrpc/svc.c                   |   52 ++++++++-----
 31 files changed, 448 insertions(+), 171 deletions(-)

-- 
1.7.3.4

--
To unsubscribe from this list: send the line "unsubscribe linux-nfs" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: dm9000 patch
From: Angelo Dureghello @ 2010-12-29 13:13 UTC (permalink / raw)
  To: Baruch Siach; +Cc: netdev, linux-kernel
In-Reply-To: <20101229060637.GA31010@jasper.tkos.co.il>

Hi all,

just FYI, i tested kernel 2.6.36.2, unfortunately the issue is still 
there, below the call stack trace.

[    4.620000] eth0: link up, 100Mbps, full-duplex, lpa 0x45E1
[   39.390000] BUG: spinlock recursion on CPU#0, httpd/29
[   39.390000]  lock: 00189c44, .magic: dead4ead, .owner: httpd/29, 
.owner_cpu: 0
[   39.390000] Stack from 00d6a990:
[   39.390000]         00d6a9bc 000a9710 0017cac7 00189c44 dead4ead 
00de48f4 0000001d 00000000
[   39.390000]         00189c44 0002a646 00145f70 00d6a9f0 000a98e2 
00189c44 0017cb2d 00189c44
[   39.390000]         00d6aad8 0000001f 00145f5c 001523f6 00189c08 
0002a646 00145f70 0002bc52
[   39.390000]         00d6a9fc 00145f7e 00189c44 00d6aa28 0002a75e 
00189c44 0000001f 00d6aad8
[   39.390000]         0000001f 00145f5c 00189c08 0002a646 00145f70 
0002bc52 00d6aa3c 00000bb6
[   39.390000]         0000001f 00189c44 00cfc780 00d6aa84 0000337a 
0000001f 00d6aa4c 00000001
[   39.390000] Call Trace:
[   39.390000]  [000a9710] spin_bug+0x86/0x11a
[   39.390000]  [000a98e2] do_raw_spin_lock+0x58/0x120
[   39.390000]  [00145f7e] _raw_spin_lock+0xe/0x14
[   39.390000]  [0002a75e] __do_IRQ+0x2c/0x108
[   39.390000]  [00000bb6] do_IRQ+0x48/0x62
[   39.390000]  [0000337a] inthandler+0x6a/0x74
[   39.390000]  [0002a82e] __do_IRQ+0xfc/0x108
[   39.390000]  [00000bb6] do_IRQ+0x48/0x62
[   39.390000]  [0000337a] inthandler+0x6a/0x74
[   39.390000]  [000ef0ce] skb_release_all+0x10/0x20
[   39.390000]  [000ee6bc] __kfree_skb+0x10/0x92
[   39.390000]  [000ee75e] consume_skb+0x20/0x34
[   39.390000]  [000e004e] dm9000_start_xmit+0xdc/0xec
[   39.390000]  [000f67a2] dev_hard_start_xmit+0x146/0x472
[   39.390000]  [00106506] sch_direct_xmit+0xc0/0x1bc
[   39.390000]  [000f9914] dev_queue_xmit+0x160/0x3e4
[   39.390000]  [00113b3e] ip_finish_output+0xee/0x318
[   39.390000]  [001142b4] ip_output+0x7c/0x88
[   39.390000]  [00113dc6] ip_local_out+0x26/0x30
[   39.390000]  [00114d9a] ip_queue_xmit+0x152/0x374
[   39.390000]  [00125c8c] tcp_transmit_skb+0x3f0/0x732
[   39.390000]  [00126f26] tcp_write_xmit+0x178/0x868
[   39.390000]  [00127644] __tcp_push_pending_frames+0x2e/0x9a
[   39.390000]  [0011c3d6] tcp_sendmsg+0x82e/0x98c
[   39.390000]  [00137544] inet_sendmsg+0x32/0x54
[   39.390000]  [000e79a6] sock_aio_write+0xc8/0x138
[   39.390000]  [00042590] do_sync_write+0x9e/0xfe
[   39.390000]  [00042668] vfs_write+0x78/0x84
[   39.390000]  [00042a92] sys_write+0x40/0x7a
[   39.390000]  [00003224] system_call+0x84/0xc2
[   39.390000]

dm9000e is as default not visible/selectable in menuconfig for Coldfire 
architectures, so this probably cannot be considered as a kernel bug.

I going forward in investigations, every help is appreciated,

regards,
angelo



On 29/12/2010 07:06, Baruch Siach wrote:
> Hi Angelo,
>
> On Tue, Dec 28, 2010 at 10:52:42PM +0100, Angelo Dureghello wrote:
>    
>> sorry to contact you directly but i couldn't get any help from the
>> kernel.org mailing list, since i am not a developer my mails are
>> generally skipped.
>>      
> The best way to get the contact info for a piece of kernel code, is using the
> get_maintainer.pl script. Running 'scripts/get_maintainer.pl -f
> drivers/net/dm9000.c' gives the following output:
>
> netdev@vger.kernel.org
> linux-kernel@vger.kernel.org
>
> I added both to Cc.
>
>    
>> I am very near to have a custom board working with MCF5307 cpu and dm9000.
>> I am using kernel 2.6.36-rc3 with your last patch about
>> spinlock-recursion already included.
>>      
> You should try to update to the latest .36 kernel, which is currently
> 2.6.36.2. The problem that you experience might be unrelated to the dm9000
> driver (or to networking at all), so it might have been fixed in this version.
>
>    
>> I have "ping" and "telnet" to the embedded board fully working.
>> If i try to get a sample web page with some images from the board
>> httpd with a browser, in 80% of cases i get a trap/oops:
>>      
> Try to enable KALLSYMS in your kernel .config to make your stack trace more
> meaningful. This is under 'General setup ->  Configure standard kernel features
> (for small systems) ->  Load all symbols for debugging/ksymoops'.
>
> I hope this helps.
>
> baruch
>
>    
>> [    4.590000] eth0: link up, 100Mbps, full-duplex, lpa 0x45E1
>> [   67.630000] BUG: spinlock recursion on CPU#0, httpd/29
>> [   67.630000]  lock: 00c42c06, .magic: dead4ead, .owner: httpd/29,
>> .owner_cpu: 0
>> [   67.630000] Stack from 00d7b914:
>> [   67.630000]         00d7b940 000a8cf0 0015f693 00c42c06 dead4ead
>> 00dec1d4 0000001d 00000000
>> [   67.630000]         00c42c06 00006188 00c42800 00d7b974 000a8ec2
>> 00c42c06 0015f6f9 00002704
>> [   67.630000]         00000000 0000001f 00146fa4 00152f0c 00c42b60
>> 00006188 00c42800 0002b312
>> [   67.630000]         00d7b984 0014701e 00c42c06 00000000 00d7b9c4
>> 000df21c 00c42c06 00000000
>> [   67.630000]         00000000 0000001f 00146fa4 00152f0c 000005ea
>> 00cfc640 00006188 000096e8
>> [   67.630000]         0002b312 00146fa4 00c42b60 00002704 00d7b9ec
>> 00029d3a 0000001f 00c42800
>> [   67.630000] Call Trace:
>> [   67.630000]  [000a8cf0]  [000a8ec2]  [0014701e]  [000df21c]  [00029d3a]
>> [   67.630000]  [00029e84]  [00000bb6]  [0000336e]  [000df162]  [000effd6]
>> [   67.630000]  [00100482]  [000f312e]  [000f9ebc]  [0010dd2a]  [0010e4a0]
>> [   67.630000]  [0010dfb2]  [0010ef80]  [0011fed6]  [00121170]  [0012188e]
>> [   67.630000]  [0011ecc6]  [001249fe]  [000e4084]  [0011621c]  [00131a44]
>> [   67.630000]  [000e11ee]  [00041944]  [00041a1c]  [00041e46]  [00003218]
>> [   67.630000] BUG: spinlock lockup on CPU#0, httpd/29, 00c42c06
>> [   67.630000] Stack from 00d7b934:
>> [   67.630000]         00d7b974 000a8f66 0015f703 00000000 00dec1d4
>> 0000001d 00c42c06 00002704
>> [   67.630000]         00000000 0000001f 00146fa4 00152f0c 00c42b60
>> 00006188 00c42800 0002b312
>> [   67.630000]         00d7b984 0014701e 00c42c06 00000000 00d7b9c4
>> 000df21c 00c42c06 00000000
>> [   67.630000]         00000000 0000001f 00146fa4 00152f0c 000005ea
>> 00cfc640 00006188 000096e8
>> [   67.630000]         0002b312 00146fa4 00c42b60 00002704 00d7b9ec
>> 00029d3a 0000001f 00c42800
>> [   67.630000]         0016c1b4 00cfc640 0000001f 0016c178 00029d10
>> 00146fb8 00d7ba20 00029e84
>> [   67.630000] Call Trace:
>> [   67.630000]  [000a8f66]  [0014701e]  [000df21c]  [00029d3a]  [00029e84]
>> [   67.630000]  [00000bb6]  [0000336e]  [000df162]  [000effd6]  [00100482]
>> [   67.630000]  [000f312e]  [000f9ebc]  [0010dd2a]  [0010e4a0]  [0010dfb2]
>> [   67.630000]  [0010ef80]  [0011fed6]  [00121170]  [0012188e]  [0011ecc6]
>> [   67.630000]  [001249fe]  [000e4084]  [0011621c]  [00131a44]  [000e11ee]
>> [   67.630000]  [00041944]  [00041a1c]  [00041e46]  [00003218]
>>
>> As i said, i was hoping in your patch but i sadly discovered it is
>> already included in this kernel version.
>> Hope you can give me some help or can forward me to an appropriate
>> mailing list.
>>      
>    

^ permalink raw reply

* [PATCH v3] Gemini: Gigabit ethernet driver
From: Michał Mirosław @ 2010-12-29 13:02 UTC (permalink / raw)
  To: Hans Ulli Kroll, gemini-board-dev; +Cc: netdev, Christoph Biedl

Driver for SL351x (Gemini) SoC ethernet peripheral. Based in part
on work by Paulius Zaleckas and GPLd code from Raidsonic and other
NAS vendors.

Tested on Raidsonic IcyBox 4220-B (dual SATA NAS).

Signed-off-by: Michał Mirosław <mirq-linux@rere.qmqm.pl>

---

Against: v2.6.37-rc8

This depends on commit 83097f22d1f91a748bb2c066d049d0d1d2843ef2

        Gemini: create platform device for ethernet in Raidsonic IB-4220B

from  git://git.berlios.de/gemini-board

Note for testers: you may tweak DEFAULT_RX_BUF_ORDER (=log2(buffer size),
at most a page - so in range [6..12]) and RX_MAX_ALLOC_ORDER (max allocated
rx buffer page order) to find best memory usage/performance settings apart
from what is available via ethtool. For TX, there's not much you can do when
offloads are disabled.

Changes from v2:
 - converted to page buffers and napi_gro_frags()
 - later IRQ acking and NAPI exits
 - larger rings by default
 - tx-interrupt coalescing
 - MTU changing
 - jumbo frames support
 - ringparam and coalesce settings via ethtool
 - more fixes/cleanups

Changes from v1:
 - fixed stats (now using u64_stats_sync; no-op on UP anyway)
 - pre-load mdio-gpio if built as module
 - disable TX checksum offload by default (unreliable HW)
 - convert to NAPI+GRO (netperf TCP STREAM RX test:
	before: 156mbit/s, now: 185mbit/s)

Later TODO:
 - netpoll (netconsole)

---
 MAINTAINERS             |    9 +
 drivers/net/Kconfig     |   10 +
 drivers/net/Makefile    |    1 +
 drivers/net/sl351x.c    | 2287 +++++++++++++++++++++++++++++++++++++++++++++++
 drivers/net/sl351x_hw.h | 1435 +++++++++++++++++++++++++++++
 5 files changed, 3742 insertions(+), 0 deletions(-)

diff --git a/MAINTAINERS b/MAINTAINERS
index 71e40f9..694de04 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -5439,6 +5439,15 @@ S:	Maintained
 F:	drivers/net/skge.*
 F:	drivers/net/sky2.*
 
+SL351X (STORLINK GEMINI SOC) GIGABIT ETHERNET DRIVER
+M:	Michał Mirosław <mirq-linux@rere.qmqm.pl>
+L:	netdev@vger.kernel.org
+L:	gemini-board-dev@lists.berlios.de
+T:	git git://git.berlios.de/gemini-board
+S:	Maintained
+F:	driver/net/sl351x.c
+F:	driver/net/sl351x_hw.h
+
 SLAB ALLOCATOR
 M:	Christoph Lameter <cl@linux-foundation.org>
 M:	Pekka Enberg <penberg@cs.helsinki.fi>
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 4f1755b..0336c4f 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -2068,6 +2068,16 @@ config ACENIC_OMIT_TIGON_I
 
 	  The safe and default value for this is N.
 
+config GEMINI_SL351X
+	tristate "StorLink SL351x Gigabit Ethernet support"
+	depends on ARCH_GEMINI
+	select PHYLIB
+	select MDIO_BITBANG
+	select MDIO_GPIO
+	select CRC32
+	help
+	  This driver supports StorLink SL351x (Gemini) dual Gigabit Ethernet.
+
 config DL2K
 	tristate "DL2000/TC902x-based Gigabit Ethernet support"
 	depends on PCI
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index b90738d..2d3491b 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -257,6 +257,7 @@ obj-$(CONFIG_MLX4_CORE) += mlx4/
 obj-$(CONFIG_ENC28J60) += enc28j60.o
 obj-$(CONFIG_ETHOC) += ethoc.o
 obj-$(CONFIG_GRETH) += greth.o
+obj-$(CONFIG_GEMINI_SL351X) += sl351x.o
 
 obj-$(CONFIG_XTENSA_XT2000_SONIC) += xtsonic.o
 
diff --git a/drivers/net/sl351x.c b/drivers/net/sl351x.c
new file mode 100644
index 0000000..28b55b0
--- /dev/null
+++ b/drivers/net/sl351x.c
@@ -0,0 +1,2287 @@
+/*
+ *  Ethernet device driver for Gemini SoC (SL351x GMAC).
+ *
+ *  Copyright (C) 2010, Michał Mirosław <mirq-linux@rere.qmqm.pl>
+ *
+ *  Based on work by Paulius Zaleckas <paulius.zaleckas@gmail.com> and
+ *  GPLd spaghetti code from Raidsonic and other Gemini-based NAS vendors.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/cache.h>
+#include <linux/interrupt.h>
+
+#include <linux/platform_device.h>
+#include <linux/etherdevice.h>
+#include <linux/if_vlan.h>
+#include <linux/skbuff.h>
+#include <linux/phy.h>
+#include <linux/crc32.h>
+#include <linux/ethtool.h>
+#include <linux/tcp.h>
+#include <linux/u64_stats_sync.h>
+
+#include <mach/hardware.h>
+#include <mach/global_reg.h>
+#include <mach/irqs.h>
+#include <mach/gmac.h>
+#include "sl351x_hw.h"
+
+#define DEFAULT_TX_COALESCE		16
+#define DEFAULT_GMAC_RXQ_ORDER		10
+#define DEFAULT_GMAC_TXQ_ORDER		10
+#define DEFAULT_RX_BUF_ORDER		11
+#define DEFAULT_NAPI_WEIGHT		64
+#define RX_INSERT_BYTES			2
+#define TX_MAX_FRAGS			8
+#define TX_QUEUE_NUM			1	/* max: 6 */
+#define RX_MAX_ALLOC_ORDER		4
+#define NETIF_TSO_FEATURES	\
+	(NETIF_F_TSO|NETIF_F_TSO_ECN|NETIF_F_TSO6)
+
+static int debug_level = 0;
+module_param(debug_level, int, 0600);
+MODULE_PARM_DESC(debug_level, "netif debug level mask");
+
+struct toe_private {
+	void __iomem	*iomem;
+	GMAC_RXDESC_T	*freeq_ring;
+	spinlock_t	irq_lock;
+
+	struct net_device *netdev[2];
+	struct device	*dev;
+	int		irq;
+
+	unsigned int	freeq_frag_order;
+	unsigned int	freeq_order;
+	unsigned int	freeq_entries;
+	dma_addr_t	freeq_dma_base;
+
+	struct page	*freeq_page;
+	unsigned int	freeq_page_count;
+	unsigned int	alloc_order;
+	unsigned int	freeq_page_offs;
+};
+
+struct gmac_txq {
+	GMAC_TXDESC_T	*ring;
+	unsigned int	cptr;
+	struct sk_buff	**skb;
+	unsigned int	noirq_packets;
+} ____cacheline_aligned_in_smp;
+
+struct gmac_private {
+	void __iomem		*dma_iomem;
+
+	void __iomem		*rxq_rwptr;
+	GMAC_RXDESC_T		*rxq_ring;
+	unsigned int		rxq_order;
+
+	struct napi_struct	napi;
+	struct gmac_txq		txq[TX_QUEUE_NUM];
+	unsigned int		txq_order;
+	unsigned int		irq_every_tx_packets;
+
+	dma_addr_t		rxq_dma_base;
+	dma_addr_t		txq_dma_base;
+
+	unsigned int		msg_enable;
+	spinlock_t		config_lock;
+
+	int			in_reset;
+
+	struct u64_stats_sync	tx_stats_syncp;
+	struct u64_stats_sync	rx_stats_syncp;
+	struct u64_stats_sync	ir_stats_syncp;
+
+	struct rtnl_link_stats64 stats;
+	u64			hw_stats[RX_STATS_NUM];
+	u64			rx_stats[RX_STATUS_NUM];
+	u64			rx_csum_stats[RX_CHKSUM_NUM];
+	u64			rx_napi_exits;
+	u64			tx_frag_stats[TX_MAX_FRAGS];
+	u64			tx_frags_linearized;
+	u64			tx_hw_csummed;
+};
+
+#define GMAC_STATS_NUM	(RX_STATS_NUM + RX_STATUS_NUM + RX_CHKSUM_NUM + 1 + TX_MAX_FRAGS + 2)
+static const char gmac_stats_strings[GMAC_STATS_NUM][ETH_GSTRING_LEN] = {
+	"GMAC_IN_DISCARDS",
+	"GMAC_IN_ERRORS",
+	"GMAC_IN_MCAST",
+	"GMAC_IN_BCAST",
+	"GMAC_IN_MAC1",
+	"GMAC_IN_MAC2",
+	"RX_STATUS_GOOD_FRAME",
+	"RX_STATUS_TOO_LONG_GOOD_CRC",
+	"RX_STATUS_RUNT_FRAME",
+	"RX_STATUS_SFD_NOT_FOUND",
+	"RX_STATUS_CRC_ERROR",
+	"RX_STATUS_TOO_LONG_BAD_CRC",
+	"RX_STATUS_ALIGNMENT_ERROR",
+	"RX_STATUS_TOO_LONG_BAD_ALIGN",
+	"RX_STATUS_RX_ERR",
+	"RX_STATUS_DA_FILTERED",
+	"RX_STATUS_BUFFER_FULL",
+	"RX_STATUS_11",
+	"RX_STATUS_12",
+	"RX_STATUS_13",
+	"RX_STATUS_14",
+	"RX_STATUS_15",
+	"RX_CHKSUM_IP_UDP_TCP_OK",
+	"RX_CHKSUM_IP_OK_ONLY",
+	"RX_CHKSUM_NONE",
+	"RX_CHKSUM_3",
+	"RX_CHKSUM_IP_ERR_UNKNOWN",
+	"RX_CHKSUM_IP_ERR",
+	"RX_CHKSUM_TCP_UDP_ERR",
+	"RX_CHKSUM_7",
+	"RX_NAPI_EXITS",
+	"TX_FRAGS[1]",
+	"TX_FRAGS[2]",
+	"TX_FRAGS[3]",
+	"TX_FRAGS[4]",
+	"TX_FRAGS[5]",
+	"TX_FRAGS[6]",
+	"TX_FRAGS[7]",
+	"TX_FRAGS[8]",
+	"TX_FRAGS_LINEARIZED",
+	"TX_HW_CSUMMED",
+};
+
+static struct gmac_private *netdev_to_gmac(struct net_device *dev)
+{
+	return netdev_priv(dev);
+}
+
+static struct toe_private *netdev_to_toe(struct net_device *dev)
+{
+	return dev->ml_priv;
+}
+
+static struct gmac_private *napi_to_gmac(struct napi_struct *napi)
+{
+	return container_of(napi, struct gmac_private, napi);
+}
+
+static void __iomem *toe_reg(struct toe_private *toe, unsigned int reg)
+{
+	return toe->iomem + reg;
+}
+
+static void __iomem *gmac_dma_reg(struct net_device *dev, unsigned int reg)
+{
+	return netdev_to_gmac(dev)->dma_iomem + reg;
+}
+
+static void __iomem *gmac_ctl_reg(struct net_device *dev, unsigned int reg)
+{
+	return (void __iomem *)dev->base_addr + reg;
+}
+
+static struct page *toe_unmap_rx_desc(struct toe_private *toe,
+	GMAC_RXDESC_T *rx)
+{
+	struct page *page;
+
+	if (unlikely(!rx->word2.buf_adr))
+		return NULL;
+
+	page = dma_to_page(toe->dev, rx->word2.buf_adr);
+
+	dma_unmap_page(toe->dev, rx->word2.buf_adr,
+		1 << toe->freeq_frag_order, DMA_FROM_DEVICE);
+
+	return page;
+}
+
+static void gmac_hw_start(struct net_device *dev)
+{
+	GMAC_DMA_CTRL_T	dma_ctrl;
+
+	dma_ctrl.bits32 = __raw_readl(gmac_dma_reg(dev, GMAC_DMA_CTRL_REG));
+
+	dma_ctrl.bits.rd_enable = 1;
+	dma_ctrl.bits.td_enable = 1;
+	dma_ctrl.bits.loopback = 0;
+	dma_ctrl.bits.drop_small_ack = 0;
+	dma_ctrl.bits.rd_prot = 0;
+	dma_ctrl.bits.rd_burst_size = 3;
+	dma_ctrl.bits.rd_insert_bytes = RX_INSERT_BYTES;
+	dma_ctrl.bits.rd_bus = 3;
+	dma_ctrl.bits.td_prot = 0;
+	dma_ctrl.bits.td_burst_size = 3;
+	dma_ctrl.bits.td_bus = 3;
+
+	__raw_writel(dma_ctrl.bits32, gmac_dma_reg(dev, GMAC_DMA_CTRL_REG));
+}
+
+static void gmac_hw_stop(struct net_device *dev)
+{
+	GMAC_DMA_CTRL_T	dma_ctrl;
+
+	dma_ctrl.bits32 = __raw_readl(gmac_dma_reg(dev, GMAC_DMA_CTRL_REG));
+
+	dma_ctrl.bits.rd_enable = 0;
+	dma_ctrl.bits.td_enable = 0;
+
+	__raw_writel(dma_ctrl.bits32, gmac_dma_reg(dev, GMAC_DMA_CTRL_REG));
+}
+
+static void gmac_update_config0_reg(struct net_device *dev, u32 val, u32 vmask)
+{
+	struct gmac_private *gmac = netdev_to_gmac(dev);
+	unsigned long flags;
+	u32 reg;
+
+	spin_lock_irqsave(&gmac->config_lock, flags);
+
+	reg = __raw_readl(gmac_ctl_reg(dev, GMAC_CONFIG0));
+	reg = (reg & ~vmask) | val;
+	__raw_writel(reg, gmac_ctl_reg(dev, GMAC_CONFIG0));
+
+	spin_unlock_irqrestore(&gmac->config_lock, flags);
+}
+
+static void gmac_enable_tx_rx(struct net_device *dev)
+{
+	gmac_update_config0_reg(dev, 0, CONFIG0_TX_RX_DISABLE);
+}
+
+static void gmac_disable_tx_rx(struct net_device *dev)
+{
+	gmac_update_config0_reg(dev, CONFIG0_TX_RX_DISABLE, CONFIG0_TX_RX_DISABLE);
+	mdelay(10);	/* let GMAC consume packet */
+}
+
+static void gmac_set_flow_control(struct net_device *dev, bool tx, bool rx)
+{
+	u32 val = (tx ? CONFIG0_FLOW_TX : 0)|(rx ? CONFIG0_FLOW_RX : 0);
+
+	gmac_update_config0_reg(dev, val, CONFIG0_FLOW_CTL);
+}
+
+static void gmac_update_link_state(struct net_device *dev)
+{
+	struct gmac_private *gmac = netdev_to_gmac(dev);
+	void __iomem *status_reg = gmac_ctl_reg(dev, GMAC_STATUS);
+	struct phy_device *phydev = dev->phydev;
+	GMAC_STATUS_T status, old_status;
+
+	old_status.bits32 = status.bits32 = __raw_readl(status_reg);
+
+	status.bits.link = phydev->link;
+	status.bits.duplex = phydev->duplex;
+
+	switch (phydev->speed) {
+	case 1000:
+		status.bits.speed = GMAC_SPEED_1000;
+		if (phydev->interface == PHY_INTERFACE_MODE_RGMII)
+			status.bits.mii_rmii = GMAC_PHY_RGMII_1000;
+		break;
+	case 100:
+		status.bits.speed = GMAC_SPEED_100;
+		if (phydev->interface == PHY_INTERFACE_MODE_RGMII)
+			status.bits.mii_rmii = GMAC_PHY_RGMII_100_10;
+		break;
+	case 10:
+		status.bits.speed = GMAC_SPEED_10;
+		if (phydev->interface == PHY_INTERFACE_MODE_RGMII)
+			status.bits.mii_rmii = GMAC_PHY_RGMII_100_10;
+		break;
+	default:
+		dev_warn(&dev->dev, "Not supported PHY speed (%d)\n", phydev->speed);
+	}
+
+	gmac_set_flow_control(dev, phydev->pause,
+		phydev->pause ^ phydev->asym_pause);
+
+	if (old_status.bits32 == status.bits32)
+		return;
+
+	if (netif_msg_link(gmac)) {
+		phy_print_status(phydev);
+		netdev_info(dev, "link flow control: %s\n",
+			phydev->pause
+				? (phydev->asym_pause ? "tx" : "both")
+				: (phydev->asym_pause ? "rx" : "none")
+		);
+	}
+
+	gmac_disable_tx_rx(dev);
+	__raw_writel(status.bits32, status_reg);
+	gmac_enable_tx_rx(dev);
+}
+
+static int gmac_setup_phy(struct net_device *dev)
+{
+	struct toe_private *toe = netdev_to_toe(dev);
+	struct gemini_gmac_platform_data *pdata = toe->dev->platform_data;
+	GMAC_STATUS_T status = { .bits32 = 0 };
+	int num = dev->dev_id;
+
+	dev->phydev = phy_connect(dev, pdata->bus_id[num],
+		&gmac_update_link_state, 0, pdata->interface[num]);
+
+	if (IS_ERR(dev->phydev)) {
+		int err = PTR_ERR(dev->phydev);
+		dev->phydev = NULL;
+		return err;
+	}
+
+	dev->phydev->supported &= PHY_GBIT_FEATURES|SUPPORTED_Pause;
+	dev->phydev->advertising = dev->phydev->supported;
+
+	/* set PHY interface type */
+	switch (dev->phydev->interface) {
+	case PHY_INTERFACE_MODE_MII:
+		status.bits.mii_rmii = GMAC_PHY_MII;
+		break;
+	case PHY_INTERFACE_MODE_GMII:
+		status.bits.mii_rmii = GMAC_PHY_GMII;
+		break;
+	case PHY_INTERFACE_MODE_RGMII:
+		status.bits.mii_rmii = GMAC_PHY_RGMII_100_10;
+		break;
+	default:
+		dev_err(&dev->dev, "Unsupported MII interface\n");
+		phy_disconnect(dev->phydev);
+		dev->phydev = NULL;
+		return -EINVAL;
+	}
+	__raw_writel(status.bits32, gmac_ctl_reg(dev, GMAC_STATUS));
+
+	return 0;
+}
+
+static int gmac_pick_rx_max_len(int max_l3_len)
+{
+	/* index = CONFIG_MAXLEN_XXX values */
+	static const int max_len[8] = {
+		1536, 1518, 1522, 1542,
+		9212, 10236, 1518, 1518
+	};
+	int i, n = 5;
+
+	max_l3_len += ETH_HLEN + VLAN_HLEN;
+
+	if (max_l3_len > max_len[n])
+		return -1;
+
+	for (i = 0; i < 5; ++i) {
+		if (max_len[i] >= max_l3_len && max_len[i] < max_len[n])
+			n = i;
+	}
+
+	return n;
+}
+
+static int gmac_init(struct net_device *dev)
+{
+	struct gmac_private *gmac = netdev_to_gmac(dev);
+	u32 val;
+
+	GMAC_CONFIG0_T config0 = { .bits = {
+		.dis_tx = 1,
+		.dis_rx = 1,
+		.ipv4_rx_chksum = 1,
+		.ipv6_rx_chksum = 1,
+		.rx_err_detect = 1,
+		.rgmm_edge = 1,
+		.port0_chk_hwq = 1,
+		.port1_chk_hwq = 1,
+		.port0_chk_toeq = 1,
+		.port1_chk_toeq = 1,
+		.port0_chk_classq = 1,
+		.port1_chk_classq = 1,
+	} };
+	GMAC_TX_WCR0_T hw_weigh = { .bits = {
+		.hw_tq3 = 1,
+		.hw_tq2 = 1,
+		.hw_tq1 = 1,
+		.hw_tq0 = 1,
+	} };
+	GMAC_TX_WCR1_T sw_weigh = { .bits = {
+		.sw_tq5 = 1,
+		.sw_tq4 = 1,
+		.sw_tq3 = 1,
+		.sw_tq2 = 1,
+		.sw_tq1 = 1,
+		.sw_tq0 = 1,
+	} };
+	GMAC_CONFIG1_T config1 = { .bits = {
+		.set_threshold = 16,
+		.rel_threshold = 24,
+	} };
+	GMAC_CONFIG2_T config2 = { .bits = {
+		.set_threshold = 16,
+		.rel_threshold = 32,
+	} };
+	GMAC_CONFIG3_T config3 = { .bits = {
+		.set_threshold = 0,
+		.rel_threshold = 0,
+	} };
+
+	config0.bits.max_len = gmac_pick_rx_max_len(dev->mtu);
+
+	val = __raw_readl(gmac_ctl_reg(dev, GMAC_CONFIG0));
+	config0.bits.reserved = ((GMAC_CONFIG0_T)val).bits.reserved;
+	__raw_writel(config0.bits32, gmac_ctl_reg(dev, GMAC_CONFIG0));
+	__raw_writel(config1.bits32, gmac_ctl_reg(dev, GMAC_CONFIG1));
+	__raw_writel(config2.bits32, gmac_ctl_reg(dev, GMAC_CONFIG2));
+	__raw_writel(config3.bits32, gmac_ctl_reg(dev, GMAC_CONFIG3));
+	__raw_writel(hw_weigh.bits32, gmac_dma_reg(dev, GMAC_TX_WEIGHTING_CTRL_0_REG));
+	__raw_writel(sw_weigh.bits32, gmac_dma_reg(dev, GMAC_TX_WEIGHTING_CTRL_1_REG));
+
+	gmac->rxq_order = DEFAULT_GMAC_RXQ_ORDER;
+	gmac->txq_order = DEFAULT_GMAC_TXQ_ORDER;
+
+	gmac->irq_every_tx_packets = DEFAULT_TX_COALESCE;
+
+	return 0;
+}
+
+static void gmac_uninit(struct net_device *dev)
+{
+	if (dev->phydev)
+		phy_disconnect(dev->phydev);
+}
+
+static int gmac_setup_txqs(struct net_device *dev)
+{
+	struct gmac_private *gmac = netdev_to_gmac(dev);
+	struct toe_private *toe = netdev_to_toe(dev);
+	void __iomem *rwptr_reg = gmac_dma_reg(dev, GMAC_SW_TX_QUEUE0_PTR_REG);
+	void __iomem *base_reg = gmac_dma_reg(dev, GMAC_SW_TX_QUEUE_BASE_REG);
+
+	unsigned int n_txq = dev->num_tx_queues;
+	struct gmac_txq *txq = gmac->txq;
+	GMAC_TXDESC_T *desc_ring;
+	struct sk_buff **skb_tab;
+	int i;
+
+	skb_tab = kzalloc(n_txq * dev->tx_queue_len * sizeof(*skb_tab), GFP_KERNEL);
+	if (!skb_tab)
+		return -ENOMEM;
+
+	desc_ring = dma_alloc_coherent(toe->dev,
+		n_txq * sizeof(*desc_ring) << gmac->txq_order,
+		&gmac->txq_dma_base, GFP_KERNEL);
+	if (!desc_ring) {
+		kfree(skb_tab);
+		return -ENOMEM;
+	}
+
+	BUG_ON(gmac->txq_dma_base & ~DMA_Q_BASE_MASK);
+
+	for (i = 0; i < n_txq; i++) {
+		netif_info(gmac, ifup, dev,
+			"txq%u: ring %p (dma 0x%08x), skb %p, rwptr %p, len %u (order %u)\n",
+			i, desc_ring, gmac->txq_dma_base, skb_tab, rwptr_reg,
+			1 << gmac->txq_order, gmac->txq_order);
+
+		__raw_writel(0, rwptr_reg);
+		txq->ring = desc_ring;
+		txq->cptr = 0;
+		txq->skb = skb_tab;
+
+		desc_ring += 1 << gmac->txq_order;
+		skb_tab += 1 << gmac->txq_order;
+		rwptr_reg += 4;
+	}
+
+	__raw_writel(gmac->txq_dma_base | gmac->txq_order, base_reg);
+
+	return 0;
+}
+
+static void gmac_cleanup_txqs(struct net_device *dev)
+{
+	struct gmac_private *gmac = netdev_to_gmac(dev);
+	struct toe_private *toe = netdev_to_toe(dev);
+	void __iomem *rwptr_reg = gmac_dma_reg(dev, GMAC_SW_TX_QUEUE0_PTR_REG);
+	void __iomem *base_reg = gmac_dma_reg(dev, GMAC_SW_TX_QUEUE_BASE_REG);
+
+	struct gmac_txq *txq = gmac->txq;
+	unsigned n_txq = dev->num_tx_queues;
+	int i, j;
+
+	for (i = 0; i < n_txq; ++i, ++txq) {
+		__raw_writel(0, rwptr_reg + 4 * i);
+		for (j = 0; j < dev->tx_queue_len; ++j)
+			if (txq->skb[j])
+				dev_kfree_skb(txq->skb[j]);
+	}
+
+	__raw_writel(0, base_reg);
+
+	kfree(gmac->txq->skb);
+	dma_free_coherent(toe->dev,
+		n_txq * sizeof(*gmac->txq->ring) << gmac->txq_order,
+		gmac->txq->ring, gmac->txq_dma_base);
+}
+
+static int gmac_setup_rxq(struct net_device *dev)
+{
+	struct gmac_private *gmac = netdev_to_gmac(dev);
+	struct toe_private *toe = netdev_to_toe(dev);
+	NONTOE_QHDR_T __iomem *qhdr = toe_reg(toe, TOE_DEFAULT_Q_HDR_BASE(dev->dev_id));
+
+	gmac->rxq_rwptr = &qhdr->word1;
+	gmac->rxq_ring = dma_alloc_coherent(toe->dev,
+		sizeof(*gmac->rxq_ring) << gmac->rxq_order,
+		&gmac->rxq_dma_base, GFP_KERNEL);
+	if (!gmac->rxq_ring)
+		return -ENOMEM;
+
+	BUG_ON(gmac->rxq_dma_base & ~NONTOE_QHDR0_BASE_MASK);
+
+	__raw_writel(0, gmac->rxq_rwptr);
+	__raw_writel(gmac->rxq_dma_base | gmac->rxq_order, &qhdr->word0);
+
+	netif_info(gmac, ifup, dev,
+		"rxq: ring %p (dma 0x%08x), rwptr %p, len %u (order %u)\n",
+		gmac->rxq_ring, gmac->rxq_dma_base, gmac->rxq_rwptr,
+		1 << gmac->rxq_order, gmac->rxq_order);
+	return 0;
+}
+
+static void gmac_cleanup_rxq(struct net_device *dev)
+{
+	struct gmac_private *gmac = netdev_to_gmac(dev);
+	struct toe_private *toe = netdev_to_toe(dev);
+
+	NONTOE_QHDR_T __iomem *qhdr = toe_reg(toe, TOE_DEFAULT_Q_HDR_BASE(dev->dev_id));
+	void __iomem *dma_reg = &qhdr->word0;
+	void __iomem *ptr_reg = &qhdr->word1;
+	unsigned i, e, mask = __RWPTR_MASK(gmac->rxq_order);
+	struct page *page;
+
+	i = GET_RPTR(ptr_reg);
+	e = GET_WPTR(ptr_reg);
+	__raw_writel(0, ptr_reg);
+	__raw_writel(0, dma_reg);
+
+	for (; i != e; i = __RWPTR_NEXT(i, mask)) {
+		page = toe_unmap_rx_desc(toe, &gmac->rxq_ring[i]);
+		if (likely(page))
+			put_page(page);
+	}
+
+	dma_free_coherent(toe->dev, sizeof(*gmac->rxq_ring) << gmac->rxq_order,
+		gmac->rxq_ring, gmac->rxq_dma_base);
+}
+
+static void __gmac_enable_txfin_irq(struct net_device *dev, int txq, int enable);
+
+static void gmac_tx_interrupt(struct net_device *dev, unsigned txq_num)
+{
+	struct gmac_private *gmac = netdev_to_gmac(dev);
+	struct toe_private *toe = netdev_to_toe(dev);
+
+	void __iomem *ptr_reg = gmac_dma_reg(dev, GMAC_SW_TX_QUEUE_PTR_REG(txq_num));
+	struct netdev_queue *ntxq = netdev_get_tx_queue(dev, txq_num);
+	struct gmac_txq *txq = &gmac->txq[txq_num];
+
+	unsigned i, n, errs = 0, mask = __RWPTR_MASK(gmac->txq_order);
+	struct sk_buff *skb;
+	GMAC_TXDESC_T *tx;
+
+	netif_info(gmac, tx_done, dev, "txirq%u: %u,%u,%u\n",
+		txq_num, txq->cptr, GET_RPTR(ptr_reg), GET_WPTR(ptr_reg));
+
+	for (i = txq->cptr; i != GET_RPTR(ptr_reg); i = __RWPTR_NEXT(i, mask)) {
+retry:
+		tx = &txq->ring[i];
+		skb = txq->skb[i];
+		txq->skb[i] = NULL;
+
+		BUG_ON(!skb);
+
+		dma_unmap_single(toe->dev, tx->word2.buf_adr,
+			tx->word0.bits.buffer_size, DMA_TO_DEVICE);
+
+		if (tx->word0.bits.status_tx_ok) {
+			netif_info(gmac, tx_done, dev,
+				"TX done descriptor: [%u] 0x%08x 0x%08x 0x%08x 0x%08x\n",
+				i, tx->word0.bits32, tx->word1.bits32,
+				tx->word2.bits32, tx->word3.bits32);
+		} else {
+			errs++;
+			netif_err(gmac, tx_err, dev,
+				"TX error descriptor: [%u] 0x%08x 0x%08x 0x%08x 0x%08x\n",
+				i, tx->word0.bits32, tx->word1.bits32,
+				tx->word2.bits32, tx->word3.bits32);
+		}
+
+		n = tx->word0.bits.desc_count;
+		BUG_ON(__RWPTR_DISTANCE(i, GET_RPTR(ptr_reg), mask) < n);
+
+		while (--n) {
+			i = __RWPTR_NEXT(i, mask);
+			dma_unmap_page(toe->dev, txq->ring[i].word2.buf_adr,
+				txq->ring[i].word0.bits.buffer_size, DMA_TO_DEVICE);
+			netif_info(gmac, tx_done, dev,
+				"TX frag descriptor: [%u] 0x%08x 0x%08x 0x%08x 0x%08x\n",
+				i, txq->ring[i].word0.bits32, txq->ring[i].word1.bits32,
+				txq->ring[i].word2.bits32, txq->ring[i].word3.bits32);
+		}
+
+		dev_kfree_skb_irq(skb);
+	}
+
+	spin_lock(&toe->irq_lock);
+
+	u64_stats_update_begin(&gmac->ir_stats_syncp);
+	gmac->stats.tx_errors += errs;
+	u64_stats_update_end(&gmac->ir_stats_syncp);
+
+	txq->cptr = i;
+	__gmac_enable_txfin_irq(dev, txq_num, 0);
+	netif_tx_wake_queue(ntxq);
+
+	spin_unlock(&toe->irq_lock);
+
+	__raw_writel(
+		(GMAC0_SWTQ00_EOF_INT_BIT|GMAC0_SWTQ00_FIN_INT_BIT)
+			<< (6 * dev->dev_id + txq_num),
+		toe_reg(toe, GLOBAL_INTERRUPT_STATUS_0_REG));
+	txq->noirq_packets = gmac->irq_every_tx_packets;
+
+	if (unlikely(i != GET_RPTR(ptr_reg))) {
+		errs = 0;
+		goto retry;
+	}
+}
+
+static inline unsigned tss_pkt_len(struct sk_buff *skb)
+{
+	if (!skb_is_gso(skb))
+		return 0;
+
+	return skb_transport_offset(skb) +
+		tcp_hdrlen(skb) + skb_shinfo(skb)->gso_size;
+}
+
+static int gmac_map_tx_bufs(struct net_device *dev, struct sk_buff *skb,
+	struct gmac_txq *txq, int desc, unsigned tss_flags)
+{
+	struct gmac_private *gmac = netdev_to_gmac(dev);
+	struct device *dma_dev = netdev_to_toe(dev)->dev;
+	skb_frag_t *frag;
+	dma_addr_t mapping;
+	int nfrags, w;
+
+	frag = skb_shinfo(skb)->frags;
+	nfrags = skb_shinfo(skb)->nr_frags;
+	w = desc;
+
+	mapping = dma_map_single(dma_dev, skb->data,
+		skb_headlen(skb), DMA_TO_DEVICE);
+	if (dma_mapping_error(dma_dev, mapping))
+		goto map1_error;
+
+	if (!skb_is_gso(skb))
+		tss_flags |= TSS_BYPASS_BIT;
+
+	txq->ring[w].word0.bits32 = skb_headlen(skb);
+	txq->ring[w].word1.bits32 = skb->len | tss_flags;
+	txq->ring[w].word2.bits32 = mapping;
+	txq->ring[w].word3.bits32 = tss_pkt_len(skb) | SOF_BIT;
+
+	/* racing with TX completion irq, harmless */
+	if (txq->noirq_packets == 1) {
+		txq->noirq_packets = 0;
+		txq->ring[w].word3.bits32 |= EOFIE_BIT;
+	} else if (txq->noirq_packets)
+		txq->noirq_packets--;
+
+	netif_info(gmac, tx_queued, dev,
+		"txq%ld[%u]: 0x%08x 0x%08x 0x%08x 0x%08x, datap %p\n",
+		txq - gmac->txq, w,
+		txq->ring[w].word0.bits32, txq->ring[w].word1.bits32,
+		txq->ring[w].word2.bits32, txq->ring[w].word3.bits32,
+		skb->data);
+
+	while (nfrags--) {
+		mapping = dma_map_page(dma_dev, frag->page,
+			frag->page_offset, frag->size, DMA_TO_DEVICE);
+		if (dma_mapping_error(dma_dev, mapping))
+			goto map_error;
+
+		w = RWPTR_NEXT(w, gmac->txq_order);
+		txq->ring[w].word0.bits32 = frag->size;
+		txq->ring[w].word1.bits32 = 0;
+		txq->ring[w].word2.bits32 = mapping;
+		txq->ring[w].word3.bits32 = 0;
+
+		netif_info(gmac, tx_queued, dev,
+			"txq%ld[%u]: 0x%08x 0x%08x 0x%08x 0x%08x, data %u @ %p+0x%03x\n",
+			txq - gmac->txq, w,
+			txq->ring[w].word0.bits32, txq->ring[w].word1.bits32,
+			txq->ring[w].word2.bits32, txq->ring[w].word3.bits32,
+			frag->size, frag->page, frag->page_offset);
+
+		++frag;
+	}
+
+	txq->ring[w].word3.bits32 |= EOFIE_BIT | EOF_BIT;
+
+	return RWPTR_NEXT(w, gmac->txq_order);
+
+map_error:
+	while (w != desc) {
+		dma_unmap_page(dma_dev, txq->ring[w].word2.buf_adr,
+			txq->ring[w].word0.bits.buffer_size, DMA_TO_DEVICE);
+		w = RWPTR_PREV(w, gmac->txq_order);
+	}
+
+	dma_unmap_single(dma_dev, txq->ring[w].word2.buf_adr,
+		txq->ring[w].word0.bits.buffer_size, DMA_TO_DEVICE);
+
+map1_error:
+	netif_info(gmac, tx_err, dev,
+		"txq%ld: DMA mapping error\n", txq - gmac->txq);
+
+	return -ENOMEM;
+}
+
+static int gmac_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	struct gmac_private *gmac = netdev_to_gmac(dev);
+	struct toe_private *toe = netdev_to_toe(dev);
+
+	void __iomem *ptr_reg;
+	struct gmac_txq *txq;
+	struct netdev_queue *ntxq;
+	int w, nw, txq_num, nfrags;
+	unsigned long flags;
+
+	SKB_FRAG_ASSERT(skb);
+
+	txq_num = skb_get_queue_mapping(skb);
+	ptr_reg = gmac_dma_reg(dev, GMAC_SW_TX_QUEUE_PTR_REG(txq_num));
+	txq = &gmac->txq[txq_num];
+	ntxq = netdev_get_tx_queue(dev, txq_num);
+
+	netif_info(gmac, tx_queued, dev, "txq%u: %u,%u,%u ? %p (%u @ %p) /%u\n",
+		txq_num, txq->cptr, GET_RPTR(ptr_reg), GET_WPTR(ptr_reg),
+		skb, skb->len, skb->data, skb_shinfo(skb)->gso_size);
+	if (netif_msg_pktdata(gmac))
+	        print_hex_dump(KERN_DEBUG, "TX: ", DUMP_PREFIX_OFFSET, 16, 1,
+                        skb->data, skb_headlen(skb), true);
+
+	u64_stats_update_begin(&gmac->tx_stats_syncp);
+
+	if (skb->len >= 0x10000)
+		goto out_drop_free;
+
+	w = GET_WPTR(ptr_reg);
+	spin_lock_irqsave(&toe->irq_lock, flags);
+	nw = RWPTR_DISTANCE(w, txq->cptr - 1, gmac->txq_order);
+	if (!nw) {
+		netif_tx_stop_queue(ntxq);
+		__gmac_enable_txfin_irq(dev, txq_num, 1);
+		spin_unlock_irqrestore(&toe->irq_lock, flags);
+		goto out_drop_free;
+	}
+	spin_unlock_irqrestore(&toe->irq_lock, flags);
+
+	nfrags = skb_shinfo(skb)->nr_frags;
+	if (nw <= nfrags || nfrags >= TX_MAX_FRAGS) {
+		if (skb_linearize(skb))
+			goto out_drop;
+		gmac->tx_frags_linearized++;
+	} else
+		gmac->tx_frag_stats[nfrags]++;
+
+	txq->skb[w] = skb;
+
+	w = gmac_map_tx_bufs(dev, skb, txq, w,
+		(skb->ip_summed != CHECKSUM_NONE ? TSS_CHECKUM_ENABLE : 0)
+	);
+
+	if (w < 0)
+		goto out_drop_free;
+
+	if (skb->ip_summed == CHECKSUM_PARTIAL)
+		gmac->tx_hw_csummed++;
+
+	ntxq->tx_bytes += skb->len;
+	ntxq->tx_packets++;
+
+	u64_stats_update_end(&gmac->tx_stats_syncp);
+
+	netif_info(gmac, tx_queued, dev, "txq%u: %u,%u,%u + %p\n",
+		txq_num, txq->cptr, GET_RPTR(ptr_reg), w, skb);
+
+	SET_WPTR(ptr_reg, w);
+	ntxq->trans_start = jiffies;
+
+	/* stats updated on tx completion */
+	return NETDEV_TX_OK;
+
+out_drop_free:
+	dev_kfree_skb(skb);
+out_drop:
+	ntxq->tx_dropped++;
+	u64_stats_update_end(&gmac->tx_stats_syncp);
+	return NETDEV_TX_OK;
+}
+
+static void gmac_unmap_rx_frags(struct toe_private *toe, struct sk_buff *skb)
+{
+	int i = skb_shinfo(skb)->nr_frags;
+	skb_frag_t *frag = &skb_shinfo(skb)->frags[0];
+
+	for (; i--; frag++)
+		dma_unmap_page(toe->dev,
+			page_to_dma(toe->dev, frag->page) + frag->page_offset,
+			frag->size, DMA_FROM_DEVICE);
+}
+
+static struct sk_buff *gmac_drop_napi_skb(struct gmac_private *gmac,
+	struct toe_private *toe)
+{
+	gmac_unmap_rx_frags(toe, napi_get_frags(&gmac->napi));
+
+	napi_free_frags(&gmac->napi);
+
+	u64_stats_update_begin(&gmac->rx_stats_syncp);
+	gmac->stats.rx_dropped++;
+	u64_stats_update_end(&gmac->rx_stats_syncp);
+
+	return NULL;
+}
+
+static unsigned gmac_rx(struct net_device *dev, unsigned budget)
+{
+	struct gmac_private *gmac = netdev_to_gmac(dev);
+	struct toe_private *toe = netdev_to_toe(dev);
+	void __iomem *ptr_reg = gmac->rxq_rwptr;
+
+	unsigned i, mask = __RWPTR_MASK(gmac->rxq_order);
+	GMAC_RXDESC_T *rx = NULL;
+	struct sk_buff *skb = NULL;
+	struct page *page, *last_page = NULL;
+	unsigned page_offs, next_offs = 0;
+	int frag_nr = 0;
+	unsigned pkt_size = 0, rx_status, rx_csum, frag_size;
+
+	netif_info(gmac, rx_status, dev, "rxq: %u,%u\n",
+		 GET_RPTR(ptr_reg), GET_WPTR(ptr_reg));
+
+	for (i = GET_RPTR(ptr_reg); budget && i != GET_WPTR(ptr_reg); i = __RWPTR_NEXT(i, mask)) {
+		rx = &gmac->rxq_ring[i];
+
+		page = dma_to_page(toe->dev, rx->word2.buf_adr);
+		page_offs = rx->word2.buf_adr & ~PAGE_MASK;
+
+		netif_info(gmac, rx_status, dev,
+			"rxq[%u]: 0x%08x 0x%08x 0x%08x 0x%08x, page %p, offs 0x%04x\n",
+			i, rx->word0.bits32, rx->word1.bits32,
+			rx->word2.bits32, rx->word3.bits32, page, page_offs);
+
+		if (unlikely(!rx->word2.buf_adr)) {
+			netif_err(gmac, rx_status, dev,
+				"rxq[%u]: HW BUG: zero DMA descriptor\n", i);
+			if (skb)
+				skb = gmac_drop_napi_skb(gmac, toe);
+			continue;
+		}
+
+		if (rx->word3.bits32 & SOF_BIT) {
+			if (skb)
+				gmac_drop_napi_skb(gmac, toe);
+
+			/* update packet stats; skb == NULL here */
+
+			pkt_size = rx->word1.bits.byte_count;
+			rx_status = rx->word0.bits.status;
+			rx_csum = rx->word0.bits.chksum_status;
+
+			u64_stats_update_begin(&gmac->rx_stats_syncp);
+
+			gmac->rx_stats[rx_status]++;
+			gmac->rx_csum_stats[rx_csum]++;
+
+			if (rx->word0.bits.derr || rx->word0.bits.perr ||
+			    rx_status || pkt_size < ETH_ZLEN ||
+			    rx_csum >= RX_CHKSUM_IP_ERR_UNKNOWN) {
+				gmac->stats.rx_errors++;
+
+				if (pkt_size < ETH_ZLEN || RX_ERROR_LENGTH(rx_status))
+					gmac->stats.rx_length_errors++;
+				if (RX_ERROR_OVER(rx_status))
+					gmac->stats.rx_over_errors++;
+				if (RX_ERROR_CRC(rx_status))
+					gmac->stats.rx_crc_errors++;
+				if (RX_ERROR_FRAME(rx_status))
+					gmac->stats.rx_frame_errors++;
+				if (RX_ERROR_FIFO(rx_status))
+					gmac->stats.rx_fifo_errors++;
+
+				u64_stats_update_end(&gmac->rx_stats_syncp);
+
+				skb = NULL;
+				put_page(page);
+				continue;
+			}
+
+			skb = napi_get_frags(&gmac->napi);
+			if (!skb) {
+				gmac->stats.rx_dropped++;
+				u64_stats_update_end(&gmac->rx_stats_syncp);
+
+				put_page(page);
+				continue;
+			}
+
+			skb->dev = dev;
+
+			if (rx_csum == RX_CHKSUM_IP_UDP_TCP_OK)
+				skb->ip_summed = CHECKSUM_UNNECESSARY;
+
+			gmac->stats.rx_bytes += pkt_size;
+			gmac->stats.rx_packets++;
+
+			u64_stats_update_end(&gmac->rx_stats_syncp);
+
+			frag_nr = -1;
+			last_page = NULL;
+			page_offs += RX_INSERT_BYTES;
+		} else if (!skb) {
+			put_page(page);
+			continue;
+		}
+
+		/* append page frag to skb */
+
+		if (rx->word3.bits32 & EOF_BIT)
+			frag_size = pkt_size;
+		else {
+			frag_size = 1 << toe->freeq_frag_order;
+			if (rx->word3.bits32 & SOF_BIT)
+				frag_size -= RX_INSERT_BYTES;
+		}
+
+		if (page == last_page && page_offs == next_offs) {
+			skb_shinfo(skb)->frags[frag_nr].size += frag_size;
+			put_page(page);
+		} else if (likely(++frag_nr != MAX_SKB_FRAGS))
+			skb_fill_page_desc(skb, frag_nr, page, page_offs, frag_size);
+		else {
+			skb = gmac_drop_napi_skb(gmac, toe);
+			put_page(page);
+			continue;
+		}
+
+		last_page = page;
+		next_offs = page_offs + frag_size;
+
+		skb->len += frag_size;
+		skb->data_len += frag_size;
+		skb->truesize += frag_size;
+
+		/* receive */
+
+		if (rx->word3.bits32 & EOF_BIT) {
+			gmac_unmap_rx_frags(toe, skb);
+			napi_gro_frags(&gmac->napi);
+			skb = NULL;
+			--budget;
+		}
+	}
+
+	SET_RPTR(ptr_reg, i);
+
+	if (rx)
+		dev->last_rx = jiffies;
+
+	if (skb)
+		gmac_drop_napi_skb(gmac, toe);
+
+	return budget;
+}
+
+#define GMAC0_IRQ0_2 (GMAC0_TXDERR_INT_BIT|GMAC0_TXPERR_INT_BIT| \
+	GMAC0_RXDERR_INT_BIT|GMAC0_RXPERR_INT_BIT)
+#define GMAC0_IRQ0_6 (GMAC0_SWTQ00_EOF_INT_BIT|GMAC0_SWTQ00_FIN_INT_BIT)
+#define GMAC0_IRQ4_8 (GMAC0_MIB_INT_BIT)
+
+static void gmac_enable_irq(struct net_device *dev, int enable)
+{
+	struct toe_private *toe = netdev_to_toe(dev);
+	unsigned long flags;
+	unsigned val, mask;
+
+	spin_lock_irqsave(&toe->irq_lock, flags);
+
+	mask = (GMAC0_IRQ0_2 << (dev->dev_id * 2)) |
+		(GMAC0_IRQ0_6 << (dev->dev_id * 6));
+	val = __raw_readl(toe_reg(toe, GLOBAL_INTERRUPT_ENABLE_0_REG));
+	val = enable ? (val | mask) : (val & ~mask);
+	__raw_writel(val, toe_reg(toe, GLOBAL_INTERRUPT_ENABLE_0_REG));
+
+	mask = DEFAULT_Q0_INT_BIT << dev->dev_id;
+	val = __raw_readl(toe_reg(toe, GLOBAL_INTERRUPT_ENABLE_1_REG));
+	val = enable ? (val | mask) : (val & ~mask);
+	__raw_writel(val, toe_reg(toe, GLOBAL_INTERRUPT_ENABLE_1_REG));
+
+	mask = GMAC0_IRQ4_8 << (dev->dev_id * 8);
+	val = __raw_readl(toe_reg(toe, GLOBAL_INTERRUPT_ENABLE_4_REG));
+	val = enable ? (val | mask) : (val & ~mask);
+	__raw_writel(val, toe_reg(toe, GLOBAL_INTERRUPT_ENABLE_4_REG));
+
+	spin_unlock_irqrestore(&toe->irq_lock, flags);
+}
+
+static void __gmac_enable_txfin_irq(struct net_device *dev, int txq, int enable)
+{
+	struct toe_private *toe = netdev_to_toe(dev);
+	unsigned val, mask;
+
+	mask = GMAC0_SWTQ00_FIN_INT_BIT << (6 * dev->dev_id + txq);
+	val = __raw_readl(toe_reg(toe, GLOBAL_INTERRUPT_ENABLE_0_REG));
+	val = enable ? (val | mask) : (val & ~mask);
+	__raw_writel(val, toe_reg(toe, GLOBAL_INTERRUPT_ENABLE_0_REG));
+}
+
+static void gmac_enable_rx_irq(struct net_device *dev, int enable)
+{
+	struct toe_private *toe = netdev_to_toe(dev);
+	unsigned long flags;
+	unsigned val, mask;
+
+	spin_lock_irqsave(&toe->irq_lock, flags);
+
+	mask = DEFAULT_Q0_INT_BIT << dev->dev_id;
+	val = __raw_readl(toe_reg(toe, GLOBAL_INTERRUPT_ENABLE_1_REG));
+	val = enable ? (val | mask) : (val & ~mask);
+	__raw_writel(val, toe_reg(toe, GLOBAL_INTERRUPT_ENABLE_1_REG));
+
+	spin_unlock_irqrestore(&toe->irq_lock, flags);
+}
+
+static int gmac_napi_poll(struct napi_struct *napi, int max_work)
+{
+	struct gmac_private *gmac = napi_to_gmac(napi);
+	unsigned work_left;
+
+	work_left = gmac_rx(napi->dev, max_work);
+
+	if (work_left != max_work) {
+		if (work_left) {
+			struct toe_private *toe = netdev_to_toe(napi->dev);
+			/* we've cleared the queue, ack rx interrupt;
+			 * on next poll the interrupt will be enabled
+			 * if the queue stays empty
+			 */
+			__raw_writel(DEFAULT_Q0_INT_BIT << napi->dev->dev_id,
+				toe_reg(toe, GLOBAL_INTERRUPT_STATUS_1_REG));
+		}
+		return max_work - work_left;
+	}
+
+	napi_complete(napi);
+	u64_stats_update_begin(&gmac->rx_stats_syncp);
+	++gmac->rx_napi_exits;
+	u64_stats_update_end(&gmac->rx_stats_syncp);
+	gmac_enable_rx_irq(napi->dev, 1);
+
+	return 0;
+}
+
+static void gmac_dump_dma_state(struct net_device *dev)
+{
+	struct gmac_private *gmac = netdev_to_gmac(dev);
+	struct toe_private *toe = netdev_to_toe(dev);
+	void __iomem *ptr_reg;
+	unsigned reg[5];
+
+	/* Interrupt status */
+	reg[0] = __raw_readl(toe_reg(toe, GLOBAL_INTERRUPT_STATUS_0_REG));
+	reg[1] = __raw_readl(toe_reg(toe, GLOBAL_INTERRUPT_STATUS_1_REG));
+	reg[2] = __raw_readl(toe_reg(toe, GLOBAL_INTERRUPT_STATUS_2_REG));
+	reg[3] = __raw_readl(toe_reg(toe, GLOBAL_INTERRUPT_STATUS_3_REG));
+	reg[4] = __raw_readl(toe_reg(toe, GLOBAL_INTERRUPT_STATUS_4_REG));
+	netdev_err(dev, "IRQ status: 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n",
+		reg[0], reg[1], reg[2], reg[3], reg[4]);
+
+	/* Interrupt enable */
+	reg[0] = __raw_readl(toe_reg(toe, GLOBAL_INTERRUPT_ENABLE_0_REG));
+	reg[1] = __raw_readl(toe_reg(toe, GLOBAL_INTERRUPT_ENABLE_1_REG));
+	reg[2] = __raw_readl(toe_reg(toe, GLOBAL_INTERRUPT_ENABLE_2_REG));
+	reg[3] = __raw_readl(toe_reg(toe, GLOBAL_INTERRUPT_ENABLE_3_REG));
+	reg[4] = __raw_readl(toe_reg(toe, GLOBAL_INTERRUPT_ENABLE_4_REG));
+	netdev_err(dev, "IRQ enable: 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n",
+		reg[0], reg[1], reg[2], reg[3], reg[4]);
+
+	/* RX DMA status */
+	reg[0] = __raw_readl(gmac_dma_reg(dev, GMAC_DMA_RX_FIRST_DESC_REG));
+	reg[1] = __raw_readl(gmac_dma_reg(dev, GMAC_DMA_RX_CURR_DESC_REG));
+	reg[2] = GET_RPTR(gmac->rxq_rwptr);
+	reg[3] = GET_WPTR(gmac->rxq_rwptr);
+	netdev_err(dev, "RX DMA regs: 0x%08x 0x%08x, ptr: %u %u\n",
+		reg[0], reg[1], reg[2], reg[3]);
+
+	reg[0] = __raw_readl(gmac_dma_reg(dev, GMAC_DMA_RX_DESC_WORD0_REG));
+	reg[1] = __raw_readl(gmac_dma_reg(dev, GMAC_DMA_RX_DESC_WORD1_REG));
+	reg[2] = __raw_readl(gmac_dma_reg(dev, GMAC_DMA_RX_DESC_WORD2_REG));
+	reg[3] = __raw_readl(gmac_dma_reg(dev, GMAC_DMA_RX_DESC_WORD3_REG));
+	netdev_err(dev, "RX DMA descriptor: 0x%08x 0x%08x 0x%08x 0x%08x\n",
+		reg[0], reg[1], reg[2], reg[3]);
+
+	/* TX DMA status */
+	ptr_reg = gmac_dma_reg(dev, GMAC_SW_TX_QUEUE0_PTR_REG);
+
+	reg[0] = __raw_readl(gmac_dma_reg(dev, GMAC_DMA_TX_FIRST_DESC_REG));
+	reg[1] = __raw_readl(gmac_dma_reg(dev, GMAC_DMA_TX_CURR_DESC_REG));
+	reg[2] = GET_RPTR(ptr_reg);
+	reg[3] = GET_WPTR(ptr_reg);
+	netdev_err(dev, "TX DMA regs: 0x%08x 0x%08x, ptr: %u %u\n",
+		reg[0], reg[1], reg[2], reg[3]);
+
+	reg[0] = __raw_readl(gmac_dma_reg(dev, GMAC_DMA_TX_DESC_WORD0_REG));
+	reg[1] = __raw_readl(gmac_dma_reg(dev, GMAC_DMA_TX_DESC_WORD1_REG));
+	reg[2] = __raw_readl(gmac_dma_reg(dev, GMAC_DMA_TX_DESC_WORD2_REG));
+	reg[3] = __raw_readl(gmac_dma_reg(dev, GMAC_DMA_TX_DESC_WORD3_REG));
+	netdev_err(dev, "TX DMA descriptor: 0x%08x 0x%08x 0x%08x 0x%08x\n",
+		reg[0], reg[1], reg[2], reg[3]);
+
+	/* FREE queues status */
+	ptr_reg = toe_reg(toe, GLOBAL_SWFQ_RWPTR_REG);
+
+	reg[0] = GET_RPTR(ptr_reg);
+	reg[1] = GET_WPTR(ptr_reg);
+
+	ptr_reg = toe_reg(toe, GLOBAL_HWFQ_RWPTR_REG);
+
+	reg[2] = GET_RPTR(ptr_reg);
+	reg[3] = GET_WPTR(ptr_reg);
+	netdev_err(dev, "FQ SW ptr: %u %u, HW ptr: %u %u\n",
+		reg[0], reg[1], reg[2], reg[3]);
+}
+
+static void gmac_update_hw_stats(struct net_device *dev)
+{
+	struct gmac_private *gmac = netdev_to_gmac(dev);
+	struct toe_private *toe = netdev_to_toe(dev);
+	unsigned long flags;
+	unsigned int rx_discards, rx_mcast, rx_bcast;
+
+	spin_lock_irqsave(&toe->irq_lock, flags);
+	u64_stats_update_begin(&gmac->ir_stats_syncp);
+
+	gmac->hw_stats[0] += rx_discards = __raw_readl(gmac_ctl_reg(dev, GMAC_IN_DISCARDS));
+	gmac->hw_stats[1] += __raw_readl(gmac_ctl_reg(dev, GMAC_IN_ERRORS));
+	gmac->hw_stats[2] += rx_mcast = __raw_readl(gmac_ctl_reg(dev, GMAC_IN_MCAST));
+	gmac->hw_stats[3] += rx_bcast = __raw_readl(gmac_ctl_reg(dev, GMAC_IN_BCAST));
+	gmac->hw_stats[4] += __raw_readl(gmac_ctl_reg(dev, GMAC_IN_MAC1));
+	gmac->hw_stats[5] += __raw_readl(gmac_ctl_reg(dev, GMAC_IN_MAC2));
+
+	gmac->stats.rx_missed_errors += rx_discards;
+	gmac->stats.multicast += rx_mcast;
+	gmac->stats.multicast += rx_bcast;
+
+	__raw_writel(GMAC0_MIB_INT_BIT << (dev->dev_id * 8),
+		toe_reg(toe, GLOBAL_INTERRUPT_STATUS_4_REG));
+
+	u64_stats_update_end(&gmac->ir_stats_syncp);
+	spin_unlock_irqrestore(&toe->irq_lock, flags);
+}
+
+static inline unsigned gmac_get_intr_flags(struct net_device *dev, int i)
+{
+	struct gmac_private *gmac = netdev_to_gmac(dev);
+	struct toe_private *toe = netdev_to_toe(dev);
+	void __iomem *irqif_reg, *irqen_reg;
+	unsigned offs, val;
+
+	offs = i * (GLOBAL_INTERRUPT_STATUS_1_REG - GLOBAL_INTERRUPT_STATUS_0_REG);
+
+	irqif_reg = toe_reg(toe, GLOBAL_INTERRUPT_STATUS_0_REG + offs);
+	irqen_reg = toe_reg(toe, GLOBAL_INTERRUPT_ENABLE_0_REG + offs);
+
+	val = __raw_readl(irqif_reg) & __raw_readl(irqen_reg);
+	if (val)
+		netif_info(gmac, intr, dev, "irq: val%d&en = 0x%08x\n", i, val);
+
+	return val;
+}
+
+static irqreturn_t gmac_interrupt(int irq, void *data)
+{
+	struct net_device *dev = data;
+	struct gmac_private *gmac = netdev_to_gmac(dev);
+	unsigned val, orr = 0;
+
+
+	orr |= val = gmac_get_intr_flags(dev, 0);
+
+	if (val & (GMAC0_IRQ0_2 << (dev->dev_id * 2))) {
+		/* oh, crap. */
+		netif_err(gmac, intr, dev, "hw failure/sw bug\n");
+		gmac_dump_dma_state(dev);
+
+		/* don't know how to recover, just reduce losses */
+		gmac_enable_irq(dev, 0);
+		return IRQ_HANDLED;
+	}
+
+	if (val & (GMAC0_IRQ0_6 << (dev->dev_id * 6)))
+		gmac_tx_interrupt(dev, 0);
+
+
+	orr |= val = gmac_get_intr_flags(dev, 1);
+
+	if (val & (DEFAULT_Q0_INT_BIT << dev->dev_id)) {
+		gmac_enable_rx_irq(dev, 0);
+		napi_schedule(&gmac->napi);
+	}
+
+
+	orr |= val = gmac_get_intr_flags(dev, 4);
+
+	if (val & (GMAC0_MIB_INT_BIT << (dev->dev_id * 8)))
+		gmac_update_hw_stats(dev);
+
+
+	return orr ? IRQ_HANDLED : IRQ_NONE;
+}
+
+static int gmac_open_running(struct net_device *dev)
+{
+	struct gmac_private *gmac = netdev_to_gmac(dev);
+	int err;
+
+	err = gmac_setup_rxq(dev);
+	if (unlikely(err))
+		return err;
+
+	err = gmac_setup_txqs(dev);
+	if (unlikely(err)) {
+		gmac_cleanup_rxq(dev);
+		return err;
+	}
+
+	napi_enable(&gmac->napi);
+	gmac_hw_start(dev);
+	gmac_enable_irq(dev, 1);
+	gmac_enable_tx_rx(dev);
+	netif_tx_start_all_queues(dev);
+
+	gmac->in_reset = 0;
+
+	return 0;
+}
+
+static void gmac_stop_running(struct net_device *dev)
+{
+	struct gmac_private *gmac = netdev_to_gmac(dev);
+
+	netif_tx_stop_all_queues(dev);
+
+	gmac_disable_tx_rx(dev);
+	gmac_hw_stop(dev);
+
+	napi_disable(&gmac->napi);
+
+	gmac_enable_irq(dev, 0);
+
+	gmac_cleanup_txqs(dev);
+	gmac_cleanup_rxq(dev);
+
+	gmac->in_reset = 1;
+}
+
+static int gmac_open(struct net_device *dev)
+{
+	struct gmac_private *gmac = netdev_to_gmac(dev);
+	int err;
+
+	if (!dev->phydev) {
+		err = gmac_setup_phy(dev);
+		if (err) {
+			netif_err(gmac, ifup, dev, "PHY init failed: %d\n", err);
+			return err;
+		}
+	}
+
+	err = request_irq(dev->irq, gmac_interrupt, IRQF_SHARED, dev->name, dev);
+	if (unlikely(err))
+		return err;
+
+	netif_carrier_off(dev);
+	phy_start(dev->phydev);
+
+	err = gmac_open_running(dev);
+	if (likely(!err))
+		return 0;
+
+	phy_stop(dev->phydev);
+	free_irq(dev->irq, dev);
+	return err;
+}
+
+static int gmac_stop(struct net_device *dev)
+{
+	struct gmac_private *gmac = netdev_to_gmac(dev);
+
+	if (!gmac->in_reset)
+		gmac_stop_running(dev);
+
+	phy_stop(dev->phydev);
+	free_irq(dev->irq, dev);
+
+	gmac_update_hw_stats(dev);
+
+	return 0;
+}
+
+static void gmac_set_multicast_list(struct net_device *dev)
+{
+	struct netdev_hw_addr *ha;
+	__u32 mc_filter[2];
+	unsigned bit_nr;
+
+	if (dev->flags & IFF_ALLMULTI)
+		return;
+
+	mc_filter[1] = mc_filter[0] = 0;
+	netdev_for_each_mc_addr(ha, dev) {
+		bit_nr = ~crc32_be(~0, ha->addr, ETH_ALEN) & 0x3f;
+		mc_filter[bit_nr >> 5] |= 1 << (bit_nr & 0x1f);
+	}
+
+	__raw_writel(mc_filter[0], gmac_ctl_reg(dev, GMAC_MCAST_FIL0));
+	__raw_writel(mc_filter[1], gmac_ctl_reg(dev, GMAC_MCAST_FIL1));
+}
+
+static void gmac_set_rx_mode(struct net_device *dev)
+{
+	GMAC_RX_FLTR_T filter = { .bits = {
+		.broadcast = 1,
+		.multicast = 1,
+		.unicast = 1,
+	} };
+
+	if (dev->flags & IFF_PROMISC) {
+		filter.bits.error = 1;
+		filter.bits.promiscuous = 1;
+	} else if (dev->flags & IFF_ALLMULTI) {
+		__raw_writel(~0, gmac_ctl_reg(dev, GMAC_MCAST_FIL0));
+		__raw_writel(~0, gmac_ctl_reg(dev, GMAC_MCAST_FIL1));
+	} else {
+		gmac_set_multicast_list(dev);
+	}
+
+	__raw_writel(filter.bits32, gmac_ctl_reg(dev, GMAC_RX_FLTR));
+}
+
+static void __gmac_set_mac_address(struct net_device *dev)
+{
+	__le32 addr[3];
+
+	memset(addr, 0, sizeof(addr));
+	memcpy(addr, dev->dev_addr, ETH_ALEN);
+
+	__raw_writel(le32_to_cpu(addr[0]), gmac_ctl_reg(dev, GMAC_STA_ADD0));
+	__raw_writel(le32_to_cpu(addr[1]), gmac_ctl_reg(dev, GMAC_STA_ADD1));
+	__raw_writel(le32_to_cpu(addr[2]), gmac_ctl_reg(dev, GMAC_STA_ADD2));
+}
+
+static int gmac_set_mac_address(struct net_device *dev, void *addr)
+{
+	struct sockaddr *sa = addr;
+
+	memcpy(dev->dev_addr, sa->sa_data, ETH_ALEN);
+	__gmac_set_mac_address(dev);
+
+	return 0;
+}
+
+static void gmac_clear_hw_stats(struct net_device *dev)
+{
+	__raw_readl(gmac_ctl_reg(dev, GMAC_IN_DISCARDS));
+	__raw_readl(gmac_ctl_reg(dev, GMAC_IN_ERRORS));
+	__raw_readl(gmac_ctl_reg(dev, GMAC_IN_MCAST));
+	__raw_readl(gmac_ctl_reg(dev, GMAC_IN_BCAST));
+	__raw_readl(gmac_ctl_reg(dev, GMAC_IN_MAC1));
+	__raw_readl(gmac_ctl_reg(dev, GMAC_IN_MAC2));
+}
+
+static struct rtnl_link_stats64 *gmac_get_stats64(struct net_device *dev,
+	struct rtnl_link_stats64 *storage)
+{
+	struct gmac_private *gmac = netdev_to_gmac(dev);
+	unsigned int start;
+
+	gmac_update_hw_stats(dev);
+	dev_txq_stats_fold(dev, storage);
+
+	/* racing with RX NAPI */
+	do {
+		start = u64_stats_fetch_begin(&gmac->rx_stats_syncp);
+
+		storage->rx_packets = gmac->stats.rx_packets;
+		storage->rx_bytes = gmac->stats.rx_bytes;
+		storage->rx_errors = gmac->stats.rx_errors;
+		storage->rx_dropped = gmac->stats.rx_dropped;
+
+		storage->rx_length_errors = gmac->stats.rx_length_errors;
+		storage->rx_over_errors = gmac->stats.rx_over_errors;
+		storage->rx_crc_errors = gmac->stats.rx_crc_errors;
+		storage->rx_frame_errors = gmac->stats.rx_frame_errors;
+		storage->rx_fifo_errors = gmac->stats.rx_fifo_errors;
+
+	} while (u64_stats_fetch_retry(&gmac->rx_stats_syncp, start));
+
+	/* racing with MIB and TX completion interrupts */
+	do {
+		start = u64_stats_fetch_begin(&gmac->ir_stats_syncp);
+
+		storage->tx_errors = gmac->stats.tx_errors;
+
+		storage->multicast = gmac->stats.multicast;
+		storage->rx_missed_errors = gmac->stats.rx_missed_errors;
+
+	} while (u64_stats_fetch_retry(&gmac->ir_stats_syncp, start));
+
+	storage->rx_dropped += storage->rx_missed_errors;
+
+	return storage;
+}
+
+static int gmac_change_mtu(struct net_device *dev, int new_mtu)
+{
+	int max_len = gmac_pick_rx_max_len(new_mtu);
+
+	if (max_len < 0)
+		return -EINVAL;
+
+	gmac_disable_tx_rx(dev);
+
+	dev->mtu = new_mtu;
+	gmac_update_config0_reg(dev,
+		max_len << CONFIG0_MAXLEN_SHIFT,
+		CONFIG0_MAXLEN_MASK);
+
+	gmac_enable_tx_rx(dev);
+
+	return 0;
+}
+
+static u32 gmac_get_rx_csum(struct net_device *dev)
+{
+	return !!(__raw_readl(gmac_ctl_reg(dev, GMAC_CONFIG0)) & CONFIG0_RX_CHKSUM);
+}
+
+static int gmac_set_rx_csum(struct net_device *dev, u32 enable)
+{
+	gmac_update_config0_reg(dev,
+		enable ? CONFIG0_RX_CHKSUM : 0, CONFIG0_RX_CHKSUM);
+
+	return 0;
+}
+
+static int gmac_set_tso(struct net_device *dev, u32 data)
+{
+	if (data)
+		dev->features |= NETIF_TSO_FEATURES;
+	else
+		dev->features &= ~NETIF_TSO_FEATURES;
+
+	return 0;
+}
+
+static int gmac_get_sset_count(struct net_device *dev, int sset)
+{
+	return sset == ETH_SS_STATS ? GMAC_STATS_NUM : 0;
+}
+
+static void gmac_get_strings(struct net_device *dev, u32 stringset, u8 *data)
+{
+	if (stringset != ETH_SS_STATS)
+		return;
+
+	memcpy(data, gmac_stats_strings, sizeof(gmac_stats_strings));
+}
+
+static void gmac_get_ethtool_stats(struct net_device *dev,
+	struct ethtool_stats *estats, u64 *values)
+{
+	struct gmac_private *gmac = netdev_to_gmac(dev);
+	unsigned int start;
+	u64 *p;
+	int i;
+
+	gmac_update_hw_stats(dev);
+
+	/* racing with MIB interrupt */
+	do {
+		p = values;
+		start = u64_stats_fetch_begin(&gmac->ir_stats_syncp);
+
+		for (i = 0; i < RX_STATS_NUM; ++i)
+			*p++ = gmac->hw_stats[i];
+	
+	} while (u64_stats_fetch_retry(&gmac->ir_stats_syncp, start));
+	values = p;
+
+	/* racing with RX NAPI */
+	do {
+		p = values;
+		start = u64_stats_fetch_begin(&gmac->rx_stats_syncp);
+
+		for (i = 0; i < RX_STATUS_NUM; ++i)
+			*p++ = gmac->rx_stats[i];
+		for (i = 0; i < RX_CHKSUM_NUM; ++i)
+			*p++ = gmac->rx_csum_stats[i];
+		*p++ = gmac->rx_napi_exits;
+
+	} while (u64_stats_fetch_retry(&gmac->rx_stats_syncp, start));
+	values = p;
+
+	/* racing with TX start_xmit */
+	do {
+		p = values;
+		start = u64_stats_fetch_begin(&gmac->tx_stats_syncp);
+
+		for (i = 0; i < TX_MAX_FRAGS; ++i)
+			*values++ = gmac->tx_frag_stats[i];
+		*values++ = gmac->tx_frags_linearized;
+		*values++ = gmac->tx_hw_csummed;
+
+	} while (u64_stats_fetch_retry(&gmac->tx_stats_syncp, start));
+}
+
+static int gmac_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+	if (!dev->phydev)
+		return -ENXIO;
+	return phy_ethtool_gset(dev->phydev, cmd);
+}
+
+static int gmac_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+	if (!dev->phydev)
+		return -ENXIO;
+	return phy_ethtool_sset(dev->phydev, cmd);
+}
+
+static int gmac_nway_reset(struct net_device *dev)
+{
+	if (!dev->phydev)
+		return -ENXIO;
+	return phy_start_aneg(dev->phydev);
+}
+
+static void gmac_get_pauseparam(struct net_device *dev,
+	struct ethtool_pauseparam *pparam)
+{
+	GMAC_CONFIG0_T config0;
+
+	config0.bits32 = __raw_readl(gmac_ctl_reg(dev, GMAC_CONFIG0));
+
+	pparam->rx_pause = config0.bits.rx_fc_en;
+	pparam->tx_pause = config0.bits.tx_fc_en;
+	pparam->autoneg = true;
+}
+
+static void gmac_get_ringparam(struct net_device *dev,
+	struct ethtool_ringparam *rp)
+{
+	struct gmac_private *gmac = netdev_to_gmac(dev);
+	GMAC_CONFIG0_T config0;
+
+	config0.bits32 = __raw_readl(gmac_ctl_reg(dev, GMAC_CONFIG0));
+
+	rp->rx_max_pending = 1 << 15;
+	rp->rx_mini_max_pending = 0;
+	rp->rx_jumbo_max_pending = 0;
+	rp->tx_max_pending = 1 << 15;
+
+	rp->rx_pending = 1 << gmac->rxq_order;
+	rp->rx_mini_pending = 0;
+	rp->rx_jumbo_pending = 0;
+	rp->tx_pending = 1 << gmac->txq_order;
+}
+
+static int toe_resize_freeq(struct toe_private *toe, int changing_dev_id);
+
+static int gmac_set_ringparam(struct net_device *dev,
+	struct ethtool_ringparam *rp)
+{
+	struct gmac_private *gmac = netdev_to_gmac(dev);
+	struct toe_private *toe = netdev_to_toe(dev);
+	int err = 0;
+
+	if (netif_running(dev))
+		return -EBUSY;
+
+	if (rp->rx_pending) {
+		gmac->rxq_order = min(15, ilog2(rp->rx_pending - 1) + 1);
+		err = toe_resize_freeq(toe, dev->dev_id);
+	}
+
+	if (rp->tx_pending)
+		gmac->txq_order = min(15, ilog2(rp->tx_pending - 1) + 1);
+
+	return err;
+}
+
+static int gmac_get_coalesce(struct net_device *dev,
+	struct ethtool_coalesce *ecmd)
+{
+	struct gmac_private *gmac = netdev_to_gmac(dev);
+
+	ecmd->rx_max_coalesced_frames = 1;
+	ecmd->tx_max_coalesced_frames = gmac->irq_every_tx_packets;
+
+	return 0;
+}
+
+static int gmac_set_coalesce(struct net_device *dev,
+	struct ethtool_coalesce *ecmd)
+{
+	struct gmac_private *gmac = netdev_to_gmac(dev);
+
+	if (ecmd->tx_max_coalesced_frames < 1)
+		return -EINVAL;
+	if (ecmd->tx_max_coalesced_frames >= 1 << gmac->txq_order)
+		return -EINVAL;
+
+	gmac->irq_every_tx_packets = ecmd->tx_max_coalesced_frames;
+
+	return 0;
+}
+
+static u32 gmac_get_msglevel(struct net_device *dev)
+{
+	struct gmac_private *gmac = netdev_to_gmac(dev);
+	return gmac->msg_enable;
+}
+
+static void gmac_set_msglevel(struct net_device *dev, u32 level)
+{
+	struct gmac_private *gmac = netdev_to_gmac(dev);
+	gmac->msg_enable = level;
+}
+
+static void gmac_get_drvinfo(struct net_device *dev,
+	struct ethtool_drvinfo *info)
+{
+	strcpy(info->driver, "sl351x");
+	strcpy(info->version, "mq-k");
+	strcpy(info->bus_info, dev->dev_id ? "1" : "0");
+}
+
+const static struct net_device_ops gmac_351x_ops = {
+	.ndo_init		= gmac_init,
+	.ndo_uninit		= gmac_uninit,
+	.ndo_open		= gmac_open,
+	.ndo_stop		= gmac_stop,
+	.ndo_start_xmit		= gmac_start_xmit,
+	.ndo_tx_timeout		= gmac_dump_dma_state,
+	.ndo_set_multicast_list	= gmac_set_multicast_list,
+	.ndo_set_rx_mode	= gmac_set_rx_mode,
+	.ndo_set_mac_address	= gmac_set_mac_address,
+	.ndo_get_stats64	= gmac_get_stats64,
+	.ndo_change_mtu		= gmac_change_mtu,
+};
+
+const struct ethtool_ops gmac_351x_ethtool_ops = {
+	.get_rx_csum	= gmac_get_rx_csum,
+	.set_rx_csum	= gmac_set_rx_csum,
+	.get_tx_csum	= ethtool_op_get_tx_csum,
+	.set_tx_csum	= ethtool_op_set_tx_ipv6_csum,
+	.get_sg		= ethtool_op_get_sg,
+	.set_sg		= ethtool_op_set_sg,
+	.get_tso	= ethtool_op_get_tso,
+	.set_tso	= gmac_set_tso,
+	.get_sset_count	= gmac_get_sset_count,
+	.get_strings	= gmac_get_strings,
+	.get_ethtool_stats = gmac_get_ethtool_stats,
+	.get_settings	= gmac_get_settings,
+	.set_settings	= gmac_set_settings,
+	.get_link	= ethtool_op_get_link,
+	.nway_reset	= gmac_nway_reset,
+	.get_pauseparam	= gmac_get_pauseparam,
+	.get_ringparam	= gmac_get_ringparam,
+	.set_ringparam	= gmac_set_ringparam,
+	.get_coalesce	= gmac_get_coalesce,
+	.set_coalesce	= gmac_set_coalesce,
+	.get_msglevel	= gmac_get_msglevel,
+	.set_msglevel	= gmac_set_msglevel,
+	.get_drvinfo	= gmac_get_drvinfo,
+};
+
+static int __devinit gmac_init_netdev(struct toe_private *toe, int num,
+	struct platform_device *pdev)
+{
+	struct gemini_gmac_platform_data *pdata = pdev->dev.platform_data;
+	struct gmac_private *gmac;
+	struct net_device *dev;
+	__le32 addr[3];
+	int irq, err;
+
+	if (!pdata->bus_id[num])
+		return 0;
+
+	irq = platform_get_irq(pdev, num);
+	if (irq < 0) {
+		dev_err(toe->dev, "No IRQ for ethernet device #%d\n", num);
+		return irq;
+	}
+
+	dev = alloc_etherdev_mq(sizeof(*gmac), TX_QUEUE_NUM);
+	if (!dev) {
+		dev_err(toe->dev, "Can't allocate ethernet device #%d\n", num);
+		return -ENOMEM;
+	}
+
+	gmac = netdev_priv(dev);
+	dev->ml_priv = toe;
+	SET_NETDEV_DEV(dev, toe->dev);
+
+	toe->netdev[num] = dev;
+	dev->dev_id = num;
+
+	gmac->dma_iomem = toe->iomem + TOE_GMAC_DMA_BASE(num);
+	dev->base_addr = (unsigned long)(toe->iomem + TOE_GMAC_BASE(num));
+	dev->irq = irq;
+
+	dev->netdev_ops = &gmac_351x_ops;
+	SET_ETHTOOL_OPS(dev, &gmac_351x_ethtool_ops);
+
+	spin_lock_init(&gmac->config_lock);
+	gmac->msg_enable = debug_level;
+	gmac_clear_hw_stats(dev);
+
+	/* select working offloads by default */
+	/* (SG will be disabled when HW csum is disabled) */
+	/* TSO_ECN untested, TX csum unreliable, TX DMA unreliable */
+	dev->features |= NETIF_F_SG | NETIF_F_GSO | NETIF_F_GRO;
+
+	netif_napi_add(dev, &gmac->napi, gmac_napi_poll, DEFAULT_NAPI_WEIGHT);
+
+	/* dump MAC address regs; CPU is LE anyway */
+	addr[0] = cpu_to_le32(__raw_readl(gmac_ctl_reg(dev, GMAC_STA_ADD0)));
+	addr[1] = cpu_to_le32(__raw_readl(gmac_ctl_reg(dev, GMAC_STA_ADD1)));
+	addr[2] = cpu_to_le32(__raw_readl(gmac_ctl_reg(dev, GMAC_STA_ADD2)));
+	dev_dbg(&pdev->dev, "port %d address regs: %pM %pM\n",
+		num, (char *)addr, (char *)addr + ETH_ALEN);
+
+	if (is_valid_ether_addr((void *)addr))
+		memcpy(dev->dev_addr, addr, ETH_ALEN);
+	else
+		random_ether_addr(dev->dev_addr);
+	__gmac_set_mac_address(dev);
+
+	err = gmac_setup_phy(dev);
+	if (err)
+		netif_warn(gmac, probe, dev,
+			"PHY init failed: %d, deferring to ifup time\n", err);
+
+	err = register_netdev(dev);
+	if (!err)
+		return 0;
+
+	toe->netdev[num] = NULL;
+	free_netdev(dev);
+	return err;
+}
+
+static struct page *toe_alloc_freeq_pages(struct toe_private *toe, bool emerg)
+{
+	gfp_t gfp_mask = GFP_NOIO;
+	bool retried = false;
+
+retry:
+	toe->freeq_page = alloc_pages(gfp_mask, toe->alloc_order);
+	if (!toe->freeq_page) {
+		toe->alloc_order >>= 1;
+		if (gfp_mask & __GFP_HIGH)
+			/* even emergency alloc failed */
+			return NULL;
+		if (!toe->alloc_order && emerg)
+			gfp_mask |= __GFP_HIGH;
+		retried = true;
+		goto retry;
+	}
+
+	if (dma_mapping_error(toe->dev, dma_map_page(toe->dev,
+	    toe->freeq_page, 0, PAGE_SIZE << toe->alloc_order,
+	    DMA_FROM_DEVICE))) {
+		put_page(toe->freeq_page);
+		goto retry;
+	}
+
+	toe->freeq_page_count = 1 << toe->alloc_order;
+	toe->freeq_page_offs = 0;
+
+	if (!retried && toe->alloc_order < RX_MAX_ALLOC_ORDER)
+		toe->alloc_order++;
+
+	return toe->freeq_page;
+}
+
+static struct page *toe_get_next_page(struct toe_private *toe,
+	struct page *page, unsigned eaten_size)
+{
+	toe->freeq_page_offs += eaten_size;
+	if (toe->freeq_page_offs & ~PAGE_MASK) {
+		get_page(page);
+		return page;
+	}
+
+	if (!--toe->freeq_page_count)
+		return NULL;
+
+	toe->freeq_page_offs = 0;
+
+	toe->freeq_page = ++page;
+	get_page(page);
+
+	return page;
+}
+
+static unsigned int toe_fill_freeq_range(struct toe_private *toe,
+	unsigned int begin, unsigned int end)
+{
+	void __iomem *ptr_reg = toe_reg(toe, GLOBAL_SWFQ_RWPTR_REG);
+	GMAC_RXDESC_T *desc, *dend;
+	struct page *page;
+	unsigned count;
+
+	dev_dbg(toe->dev, "freeq: filling <%u,%u)  (ptr: %u %u)\n",
+		begin, end, GET_RPTR(ptr_reg), GET_WPTR(ptr_reg));
+
+	if (toe->freeq_page_count)
+		page = toe->freeq_page;
+	else
+		page = NULL;
+
+	desc = toe->freeq_ring + begin;
+	dend = toe->freeq_ring + end;
+
+	for (count = 0; desc != dend; ++desc, ++count) {
+		if (!page) {
+			page = toe_alloc_freeq_pages(toe, 0);
+			if (!page)
+				break;
+		}
+
+		/* only word2 gets copied to rxq descriptor */
+		/* buffer size is taken from DMA_SKB_SIZE_REG */
+		desc->word2.buf_adr = page_to_dma(toe->dev, page) + toe->freeq_page_offs;
+
+		if (unlikely(!count))
+			dev_dbg(toe->dev,
+				"freeq[%zu]: 0x%08x 0x%08x 0x%08x 0x%08x, page %p, offs 0x%04x\n",
+				desc - toe->freeq_ring,
+				desc->word0.bits32, desc->word1.bits32,
+				desc->word2.bits32, desc->word3.bits32,
+				page, toe->freeq_page_offs);
+
+		page = toe_get_next_page(toe, page,
+			1 << toe->freeq_frag_order);
+	}
+
+	end = (desc - toe->freeq_ring) & __RWPTR_MASK(toe->freeq_order);
+	SET_WPTR(ptr_reg, end);
+
+	return count;
+}
+
+static void toe_enable_irq(struct toe_private *toe, int enable)
+{
+	void __iomem *irqen_reg = toe_reg(toe, GLOBAL_INTERRUPT_ENABLE_4_REG);
+
+	unsigned long flags;
+	unsigned val;
+
+	spin_lock_irqsave(&toe->irq_lock, flags);
+
+	val = __raw_readl(irqen_reg);
+	if (enable)
+		val |= SWFQ_EMPTY_INT_BIT;
+	else
+		val &= ~SWFQ_EMPTY_INT_BIT;
+	__raw_writel(val, irqen_reg);
+
+	spin_unlock_irqrestore(&toe->irq_lock, flags);
+}
+
+static irqreturn_t toe_interrupt(int irq, void *data)
+{
+	struct toe_private *toe = data;
+
+	void __iomem *irqif_reg = toe_reg(toe, GLOBAL_INTERRUPT_STATUS_4_REG);
+	void __iomem *irqen_reg = toe_reg(toe, GLOBAL_INTERRUPT_ENABLE_4_REG);
+	unsigned val, en;
+	irqreturn_t ret = IRQ_NONE;
+
+	spin_lock(&toe->irq_lock);
+
+	val = __raw_readl(irqif_reg);
+	val &= en = __raw_readl(irqen_reg);
+
+	if (val & SWFQ_EMPTY_INT_BIT) {
+		toe_enable_irq(toe, 0);
+		ret = IRQ_WAKE_THREAD;
+	}
+
+	spin_unlock(&toe->irq_lock);
+
+	return ret;
+}
+
+static irqreturn_t toe_interrupt_thread(int irq, void *data)
+{
+	struct toe_private *toe = data;
+	void __iomem *rwptr_reg = toe_reg(toe, GLOBAL_SWFQ_RWPTR_REG);
+	void __iomem *irqif_reg = toe_reg(toe, GLOBAL_INTERRUPT_STATUS_4_REG);
+	unsigned int r, w, d, end, count = 0;
+
+retry:
+	w = GET_WPTR(rwptr_reg);
+	r = GET_RPTR(rwptr_reg);
+
+	d = RWPTR_DISTANCE(r, w, toe->freeq_order);
+	if (unlikely(d >= toe->freeq_entries))
+		goto full;
+	d = toe->freeq_entries - d;
+
+	end = min(w + d, 1u << toe->freeq_order);
+	count += toe_fill_freeq_range(toe, w, end);
+
+	if (count == end - w && !(end & __RWPTR_MASK(toe->freeq_order)))
+		goto retry;
+
+full:
+	dev_dbg(toe->dev, "freeq: filled %u buffers\n", count);
+
+	__raw_writel(SWFQ_EMPTY_INT_BIT, irqif_reg);
+	if (unlikely(GET_WPTR(rwptr_reg) == GET_RPTR(rwptr_reg))) {
+		if (unlikely(!count))
+			dev_warn(toe->dev, "freeq: full, but empty?\n");
+		count = 0;
+		goto retry;
+	}
+
+	toe_enable_irq(toe, 1);
+
+	return IRQ_HANDLED;
+}
+
+static int toe_setup_freeq(struct toe_private *toe)
+{
+	void __iomem *dma_reg = toe_reg(toe, GLOBAL_SW_FREEQ_BASE_SIZE_REG);
+	QUEUE_THRESHOLD_T qt;
+	DMA_SKB_SIZE_T skbsz = { .bits = { .sw_skb_size = 1 << toe->freeq_frag_order } };
+	unsigned n;
+
+	toe->freeq_ring = dma_alloc_coherent(toe->dev,
+		sizeof(*toe->freeq_ring) << toe->freeq_order,
+		&toe->freeq_dma_base, GFP_KERNEL);
+	if (!toe->freeq_ring)
+		return -ENOMEM;
+
+	BUG_ON(toe->freeq_dma_base & ~DMA_Q_BASE_MASK);
+
+	__raw_writel(skbsz.bits32, toe_reg(toe, GLOBAL_DMA_SKB_SIZE_REG));
+	__raw_writel(toe->freeq_dma_base | toe->freeq_order, dma_reg);
+
+	/* fill ring */
+	n = toe_fill_freeq_range(toe, 0, toe->freeq_entries);
+	if (!n)
+		goto err_freeq;
+	if (n != toe->freeq_entries)
+		dev_warn(toe->dev, "Allocated only %u of %u RX buffers\n",
+			n, toe->freeq_entries);
+
+	qt.bits32 = __raw_readl(toe_reg(toe, GLOBAL_QUEUE_THRESHOLD_REG));
+	qt.bits.swfq_empty = min_t(unsigned, (n + 1) >> 1, 255);
+	__raw_writel(qt.bits32, toe_reg(toe, GLOBAL_QUEUE_THRESHOLD_REG));
+
+	dev_dbg(toe->dev, "freeq: ring %p (dma 0x%08x), len %u (order %u), thr %u\n",
+		toe->freeq_ring, toe->freeq_dma_base,
+		toe->freeq_entries, toe->freeq_order, qt.bits.swfq_empty);
+
+	return 0;
+
+err_freeq:
+	__raw_writel(0, dma_reg);
+	dma_free_coherent(toe->dev, sizeof(*toe->freeq_ring) << toe->freeq_order,
+		toe->freeq_ring, toe->freeq_dma_base);
+	toe->freeq_ring = NULL;
+
+	return -ENOMEM;
+}
+
+static void toe_cleanup_freeq(struct toe_private *toe)
+{
+	void __iomem *dma_reg = toe_reg(toe, GLOBAL_SW_FREEQ_BASE_SIZE_REG);
+	void __iomem *ptr_reg = toe_reg(toe, GLOBAL_SWFQ_RWPTR_REG);
+	unsigned i, e, mask = __RWPTR_MASK(toe->freeq_order);
+
+	i = GET_RPTR(ptr_reg);
+	e = GET_WPTR(ptr_reg);
+	__raw_writel(0, ptr_reg);
+	__raw_writel(0, dma_reg);
+
+	for (; i != e; i = __RWPTR_NEXT(i, mask))
+		put_page(toe_unmap_rx_desc(toe, &toe->freeq_ring[i]));
+
+	dma_free_coherent(toe->dev, sizeof(*toe->freeq_ring) << toe->freeq_order,
+		toe->freeq_ring, toe->freeq_dma_base);
+
+	toe->freeq_ring = NULL;
+}
+
+static int toe_resize_freeq(struct toe_private *toe, int changing_dev_id)
+{
+	struct net_device *other = toe->netdev[1 - changing_dev_id];
+	unsigned new_size = 0;
+	unsigned new_order;
+	int err;
+
+	if (other && netif_running(other))
+		return -EBUSY;
+
+	if (toe->netdev[0])
+		new_size  = 1 << netdev_to_gmac(toe->netdev[0])->rxq_order;
+
+	if (toe->netdev[1])
+		new_size += 1 << netdev_to_gmac(toe->netdev[1])->rxq_order;
+
+	new_order = min(15, ilog2(new_size - 1) + 1);
+	if (new_size >= 1 << new_order)
+		new_size = (1 << new_order) - 1;
+
+	toe_enable_irq(toe, 0);
+	if (toe->freeq_ring)
+		toe_cleanup_freeq(toe);
+
+	toe->freeq_order = new_order;
+	toe->freeq_entries = new_size;
+
+	err = toe_setup_freeq(toe);
+	if (unlikely(err))
+		return err;
+
+	toe_enable_irq(toe, 1);
+
+	return 0;
+}
+
+
+/*
+ * Interrupt config:
+ *
+ *	GMAC0 intr bits ------> int0 ----> eth0
+ *	GMAC1 intr bits ------> int1 ----> eth1
+ *	TOE intr -------------> int1 ----> eth1
+ *	Classification Intr --> int0 ----> eth0
+ *	Default Q0 -----------> int0 ----> eth0
+ *	Default Q1 -----------> int1 ----> eth1
+ *	FreeQ intr -----------> int1 ----> eth1
+ */
+static void toe_prepare_irq(struct toe_private *toe)
+{
+	__raw_writel(0, toe_reg(toe, GLOBAL_INTERRUPT_ENABLE_0_REG));
+	__raw_writel(0, toe_reg(toe, GLOBAL_INTERRUPT_ENABLE_1_REG));
+	__raw_writel(0, toe_reg(toe, GLOBAL_INTERRUPT_ENABLE_2_REG));
+	__raw_writel(0, toe_reg(toe, GLOBAL_INTERRUPT_ENABLE_3_REG));
+	__raw_writel(0, toe_reg(toe, GLOBAL_INTERRUPT_ENABLE_4_REG));
+
+	__raw_writel(0xCCFC0FC0, toe_reg(toe, GLOBAL_INTERRUPT_SELECT_0_REG));
+	__raw_writel(0x00F00002, toe_reg(toe, GLOBAL_INTERRUPT_SELECT_1_REG));
+	__raw_writel(0xFFFFFFFF, toe_reg(toe, GLOBAL_INTERRUPT_SELECT_2_REG));
+	__raw_writel(0xFFFFFFFF, toe_reg(toe, GLOBAL_INTERRUPT_SELECT_3_REG));
+	__raw_writel(0xFF000003, toe_reg(toe, GLOBAL_INTERRUPT_SELECT_4_REG));
+
+	/* edge-triggered interrupts packed to level-triggered one... */
+	__raw_writel(~0, toe_reg(toe, GLOBAL_INTERRUPT_STATUS_0_REG));
+	__raw_writel(~0, toe_reg(toe, GLOBAL_INTERRUPT_STATUS_1_REG));
+	__raw_writel(~0, toe_reg(toe, GLOBAL_INTERRUPT_STATUS_2_REG));
+	__raw_writel(~0, toe_reg(toe, GLOBAL_INTERRUPT_STATUS_3_REG));
+	__raw_writel(~0, toe_reg(toe, GLOBAL_INTERRUPT_STATUS_4_REG));
+}
+
+static __devinit int toe_init(struct toe_private *toe, struct platform_device *pdev)
+{
+	int err;
+
+	__raw_writel(0, toe_reg(toe, GLOBAL_SW_FREEQ_BASE_SIZE_REG));
+	__raw_writel(0, toe_reg(toe, GLOBAL_HW_FREEQ_BASE_SIZE_REG));
+	__raw_writel(0, toe_reg(toe, GLOBAL_SWFQ_RWPTR_REG));
+	__raw_writel(0, toe_reg(toe, GLOBAL_HWFQ_RWPTR_REG));
+
+	toe->freeq_frag_order = DEFAULT_RX_BUF_ORDER;
+	toe->freeq_order = ~0;
+
+	toe_prepare_irq(toe);
+	err = request_threaded_irq(toe->irq, toe_interrupt,
+		toe_interrupt_thread, IRQF_SHARED, "sl351x-TOE", toe);
+	if (err)
+		goto err_freeq;
+
+	return 0;
+
+err_freeq:
+	toe_cleanup_freeq(toe);
+	return err;
+}
+
+static void toe_deinit(struct toe_private *toe)
+{
+	toe_prepare_irq(toe);
+	free_irq(toe->irq, toe);
+	toe_cleanup_freeq(toe);
+
+	if (toe->freeq_page_count)
+		put_page(toe->freeq_page);
+}
+
+static int toe_reset(struct toe_private *toe)
+{
+	unsigned int reg, retry = 5;
+
+	reg = __raw_readl(IO_ADDRESS(GEMINI_GLOBAL_BASE) + GLOBAL_RESET);
+	reg |= RESET_GMAC1 | RESET_GMAC0;
+	__raw_writel(reg, IO_ADDRESS(GEMINI_GLOBAL_BASE) + GLOBAL_RESET);
+
+	do {
+		udelay(2);
+		reg = __raw_readl(toe_reg(toe, GLOBAL_TOE_VERSION_REG));
+		barrier();
+	} while (!reg && --retry);
+
+	dev_info(toe->dev, "Gemini GMAC version 0x%x\n", reg);
+
+	return reg ? 0 : -EIO;
+}
+
+static int __devinit gemini_gmac_probe(struct platform_device *pdev)
+{
+	struct resource *res;
+	struct toe_private *toe;
+	int retval;
+
+	if (!pdev->dev.platform_data)
+		return -EINVAL;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "can't get device resources\n");
+		return -ENODEV;
+	}
+
+	toe = kzalloc(sizeof(*toe), GFP_KERNEL);
+	if (!toe)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, toe);
+	toe->dev = &pdev->dev;
+	toe->iomem = ioremap(res->start, resource_size(res));
+	if (!toe->iomem) {
+		dev_err(toe->dev, "ioremap failed\n");
+		retval = -EIO;
+		goto err_data;
+	}
+
+	retval = toe_reset(toe);
+	if (retval < 0)
+		goto err_unmap;
+
+	retval = toe->irq = platform_get_irq(pdev, 1);
+	if (retval < 0)
+		goto err_unmap;
+
+	spin_lock_init(&toe->irq_lock);
+
+	retval = toe_init(toe, pdev);
+	if (retval)
+		goto err_unmap;
+
+	retval = gmac_init_netdev(toe, 0, pdev);
+	if (retval)
+		goto err_uninit;
+
+	retval = gmac_init_netdev(toe, 1, pdev);
+	if (retval)
+		goto err_uninit;
+
+	toe_resize_freeq(toe, 0);
+
+	dev_dbg(&pdev->dev, "initialized.\n");
+	return 0;
+
+err_uninit:
+	if (toe->netdev[0])
+		unregister_netdev(toe->netdev[0]);
+	toe_deinit(toe);
+err_unmap:
+	iounmap(toe->iomem);
+err_data:
+	kfree(toe);
+	return retval;
+}
+
+static int __devexit gemini_gmac_remove(struct platform_device *pdev)
+{
+	struct toe_private *toe = platform_get_drvdata(pdev);
+	int i;
+
+	for (i = 0; i < 2; i++)
+		if (toe->netdev[i])
+			unregister_netdev(toe->netdev[i]);
+	toe_deinit(toe);
+
+	iounmap(toe->iomem);
+	kfree(toe);
+
+	return 0;
+}
+
+static struct platform_driver gemini_gmac_driver = {
+	.probe		= gemini_gmac_probe,
+	.remove		= __devexit_p(gemini_gmac_remove),
+	.driver.name	= "gemini-gmac",
+	.driver.owner	= THIS_MODULE,
+};
+
+static int __init gemini_gmac_init(void)
+{
+#ifdef CONFIG_MDIO_GPIO_MODULE
+	request_module("mdio-gpio");
+#endif
+	return platform_driver_register(&gemini_gmac_driver);
+}
+
+static void __exit gemini_gmac_exit(void)
+{
+	platform_driver_unregister(&gemini_gmac_driver);
+}
+
+module_init(gemini_gmac_init);
+module_exit(gemini_gmac_exit);
+
+MODULE_AUTHOR("Michał Mirosław");
+MODULE_DESCRIPTION("StorLink SL351x (Gemini) ethernet driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:gemini-gmac");
diff --git a/drivers/net/sl351x_hw.h b/drivers/net/sl351x_hw.h
new file mode 100644
index 0000000..9139b66
--- /dev/null
+++ b/drivers/net/sl351x_hw.h
@@ -0,0 +1,1435 @@
+/*
+ *  Register definitions for Gemini LEPUS GMAC Ethernet device driver.
+ *
+ *  Copyright (C) 2006, Storlink, Corp.
+ *  Copyright (C) 2008-2009, Paulius Zaleckas <paulius.zaleckas@teltonika.lt>
+ *  Copyright (C) 2010, Michał Mirosław <mirq-linux@rere.qmqm.pl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#ifndef _GMAC_HW_H
+#define _GMAC_HW_H
+
+#include <linux/bitops.h>
+
+/*
+ * Base Registers
+ */
+#define TOE_NONTOE_QUE_HDR_BASE		0x2000
+#define TOE_TOE_QUE_HDR_BASE		0x3000
+#define TOE_V_BIT_BASE			0x4000
+#define TOE_A_BIT_BASE			0x6000
+#define TOE_GMAC_DMA_BASE(x)		(0x8000 + 0x4000 * (x))
+#define TOE_GMAC_BASE(x)		(0xA000 + 0x4000 * (x))
+
+/*
+ * Queue ID
+ */
+#define TOE_SW_FREE_QID			0x00
+#define TOE_HW_FREE_QID			0x01
+#define TOE_GMAC0_SW_TXQ0_QID		0x02
+#define TOE_GMAC0_SW_TXQ1_QID		0x03
+#define TOE_GMAC0_SW_TXQ2_QID		0x04
+#define TOE_GMAC0_SW_TXQ3_QID		0x05
+#define TOE_GMAC0_SW_TXQ4_QID		0x06
+#define TOE_GMAC0_SW_TXQ5_QID		0x07
+#define TOE_GMAC0_HW_TXQ0_QID		0x08
+#define TOE_GMAC0_HW_TXQ1_QID		0x09
+#define TOE_GMAC0_HW_TXQ2_QID		0x0A
+#define TOE_GMAC0_HW_TXQ3_QID		0x0B
+#define TOE_GMAC1_SW_TXQ0_QID		0x12
+#define TOE_GMAC1_SW_TXQ1_QID		0x13
+#define TOE_GMAC1_SW_TXQ2_QID		0x14
+#define TOE_GMAC1_SW_TXQ3_QID		0x15
+#define TOE_GMAC1_SW_TXQ4_QID		0x16
+#define TOE_GMAC1_SW_TXQ5_QID		0x17
+#define TOE_GMAC1_HW_TXQ0_QID		0x18
+#define TOE_GMAC1_HW_TXQ1_QID		0x19
+#define TOE_GMAC1_HW_TXQ2_QID		0x1A
+#define TOE_GMAC1_HW_TXQ3_QID		0x1B
+#define TOE_GMAC0_DEFAULT_QID		0x20
+#define TOE_GMAC1_DEFAULT_QID		0x21
+#define TOE_CLASSIFICATION_QID(x)	(0x22 + x)	// 0x22 ~ 0x2F
+#define TOE_TOE_QID(x)			(0x40 + x)	// 0x40 ~ 0x7F
+
+/*
+ * old info:
+ * TOE DMA Queue Size should be 2^n, n = 6...12
+ * TOE DMA Queues are the following queue types:
+ *		SW Free Queue, HW Free Queue,
+ *		GMAC 0/1 SW TX Q0-5, and GMAC 0/1 HW TX Q0-5
+ * The base address and descriptor number are configured at
+ * DMA Queues Descriptor Ring Base Address/Size Register (offset 0x0004)
+ */
+
+#define GET_WPTR(addr)			__raw_readw((addr) + 2)
+#define GET_RPTR(addr)			__raw_readw((addr))
+#define SET_WPTR(addr, data)		__raw_writew((data), (addr) + 2)
+#define SET_RPTR(addr, data)		__raw_writew((data), (addr))
+#define __RWPTR_NEXT(x, mask)		(((unsigned int)(x) + 1) & (mask))
+#define __RWPTR_PREV(x, mask)		(((unsigned int)(x) - 1) & (mask))
+#define __RWPTR_DISTANCE(r, w, mask)	(((unsigned int)(w) - (r)) & (mask))
+#define __RWPTR_MASK(order)		((1 << (order)) - 1)
+#define RWPTR_NEXT(x, order)		__RWPTR_NEXT((x), __RWPTR_MASK((order)))
+#define RWPTR_PREV(x, order)		__RWPTR_PREV((x), __RWPTR_MASK((order)))
+#define RWPTR_DISTANCE(r, w, order)	__RWPTR_DISTANCE((r), (w), __RWPTR_MASK((order)))
+
+/*
+ * Global registers
+ * #define TOE_GLOBAL_BASE			(TOE_BASE + 0x0000)
+ * Base 0x60000000
+ */
+#define GLOBAL_TOE_VERSION_REG		0x0000
+#define GLOBAL_SW_FREEQ_BASE_SIZE_REG	0x0004
+#define GLOBAL_HW_FREEQ_BASE_SIZE_REG	0x0008
+#define GLOBAL_DMA_SKB_SIZE_REG		0x0010
+#define GLOBAL_SWFQ_RWPTR_REG		0x0014
+#define GLOBAL_HWFQ_RWPTR_REG		0x0018
+#define GLOBAL_INTERRUPT_STATUS_0_REG	0x0020
+#define GLOBAL_INTERRUPT_ENABLE_0_REG	0x0024
+#define GLOBAL_INTERRUPT_SELECT_0_REG	0x0028
+#define GLOBAL_INTERRUPT_STATUS_1_REG	0x0030
+#define GLOBAL_INTERRUPT_ENABLE_1_REG	0x0034
+#define GLOBAL_INTERRUPT_SELECT_1_REG	0x0038
+#define GLOBAL_INTERRUPT_STATUS_2_REG	0x0040
+#define GLOBAL_INTERRUPT_ENABLE_2_REG	0x0044
+#define GLOBAL_INTERRUPT_SELECT_2_REG	0x0048
+#define GLOBAL_INTERRUPT_STATUS_3_REG	0x0050
+#define GLOBAL_INTERRUPT_ENABLE_3_REG	0x0054
+#define GLOBAL_INTERRUPT_SELECT_3_REG	0x0058
+#define GLOBAL_INTERRUPT_STATUS_4_REG	0x0060
+#define GLOBAL_INTERRUPT_ENABLE_4_REG	0x0064
+#define GLOBAL_INTERRUPT_SELECT_4_REG	0x0068
+#define GLOBAL_HASH_TABLE_BASE_REG	0x006C
+#define GLOBAL_QUEUE_THRESHOLD_REG	0x0070
+
+/*
+ * GMAC 0/1 DMA/TOE register
+ * #define TOE_GMAC0_DMA_BASE		(TOE_BASE + 0x8000)
+ * #define TOE_GMAC1_DMA_BASE		(TOE_BASE + 0xC000)
+ * Base 0x60008000 or 0x6000C000
+ */
+#define GMAC_DMA_CTRL_REG		0x0000
+#define GMAC_TX_WEIGHTING_CTRL_0_REG	0x0004
+#define GMAC_TX_WEIGHTING_CTRL_1_REG	0x0008
+#define GMAC_SW_TX_QUEUE0_PTR_REG	0x000C
+#define GMAC_SW_TX_QUEUE1_PTR_REG	0x0010
+#define GMAC_SW_TX_QUEUE2_PTR_REG	0x0014
+#define GMAC_SW_TX_QUEUE3_PTR_REG	0x0018
+#define GMAC_SW_TX_QUEUE4_PTR_REG	0x001C
+#define GMAC_SW_TX_QUEUE5_PTR_REG	0x0020
+#define GMAC_SW_TX_QUEUE_PTR_REG(i)	(GMAC_SW_TX_QUEUE0_PTR_REG + 4 * (i))
+#define GMAC_HW_TX_QUEUE0_PTR_REG	0x0024
+#define GMAC_HW_TX_QUEUE1_PTR_REG	0x0028
+#define GMAC_HW_TX_QUEUE2_PTR_REG	0x002C
+#define GMAC_HW_TX_QUEUE3_PTR_REG	0x0030
+#define GMAC_HW_TX_QUEUE_PTR_REG(i)	(GMAC_HW_TX_QUEUE0_PTR_REG + 4 * (i))
+#define GMAC_DMA_TX_FIRST_DESC_REG	0x0038
+#define GMAC_DMA_TX_CURR_DESC_REG	0x003C
+#define GMAC_DMA_TX_DESC_WORD0_REG	0x0040
+#define GMAC_DMA_TX_DESC_WORD1_REG	0x0044
+#define GMAC_DMA_TX_DESC_WORD2_REG	0x0048
+#define GMAC_DMA_TX_DESC_WORD3_REG	0x004C
+#define GMAC_SW_TX_QUEUE_BASE_REG	0x0050
+#define GMAC_HW_TX_QUEUE_BASE_REG	0x0054
+#define GMAC_DMA_RX_FIRST_DESC_REG	0x0058
+#define GMAC_DMA_RX_CURR_DESC_REG	0x005C
+#define GMAC_DMA_RX_DESC_WORD0_REG	0x0060
+#define GMAC_DMA_RX_DESC_WORD1_REG	0x0064
+#define GMAC_DMA_RX_DESC_WORD2_REG	0x0068
+#define GMAC_DMA_RX_DESC_WORD3_REG	0x006C
+#define GMAC_HASH_ENGINE_REG0		0x0070
+#define GMAC_HASH_ENGINE_REG1		0x0074
+/* matching rule 0 Control register 0 */
+#define GMAC_MR0CR0			0x0078
+#define GMAC_MR0CR1			0x007C
+#define GMAC_MR0CR2			0x0080
+#define GMAC_MR1CR0			0x0084
+#define GMAC_MR1CR1			0x0088
+#define GMAC_MR1CR2			0x008C
+#define GMAC_MR2CR0			0x0090
+#define GMAC_MR2CR1			0x0094
+#define GMAC_MR2CR2			0x0098
+#define GMAC_MR3CR0			0x009C
+#define GMAC_MR3CR1			0x00A0
+#define GMAC_MR3CR2			0x00A4
+/* Support Protocol Regsister 0 */
+#define GMAC_SPR0			0x00A8
+#define GMAC_SPR1			0x00AC
+#define GMAC_SPR2			0x00B0
+#define GMAC_SPR3			0x00B4
+#define GMAC_SPR4			0x00B8
+#define GMAC_SPR5			0x00BC
+#define GMAC_SPR6			0x00C0
+#define GMAC_SPR7			0x00C4
+/* GMAC Hash/Rx/Tx AHB Weighting register */
+#define GMAC_AHB_WEIGHT_REG		0x00C8
+
+/*
+ * TOE GMAC 0/1 register
+ * #define TOE_GMAC0_BASE				(TOE_BASE + 0xA000)
+ * #define TOE_GMAC1_BASE				(TOE_BASE + 0xE000)
+ * Base 0x6000A000 or 0x6000E000
+ */
+enum GMAC_REGISTER {
+	GMAC_STA_ADD0 	= 0x0000,
+	GMAC_STA_ADD1	= 0x0004,
+	GMAC_STA_ADD2	= 0x0008,
+	GMAC_RX_FLTR	= 0x000c,
+	GMAC_MCAST_FIL0 = 0x0010,
+	GMAC_MCAST_FIL1 = 0x0014,
+	GMAC_CONFIG0	= 0x0018,
+	GMAC_CONFIG1	= 0x001c,
+	GMAC_CONFIG2	= 0x0020,
+	GMAC_CONFIG3	= 0x0024,
+	GMAC_RESERVED	= 0x0028,
+	GMAC_STATUS	= 0x002c,
+	GMAC_IN_DISCARDS= 0x0030,
+	GMAC_IN_ERRORS  = 0x0034,
+	GMAC_IN_MCAST   = 0x0038,
+	GMAC_IN_BCAST   = 0x003c,
+	GMAC_IN_MAC1    = 0x0040,	/* for STA 1 MAC Address */
+	GMAC_IN_MAC2    = 0x0044	/* for STA 2 MAC Address */
+};
+
+#define RX_STATS_NUM	6
+
+/*
+ * DMA Queues description Ring Base Address/Size Register (offset 0x0004)
+ */
+typedef union {
+	unsigned int bits32;
+	unsigned int base_size;
+} DMA_Q_BASE_SIZE_T;
+#define DMA_Q_BASE_MASK 	(~0x0f)
+
+/*
+ * DMA SKB Buffer register (offset 0x0008)
+ */
+typedef union {
+	unsigned int bits32;
+	struct bit_0008 {
+		unsigned int sw_skb_size : 16;	/* SW Free poll SKB Size */
+		unsigned int hw_skb_size : 16;	/* HW Free poll SKB Size */
+	} bits;
+} DMA_SKB_SIZE_T;
+
+/*
+ * DMA SW Free Queue Read/Write Pointer Register (offset 0x000C)
+ */
+typedef union {
+	unsigned int bits32;
+	struct bit_000c {
+		unsigned int rptr	: 16;	/* Read Ptr, RO */
+		unsigned int wptr	: 16;	/* Write Ptr, RW */
+	} bits;
+} DMA_RWPTR_T;
+
+/*
+ * DMA HW Free Queue Read/Write Pointer Register (offset 0x0010)
+ * see DMA_RWPTR_T structure
+ */
+
+/*
+ * Interrupt Status Register 0 	(offset 0x0020)
+ * Interrupt Mask Register 0 	(offset 0x0024)
+ * Interrupt Select Register 0 	(offset 0x0028)
+ */
+typedef union {
+	unsigned int bits32;
+	struct bit_0020 {
+		/* GMAC0 SW Tx Queue 0 EOF Interrupt */
+		unsigned int swtq00_eof	: 1;
+		unsigned int swtq01_eof	: 1;
+		unsigned int swtq02_eof	: 1;
+		unsigned int swtq03_eof	: 1;
+		unsigned int swtq04_eof	: 1;
+		unsigned int swtq05_eof	: 1;
+		/* GMAC1 SW Tx Queue 0 EOF Interrupt */
+		unsigned int swtq10_eof	: 1;
+		unsigned int swtq11_eof	: 1;
+		unsigned int swtq12_eof	: 1;
+		unsigned int swtq13_eof	: 1;
+		unsigned int swtq14_eof	: 1;
+		unsigned int swtq15_eof	: 1;
+		/* GMAC0 SW Tx Queue 0 Finish Interrupt */
+		unsigned int swtq00_fin	: 1;
+		unsigned int swtq01_fin	: 1;
+		unsigned int swtq02_fin	: 1;
+		unsigned int swtq03_fin	: 1;
+		unsigned int swtq04_fin	: 1;
+		unsigned int swtq05_fin	: 1;
+		/* GMAC1 SW Tx Queue 0 Finish Interrupt */
+		unsigned int swtq10_fin	: 1;
+		unsigned int swtq11_fin	: 1;
+		unsigned int swtq12_fin	: 1;
+		unsigned int swtq13_fin	: 1;
+		unsigned int swtq14_fin	: 1;
+		unsigned int swtq15_fin	: 1;
+		/* GMAC0 Rx Descriptor Protocol Error */
+		unsigned int rxPerr0	: 1;
+		/* GMAC0 AHB Bus Error while Rx */
+		unsigned int rxDerr0	: 1;
+		/* GMAC1 Rx Descriptor Protocol Error */
+		unsigned int rxPerr1	: 1;
+		/* GMAC1 AHB Bus Error while Rx */
+		unsigned int rxDerr1	: 1;
+		/* GMAC0 Tx Descriptor Protocol Error */
+		unsigned int txPerr0	: 1;
+		/* GMAC0 AHB Bus Error while Tx */
+		unsigned int txDerr0	: 1;
+		/* GMAC1 Tx Descriptor Protocol Error */
+		unsigned int txPerr1	: 1;
+		/* GMAC1 AHB Bus Error while Tx */
+		unsigned int txDerr1	: 1;
+	} bits;
+} INTR_REG0_T;
+
+#define GMAC1_TXDERR_INT_BIT		BIT(31)
+#define GMAC1_TXPERR_INT_BIT		BIT(30)
+#define GMAC0_TXDERR_INT_BIT		BIT(29)
+#define GMAC0_TXPERR_INT_BIT		BIT(28)
+#define GMAC1_RXDERR_INT_BIT		BIT(27)
+#define GMAC1_RXPERR_INT_BIT		BIT(26)
+#define GMAC0_RXDERR_INT_BIT		BIT(25)
+#define GMAC0_RXPERR_INT_BIT		BIT(24)
+#define GMAC1_SWTQ15_FIN_INT_BIT	BIT(23)
+#define GMAC1_SWTQ14_FIN_INT_BIT	BIT(22)
+#define GMAC1_SWTQ13_FIN_INT_BIT	BIT(21)
+#define GMAC1_SWTQ12_FIN_INT_BIT	BIT(20)
+#define GMAC1_SWTQ11_FIN_INT_BIT	BIT(19)
+#define GMAC1_SWTQ10_FIN_INT_BIT	BIT(18)
+#define GMAC0_SWTQ05_FIN_INT_BIT	BIT(17)
+#define GMAC0_SWTQ04_FIN_INT_BIT	BIT(16)
+#define GMAC0_SWTQ03_FIN_INT_BIT	BIT(15)
+#define GMAC0_SWTQ02_FIN_INT_BIT	BIT(14)
+#define GMAC0_SWTQ01_FIN_INT_BIT	BIT(13)
+#define GMAC0_SWTQ00_FIN_INT_BIT	BIT(12)
+#define GMAC1_SWTQ15_EOF_INT_BIT	BIT(11)
+#define GMAC1_SWTQ14_EOF_INT_BIT	BIT(10)
+#define GMAC1_SWTQ13_EOF_INT_BIT	BIT(9)
+#define GMAC1_SWTQ12_EOF_INT_BIT	BIT(8)
+#define GMAC1_SWTQ11_EOF_INT_BIT	BIT(7)
+#define GMAC1_SWTQ10_EOF_INT_BIT	BIT(6)
+#define GMAC0_SWTQ05_EOF_INT_BIT	BIT(5)
+#define GMAC0_SWTQ04_EOF_INT_BIT	BIT(4)
+#define GMAC0_SWTQ03_EOF_INT_BIT	BIT(3)
+#define GMAC0_SWTQ02_EOF_INT_BIT	BIT(2)
+#define GMAC0_SWTQ01_EOF_INT_BIT	BIT(1)
+#define GMAC0_SWTQ00_EOF_INT_BIT	BIT(0)
+
+/*
+ * Interrupt Status Register 1 	(offset 0x0030)
+ * Interrupt Mask Register 1 	(offset 0x0034)
+ * Interrupt Select Register 1 	(offset 0x0038)
+ */
+typedef union {
+	unsigned int bits32;
+	struct bit_0030 {
+		unsigned int default_q0_eof	: 1;	/* Default Queue 0 EOF Interrupt */
+		unsigned int default_q1_eof	: 1;	/* Default Queue 1 EOF Interrupt */
+		unsigned int class_rx		: 14;	/* Classification Queue Rx Interrupt */
+		unsigned int hwtq00_eof		: 1;	/* GMAC0 HW Tx Queue0 EOF Interrupt */
+		unsigned int hwtq01_eof		: 1;	/* GMAC0 HW Tx Queue1 EOF Interrupt */
+		unsigned int hwtq02_eof		: 1;	/* GMAC0 HW Tx Queue2 EOF Interrupt */
+		unsigned int hwtq03_eof		: 1;	/* GMAC0 HW Tx Queue3 EOF Interrupt */
+		unsigned int hwtq10_eof		: 1;	/* GMAC1 HW Tx Queue0 EOF Interrupt */
+		unsigned int hwtq11_eof		: 1;	/* GMAC1 HW Tx Queue1 EOF Interrupt */
+		unsigned int hwtq12_eof		: 1;	/* GMAC1 HW Tx Queue2 EOF Interrupt */
+		unsigned int hwtq13_eof		: 1;	/* GMAC1 HW Tx Queue3 EOF Interrupt */
+		unsigned int toe_iq0_intr	: 1;	/* TOE Interrupt Queue 0 with Interrupts */
+		unsigned int toe_iq1_intr	: 1;	/* TOE Interrupt Queue 1 with Interrupts */
+		unsigned int toe_iq2_intr	: 1;	/* TOE Interrupt Queue 2 with Interrupts */
+		unsigned int toe_iq3_intr	: 1;	/* TOE Interrupt Queue 3 with Interrupts */
+		unsigned int toe_iq0_full	: 1;	/* TOE Interrupt Queue 0 Full Interrupt */
+		unsigned int toe_iq1_full	: 1;	/* TOE Interrupt Queue 1 Full Interrupt */
+		unsigned int toe_iq2_full	: 1;	/* TOE Interrupt Queue 2 Full Interrupt */
+		unsigned int toe_iq3_full	: 1;	/* TOE Interrupt Queue 3 Full Interrupt */
+	} bits;
+} INTR_REG1_T;
+
+#define TOE_IQ3_FULL_INT_BIT		BIT(31)
+#define TOE_IQ2_FULL_INT_BIT		BIT(30)
+#define TOE_IQ1_FULL_INT_BIT		BIT(29)
+#define TOE_IQ0_FULL_INT_BIT		BIT(28)
+#define TOE_IQ3_INT_BIT			BIT(27)
+#define TOE_IQ2_INT_BIT			BIT(26)
+#define TOE_IQ1_INT_BIT			BIT(25)
+#define TOE_IQ0_INT_BIT			BIT(24)
+#define GMAC1_HWTQ13_EOF_INT_BIT	BIT(23)
+#define GMAC1_HWTQ12_EOF_INT_BIT	BIT(22)
+#define GMAC1_HWTQ11_EOF_INT_BIT	BIT(21)
+#define GMAC1_HWTQ10_EOF_INT_BIT	BIT(20)
+#define GMAC0_HWTQ03_EOF_INT_BIT	BIT(19)
+#define GMAC0_HWTQ02_EOF_INT_BIT	BIT(18)
+#define GMAC0_HWTQ01_EOF_INT_BIT	BIT(17)
+#define GMAC0_HWTQ00_EOF_INT_BIT	BIT(16)
+#define CLASS_RX_INT_BIT(x)		BIT((x + 2))
+#define DEFAULT_Q1_INT_BIT		BIT(1)
+#define DEFAULT_Q0_INT_BIT		BIT(0)
+
+#define TOE_IQ_INT_BITS		(TOE_IQ0_INT_BIT | TOE_IQ1_INT_BIT | \
+		               	 TOE_IQ2_INT_BIT | TOE_IQ3_INT_BIT)
+#define	TOE_IQ_FULL_BITS	(TOE_IQ0_FULL_INT_BIT | TOE_IQ1_FULL_INT_BIT | \
+		                 TOE_IQ2_FULL_INT_BIT | TOE_IQ3_FULL_INT_BIT)
+#define	TOE_IQ_ALL_BITS		(TOE_IQ_INT_BITS | TOE_IQ_FULL_BITS)
+#define TOE_CLASS_RX_INT_BITS	0xfffc
+
+/*
+ * Interrupt Status Register 2 	(offset 0x0040)
+ * Interrupt Mask Register 2 	(offset 0x0044)
+ * Interrupt Select Register 2 	(offset 0x0048)
+ */
+typedef union {
+	unsigned int bits32;
+	struct bit_0040 {
+		unsigned int toe_q0_full	: 1;	// bit 0	TOE Queue 0 Full Interrupt
+		unsigned int toe_q1_full	: 1;	// bit 1	TOE Queue 1 Full Interrupt
+		unsigned int toe_q2_full	: 1;	// bit 2	TOE Queue 2 Full Interrupt
+		unsigned int toe_q3_full	: 1;	// bit 3	TOE Queue 3 Full Interrupt
+		unsigned int toe_q4_full	: 1;	// bit 4	TOE Queue 4 Full Interrupt
+		unsigned int toe_q5_full	: 1;	// bit 5	TOE Queue 5 Full Interrupt
+		unsigned int toe_q6_full	: 1;	// bit 6	TOE Queue 6 Full Interrupt
+		unsigned int toe_q7_full	: 1;	// bit 7	TOE Queue 7 Full Interrupt
+		unsigned int toe_q8_full	: 1;	// bit 8	TOE Queue 8 Full Interrupt
+		unsigned int toe_q9_full	: 1;	// bit 9	TOE Queue 9 Full Interrupt
+		unsigned int toe_q10_full	: 1;	// bit 10	TOE Queue 10 Full Interrupt
+		unsigned int toe_q11_full	: 1;	// bit 11	TOE Queue 11 Full Interrupt
+		unsigned int toe_q12_full	: 1;	// bit 12	TOE Queue 12 Full Interrupt
+		unsigned int toe_q13_full	: 1;	// bit 13	TOE Queue 13 Full Interrupt
+		unsigned int toe_q14_full	: 1;	// bit 14	TOE Queue 14 Full Interrupt
+		unsigned int toe_q15_full	: 1;	// bit 15	TOE Queue 15 Full Interrupt
+		unsigned int toe_q16_full	: 1;	// bit 16	TOE Queue 16 Full Interrupt
+		unsigned int toe_q17_full	: 1;	// bit 17	TOE Queue 17 Full Interrupt
+		unsigned int toe_q18_full	: 1;	// bit 18	TOE Queue 18 Full Interrupt
+		unsigned int toe_q19_full	: 1;	// bit 19	TOE Queue 19 Full Interrupt
+		unsigned int toe_q20_full	: 1;	// bit 20	TOE Queue 20 Full Interrupt
+		unsigned int toe_q21_full	: 1;	// bit 21	TOE Queue 21 Full Interrupt
+		unsigned int toe_q22_full	: 1;	// bit 22	TOE Queue 22 Full Interrupt
+		unsigned int toe_q23_full	: 1;	// bit 23	TOE Queue 23 Full Interrupt
+		unsigned int toe_q24_full	: 1;	// bit 24	TOE Queue 24 Full Interrupt
+		unsigned int toe_q25_full	: 1;	// bit 25	TOE Queue 25 Full Interrupt
+		unsigned int toe_q26_full	: 1;	// bit 26	TOE Queue 26 Full Interrupt
+		unsigned int toe_q27_full	: 1;	// bit 27	TOE Queue 27 Full Interrupt
+		unsigned int toe_q28_full	: 1;	// bit 28	TOE Queue 28 Full Interrupt
+		unsigned int toe_q29_full	: 1;	// bit 29	TOE Queue 29 Full Interrupt
+		unsigned int toe_q30_full	: 1;	// bit 30	TOE Queue 30 Full Interrupt
+		unsigned int toe_q31_full	: 1;	// bit 31	TOE Queue 31 Full Interrupt
+	} bits;
+} INTR_REG2_T;
+
+#define TOE_QL_FULL_INT_BIT(x)		BIT(x)
+
+/*
+ * Interrupt Status Register 3 	(offset 0x0050)
+ * Interrupt Mask Register 3 	(offset 0x0054)
+ * Interrupt Select Register 3 	(offset 0x0058)
+ */
+typedef union {
+	unsigned int bits32;
+	struct bit_0050 {
+		unsigned int toe_q32_full	: 1;	// bit 32	TOE Queue 32 Full Interrupt
+		unsigned int toe_q33_full	: 1;	// bit 33	TOE Queue 33 Full Interrupt
+		unsigned int toe_q34_full	: 1;	// bit 34	TOE Queue 34 Full Interrupt
+		unsigned int toe_q35_full	: 1;	// bit 35	TOE Queue 35 Full Interrupt
+		unsigned int toe_q36_full	: 1;	// bit 36	TOE Queue 36 Full Interrupt
+		unsigned int toe_q37_full	: 1;	// bit 37	TOE Queue 37 Full Interrupt
+		unsigned int toe_q38_full	: 1;	// bit 38	TOE Queue 38 Full Interrupt
+		unsigned int toe_q39_full	: 1;	// bit 39	TOE Queue 39 Full Interrupt
+		unsigned int toe_q40_full	: 1;	// bit 40	TOE Queue 40 Full Interrupt
+		unsigned int toe_q41_full	: 1;	// bit 41	TOE Queue 41 Full Interrupt
+		unsigned int toe_q42_full	: 1;	// bit 42	TOE Queue 42 Full Interrupt
+		unsigned int toe_q43_full	: 1;	// bit 43	TOE Queue 43 Full Interrupt
+		unsigned int toe_q44_full	: 1;	// bit 44	TOE Queue 44 Full Interrupt
+		unsigned int toe_q45_full	: 1;	// bit 45	TOE Queue 45 Full Interrupt
+		unsigned int toe_q46_full	: 1;	// bit 46	TOE Queue 46 Full Interrupt
+		unsigned int toe_q47_full	: 1;	// bit 47	TOE Queue 47 Full Interrupt
+		unsigned int toe_q48_full	: 1;	// bit 48	TOE Queue 48 Full Interrupt
+		unsigned int toe_q49_full	: 1;	// bit 49	TOE Queue 49 Full Interrupt
+		unsigned int toe_q50_full	: 1;	// bit 50	TOE Queue 50 Full Interrupt
+		unsigned int toe_q51_full	: 1;	// bit 51	TOE Queue 51 Full Interrupt
+		unsigned int toe_q52_full	: 1;	// bit 52	TOE Queue 52 Full Interrupt
+		unsigned int toe_q53_full	: 1;	// bit 53	TOE Queue 53 Full Interrupt
+		unsigned int toe_q54_full	: 1;	// bit 54	TOE Queue 54 Full Interrupt
+		unsigned int toe_q55_full	: 1;	// bit 55	TOE Queue 55 Full Interrupt
+		unsigned int toe_q56_full	: 1;	// bit 56	TOE Queue 56 Full Interrupt
+		unsigned int toe_q57_full	: 1;	// bit 57	TOE Queue 57 Full Interrupt
+		unsigned int toe_q58_full	: 1;	// bit 58	TOE Queue 58 Full Interrupt
+		unsigned int toe_q59_full	: 1;	// bit 59	TOE Queue 59 Full Interrupt
+		unsigned int toe_q60_full	: 1;	// bit 60	TOE Queue 60 Full Interrupt
+		unsigned int toe_q61_full	: 1;	// bit 61	TOE Queue 61 Full Interrupt
+		unsigned int toe_q62_full	: 1;	// bit 62	TOE Queue 62 Full Interrupt
+		unsigned int toe_q63_full	: 1;	// bit 63	TOE Queue 63 Full Interrupt
+	} bits;
+} INTR_REG3_T;
+
+#define TOE_QH_FULL_INT_BIT(x)		BIT(x-32)
+
+/*
+ * Interrupt Status Register 4 	(offset 0x0060)
+ * Interrupt Mask Register 4 	(offset 0x0064)
+ * Interrupt Select Register 4 	(offset 0x0068)
+ */
+typedef union {
+	unsigned char byte;
+	struct bit_0060 {
+		unsigned char status_changed	: 1;	// Status Changed Intr for RGMII Mode
+		unsigned char rx_overrun	: 1;   // GMAC Rx FIFO overrun interrupt
+		unsigned char tx_pause_off	: 1;	// received pause off frame interrupt
+		unsigned char rx_pause_off	: 1;	// received pause off frame interrupt
+		unsigned char tx_pause_on	: 1;	// transmit pause on frame interrupt
+		unsigned char rx_pause_on	: 1;	// received pause on frame interrupt
+		unsigned char cnt_full 		: 1;	// MIB counters half full interrupt
+		unsigned char reserved		: 1;	//
+	} __packed bits;
+} __packed GMAC_INTR_T;
+
+typedef union {
+	unsigned int bits32;
+	struct bit_0060_2 {
+		unsigned int    swfq_empty	: 1;	// bit 0	Software Free Queue Empty Intr.
+		unsigned int    hwfq_empty	: 1;	// bit 1	Hardware Free Queue Empty Intr.
+		unsigned int	class_qf_int	: 14;	// bit 15:2 Classification Rx Queue13-0 Full Intr.
+		GMAC_INTR_T	gmac0;
+		GMAC_INTR_T	gmac1;
+	} bits;
+} INTR_REG4_T;
+
+#define GMAC1_RESERVED_INT_BIT		BIT(31)
+#define GMAC1_MIB_INT_BIT		BIT(30)
+#define GMAC1_RX_PAUSE_ON_INT_BIT	BIT(29)
+#define GMAC1_TX_PAUSE_ON_INT_BIT	BIT(28)
+#define GMAC1_RX_PAUSE_OFF_INT_BIT	BIT(27)
+#define GMAC1_TX_PAUSE_OFF_INT_BIT	BIT(26)
+#define GMAC1_RX_OVERRUN_INT_BIT	BIT(25)
+#define GMAC1_STATUS_CHANGE_INT_BIT	BIT(24)
+#define GMAC0_RESERVED_INT_BIT		BIT(23)
+#define GMAC0_MIB_INT_BIT		BIT(22)
+#define GMAC0_RX_PAUSE_ON_INT_BIT	BIT(21)
+#define GMAC0_TX_PAUSE_ON_INT_BIT	BIT(20)
+#define GMAC0_RX_PAUSE_OFF_INT_BIT	BIT(19)
+#define GMAC0_TX_PAUSE_OFF_INT_BIT	BIT(18)
+#define GMAC0_RX_OVERRUN_INT_BIT	BIT(17)
+#define GMAC0_STATUS_CHANGE_INT_BIT	BIT(16)
+#define CLASS_RX_FULL_INT_BIT(x)	BIT((x+2))
+#define HWFQ_EMPTY_INT_BIT		BIT(1)
+#define SWFQ_EMPTY_INT_BIT		BIT(0)
+
+#define GMAC0_INT_BITS		(GMAC0_RESERVED_INT_BIT | GMAC0_MIB_INT_BIT | \
+				 GMAC0_RX_PAUSE_ON_INT_BIT | GMAC0_TX_PAUSE_ON_INT_BIT |	\
+				 GMAC0_RX_PAUSE_OFF_INT_BIT | GMAC0_TX_PAUSE_OFF_INT_BIT | 	\
+				 GMAC0_RX_OVERRUN_INT_BIT | GMAC0_STATUS_CHANGE_INT_BIT)
+#define GMAC1_INT_BITS		(GMAC1_RESERVED_INT_BIT | GMAC1_MIB_INT_BIT | \
+				 GMAC1_RX_PAUSE_ON_INT_BIT | GMAC1_TX_PAUSE_ON_INT_BIT |	\
+				 GMAC1_RX_PAUSE_OFF_INT_BIT | GMAC1_TX_PAUSE_OFF_INT_BIT | 	\
+				 GMAC1_RX_OVERRUN_INT_BIT | GMAC1_STATUS_CHANGE_INT_BIT)
+
+#define CLASS_RX_FULL_INT_BITS		0xfffc
+
+/*
+ * GLOBAL_QUEUE_THRESHOLD_REG 	(offset 0x0070)
+ */
+typedef union {
+	unsigned int bits32;
+	struct bit_0070_2 {
+		unsigned int    swfq_empty	: 8;	//  7:0  	Software Free Queue Empty Threshold
+		unsigned int    hwfq_empty	: 8;	// 15:8		Hardware Free Queue Empty Threshold
+		unsigned int	intrq		: 8;	// 23:16
+		unsigned int	toe_class	: 8;	// 31:24
+	} bits;
+} QUEUE_THRESHOLD_T;
+
+
+/*
+ * GMAC DMA Control Register
+ * GMAC0 offset 0x8000
+ * GMAC1 offset 0xC000
+ */
+typedef union {
+	unsigned int bits32;
+	struct bit_8000 {
+		unsigned int	td_bus		: 2;	// bit 1:0	Peripheral Bus Width
+		unsigned int	td_burst_size	: 2;	// bit 3:2	TxDMA max burst size for every AHB request
+		unsigned int	td_prot		: 4;	// bit 7:4	TxDMA protection control
+		unsigned int	rd_bus		: 2;	// bit 9:8 	Peripheral Bus Width
+		unsigned int	rd_burst_size	: 2;	// bit 11:10	DMA max burst size for every AHB request
+		unsigned int	rd_prot		: 4;	// bit 15:12	DMA Protection Control
+		unsigned int	rd_insert_bytes	: 2;	// bit 17:16
+		unsigned int	reserved	: 10;	// bit 27:18
+		unsigned int    drop_small_ack	: 1;	// bit 28	1: Drop, 0: Accept
+		unsigned int    loopback	: 1;	// bit 29	Loopback TxDMA to RxDMA
+		unsigned int    td_enable	: 1;	// bit 30	Tx DMA Enable
+		unsigned int    rd_enable	: 1;	// bit 31	Rx DMA Enable
+	} bits;
+} GMAC_DMA_CTRL_T;
+
+/*
+ * GMAC Tx Weighting Control Register 0
+ * GMAC0 offset 0x8004
+ * GMAC1 offset 0xC004
+ */
+typedef union {
+	unsigned int bits32;
+	struct bit_8004 {
+		unsigned int    hw_tq0		: 6;	// bit 5:0	HW TX Queue 3
+		unsigned int    hw_tq1		: 6;	// bit 11:6	HW TX Queue 2
+		unsigned int    hw_tq2		: 6;	// bit 17:12	HW TX Queue 1
+		unsigned int    hw_tq3		: 6;	// bit 23:18	HW TX Queue 0
+		unsigned int    reserved	: 8;	// bit 31:24
+	} bits;
+} GMAC_TX_WCR0_T;	/* Weighting Control Register 0 */
+
+/*
+ * GMAC Tx Weighting Control Register 1
+ * GMAC0 offset 0x8008
+ * GMAC1 offset 0xC008
+ */
+typedef union {
+	unsigned int bits32;
+	struct bit_8008 {
+		unsigned int    sw_tq0		: 5;	// bit 4:0	SW TX Queue 0
+		unsigned int    sw_tq1		: 5;	// bit 9:5	SW TX Queue 1
+		unsigned int    sw_tq2		: 5;	// bit 14:10	SW TX Queue 2
+		unsigned int    sw_tq3		: 5;	// bit 19:15	SW TX Queue 3
+		unsigned int    sw_tq4		: 5;	// bit 24:20	SW TX Queue 4
+		unsigned int    sw_tq5		: 5;	// bit 29:25	SW TX Queue 5
+		unsigned int    reserved	: 2;	// bit 31:30
+	} bits;
+} GMAC_TX_WCR1_T;	/* Weighting Control Register 1 */
+
+/*
+ * Queue Read/Write Pointer
+ * GMAC SW TX Queue 0~5 Read/Write Pointer register
+ * GMAC0 offset 0x800C ~ 0x8020
+ * GMAC1 offset 0xC00C ~ 0xC020
+ * GMAC HW TX Queue 0~3 Read/Write Pointer register
+ * GMAC0 offset 0x8024 ~ 0x8030
+ * GMAC1 offset 0xC024 ~ 0xC030
+ *
+ * see DMA_RWPTR_T structure
+ */
+
+/*
+ * GMAC DMA Tx First Description Address Register
+ * GMAC0 offset 0x8038
+ * GMAC1 offset 0xC038
+ */
+typedef union {
+	unsigned int bits32;
+	struct bit_8038 {
+		unsigned int reserved		:  3;
+		unsigned int td_busy		:  1;	// bit 3	1: TxDMA busy; 0: TxDMA idle
+		unsigned int td_first_des_ptr	: 28;	// bit 31:4	first descriptor address
+	} bits;
+} GMAC_TXDMA_FIRST_DESC_T;
+
+/*
+ * GMAC DMA Tx Current Description Address Register
+ * GMAC0 offset 0x803C
+ * GMAC1 offset 0xC03C
+ */
+typedef union {
+	unsigned int bits32;
+	struct bit_803C {
+		unsigned int reserved		:  4;
+		unsigned int td_curr_desc_ptr	: 28;	// bit 31:4	current descriptor address
+	} bits;
+} GMAC_TXDMA_CURR_DESC_T;
+
+/*
+ * GMAC DMA Tx Description Word 0 Register
+ * GMAC0 offset 0x8040
+ * GMAC1 offset 0xC040
+ */
+typedef union {
+	unsigned int bits32;
+	struct bit_8040 {
+		unsigned int buffer_size 	: 16;	// bit 15:0	Transfer size
+		unsigned int desc_count 	: 6;	// bit 21:16	number of descriptors used for the current frame
+		unsigned int status_tx_ok	: 1;	// bit 22	Tx Status, 1: Successful 0: Failed
+		unsigned int status_rvd		: 6;	// bit 28:23	Tx Status, Reserved bits
+		unsigned int perr		: 1;	// bit 29	protocol error during processing this descriptor
+		unsigned int derr		: 1;	// bit 30	data error during processing this descriptor
+		unsigned int reserved		: 1;	// bit 31
+	} bits;
+} GMAC_TXDESC_0_T;
+
+/*
+ * GMAC DMA Tx Description Word 1 Register
+ * GMAC0 offset 0x8044
+ * GMAC1 offset 0xC044
+ */
+typedef union {
+	unsigned int bits32;
+	struct txdesc_word1 {
+		unsigned int	byte_count	: 16;	// bit 15: 0	Tx Frame Byte Count
+		unsigned int	mtu_enable	: 1;	// bit 16	TSS segmentation use MTU setting
+		unsigned int	ip_chksum	: 1;	// bit 17	IPV4 Header Checksum Enable
+		unsigned int	ipv6_enable	: 1;	// bit 18	IPV6 Tx Enable
+		unsigned int	tcp_chksum	: 1;	// bit 19	TCP Checksum Enable
+		unsigned int	udp_chksum	: 1;	// bit 20	UDP Checksum Enable
+		unsigned int	bypass_tss	: 1;	// bit 21
+		unsigned int	ip_fixed_len	: 1;	// bit 22
+		unsigned int	reserved	: 9;	// bit 31:23 	Tx Flag, Reserved
+	} bits;
+} GMAC_TXDESC_1_T;
+
+#define TSS_IP_FIXED_LEN_BIT	BIT(22)
+#define TSS_BYPASS_BIT		BIT(21)
+#define TSS_UDP_CHKSUM_BIT	BIT(20)
+#define TSS_TCP_CHKSUM_BIT	BIT(19)
+#define TSS_IPV6_ENABLE_BIT	BIT(18)
+#define TSS_IP_CHKSUM_BIT	BIT(17)
+#define TSS_MTU_ENABLE_BIT	BIT(16)
+
+#define TSS_CHECKUM_ENABLE	\
+	(TSS_IP_CHKSUM_BIT|TSS_IPV6_ENABLE_BIT| \
+	 TSS_TCP_CHKSUM_BIT|TSS_UDP_CHKSUM_BIT)
+
+/*
+ * GMAC DMA Tx Description Word 2 Register
+ * GMAC0 offset 0x8048
+ * GMAC1 offset 0xC048
+ */
+typedef union {
+	unsigned int	bits32;
+	unsigned int 	buf_adr;
+} GMAC_TXDESC_2_T;
+
+/*
+ * GMAC DMA Tx Description Word 3 Register
+ * GMAC0 offset 0x804C
+ * GMAC1 offset 0xC04C
+ */
+typedef union {
+	unsigned int bits32;
+	struct txdesc_word3 {
+		unsigned int	mtu_size	: 11;	// bit 10: 0	Tx Frame Byte Count
+		unsigned int	reserved	: 18;	// bit 28:11
+		unsigned int	eofie		: 1;	// bit 29	End of frame interrupt enable
+		unsigned int	sof_eof		: 2;	// bit 31:30 	11: only one, 10: first, 01: last, 00: linking
+	} bits;
+} GMAC_TXDESC_3_T;
+#define SOF_EOF_BIT_MASK	0x3fffffff
+#define SOF_BIT			0x80000000
+#define EOF_BIT			0x40000000
+#define EOFIE_BIT		BIT(29)
+#define MTU_SIZE_BIT_MASK	0x7ff
+
+/*
+ * GMAC Tx Descriptor
+ */
+typedef struct {
+	GMAC_TXDESC_0_T	word0;
+	GMAC_TXDESC_1_T	word1;
+	GMAC_TXDESC_2_T	word2;
+	GMAC_TXDESC_3_T	word3;
+} GMAC_TXDESC_T;
+
+/*
+ * GMAC DMA Rx First Description Address Register
+ * GMAC0 offset 0x8058
+ * GMAC1 offset 0xC058
+ */
+typedef union {
+	unsigned int bits32;
+	struct bit_8058 {
+		unsigned int reserved		:  3;	// bit 2:0
+		unsigned int rd_busy		:  1;	// bit 3	1-RxDMA busy; 0-RxDMA idle
+		unsigned int rd_first_des_ptr	: 28;	// bit 31:4 first descriptor address
+	} bits;
+} GMAC_RXDMA_FIRST_DESC_T;
+
+/*
+ * GMAC DMA Rx Current Description Address Register
+ * GMAC0 offset 0x805C
+ * GMAC1 offset 0xC05C
+ */
+typedef union {
+	unsigned int bits32;
+	struct bit_805C {
+		unsigned int reserved		:  4;	// bit 3:0
+		unsigned int rd_curr_des_ptr	: 28;	// bit 31:4 current descriptor address
+	} bits;
+} GMAC_RXDMA_CURR_DESC_T;
+
+/*
+ * GMAC DMA Rx Description Word 0 Register
+ * GMAC0 offset 0x8060
+ * GMAC1 offset 0xC060
+ */
+typedef union {
+	unsigned int bits32;
+	struct bit_8060 {
+		unsigned int buffer_size 	: 16;	// bit 15:0  number of descriptors used for the current frame
+		unsigned int desc_count 	: 6;	// bit 21:16 number of descriptors used for the current frame
+		unsigned int status		: 4;	// bit 24:22 Status of rx frame
+		unsigned int chksum_status	: 3;	// bit 28:26 Check Sum Status
+		unsigned int perr		: 1;	// bit 29	 protocol error during processing this descriptor
+		unsigned int derr		: 1;	// bit 30	 data error during processing this descriptor
+		unsigned int drop		: 1;	// bit 31	 TOE/CIS Queue Full dropped packet to default queue
+	} bits;
+} GMAC_RXDESC_0_T;
+
+#define		GMAC_RXDESC_0_T_derr			BIT(30)
+#define		GMAC_RXDESC_0_T_perr			BIT(29)
+#define		GMAC_RXDESC_0_T_chksum_status(x)	BIT((x+26))
+#define		GMAC_RXDESC_0_T_status(x)		BIT((x+22))
+#define		GMAC_RXDESC_0_T_desc_count(x)		BIT((x+16))
+
+#define	RX_CHKSUM_IP_UDP_TCP_OK			0
+#define	RX_CHKSUM_IP_OK_ONLY			1
+#define	RX_CHKSUM_NONE				2
+#define	RX_CHKSUM_IP_ERR_UNKNOWN		4
+#define	RX_CHKSUM_IP_ERR			5
+#define	RX_CHKSUM_TCP_UDP_ERR			6
+#define RX_CHKSUM_NUM				8
+
+#define RX_STATUS_GOOD_FRAME			0
+#define RX_STATUS_TOO_LONG_GOOD_CRC		1
+#define RX_STATUS_RUNT_FRAME			2
+#define RX_STATUS_SFD_NOT_FOUND			3
+#define RX_STATUS_CRC_ERROR			4
+#define RX_STATUS_TOO_LONG_BAD_CRC		5
+#define RX_STATUS_ALIGNMENT_ERROR		6
+#define RX_STATUS_TOO_LONG_BAD_ALIGN		7
+#define RX_STATUS_RX_ERR			8
+#define RX_STATUS_DA_FILTERED			9
+#define RX_STATUS_BUFFER_FULL			10
+#define RX_STATUS_NUM				16
+
+#define RX_ERROR_LENGTH(s) \
+	((s) == RX_STATUS_TOO_LONG_GOOD_CRC || \
+	 (s) == RX_STATUS_TOO_LONG_BAD_CRC || \
+	 (s) == RX_STATUS_TOO_LONG_BAD_ALIGN)
+#define RX_ERROR_OVER(s) \
+	((s) == RX_STATUS_BUFFER_FULL)
+#define RX_ERROR_CRC(s) \
+	((s) == RX_STATUS_CRC_ERROR || \
+	 (s) == RX_STATUS_TOO_LONG_BAD_CRC)
+#define RX_ERROR_FRAME(s) \
+	((s) == RX_STATUS_ALIGNMENT_ERROR || \
+	 (s) == RX_STATUS_TOO_LONG_BAD_ALIGN)
+#define RX_ERROR_FIFO(s) \
+	(0)
+
+/*
+ * GMAC DMA Rx Description Word 1 Register
+ * GMAC0 offset 0x8064
+ * GMAC1 offset 0xC064
+ */
+typedef union {
+	unsigned int bits32;
+	struct rxdesc_word1 {
+		unsigned int	byte_count	: 16;	// bit 15: 0	Rx Frame Byte Count
+		unsigned int	sw_id		: 16;	// bit 31:16	Software ID
+	} bits;
+} GMAC_RXDESC_1_T;
+
+/*
+ * GMAC DMA Rx Description Word 2 Register
+ * GMAC0 offset 0x8068
+ * GMAC1 offset 0xC068
+ */
+typedef union {
+	unsigned int	bits32;
+	unsigned int	buf_adr;
+} GMAC_RXDESC_2_T;
+
+#define RX_INSERT_NONE		0
+#define RX_INSERT_1_BYTE	1
+#define RX_INSERT_2_BYTE	2
+#define RX_INSERT_3_BYTE	3
+
+/*
+ * GMAC DMA Rx Description Word 3 Register
+ * GMAC0 offset 0x806C
+ * GMAC1 offset 0xC06C
+ */
+typedef union {
+	unsigned int bits32;
+	struct rxdesc_word3 {
+		unsigned int	l3_offset	: 8;	// bit 7: 0	L3 data offset
+		unsigned int	l4_offset	: 8;	// bit 15: 8	L4 data offset
+		unsigned int	l7_offset	: 8;	// bit 23: 16	L7 data offset
+		unsigned int	dup_ack		: 1;	// bit 24	Duplicated ACK detected
+		unsigned int	abnormal	: 1;	// bit 25	abnormal case found
+		unsigned int	option		: 1;	// bit 26	IPV4 option or IPV6 extension header
+		unsigned int	out_of_seq	: 1;	// bit 27	Out of Sequence packet
+		unsigned int	ctrl_flag	: 1;	// bit 28 	Control Flag is present
+		unsigned int	eofie		: 1;	// bit 29	End of frame interrupt enable
+		unsigned int	sof_eof		: 2;	// bit 31:30 	11: only one, 10: first, 01: last, 00: linking
+	} bits;
+} GMAC_RXDESC_3_T;
+
+/*
+ * GMAC Rx Descriptor
+ */
+typedef struct {
+	GMAC_RXDESC_0_T	word0;
+	GMAC_RXDESC_1_T	word1;
+	GMAC_RXDESC_2_T	word2;
+	GMAC_RXDESC_3_T	word3;
+} GMAC_RXDESC_T;
+
+/*
+ * GMAC Hash Engine Enable/Action Register 0 Offset Register
+ * GMAC0 offset 0x8070
+ * GMAC1 offset 0xC070
+ */
+typedef union {
+	unsigned int bits32;
+	struct bit_8070 {
+		unsigned int	mr0hel		: 6;	// bit 5:0	match rule 0 hash entry size
+		unsigned int	mr0_action	: 5;	// bit 10:6	Matching Rule 0 action offset
+		unsigned int	reserved0	: 4;	// bit 14:11
+		unsigned int	mr0en		: 1;	// bit 15	Enable Matching Rule 0
+		unsigned int	mr1hel		: 6;	// bit 21:16	match rule 1 hash entry size
+		unsigned int	mr1_action	: 5;	// bit 26:22	Matching Rule 1 action offset
+		unsigned int	timing		: 3;	// bit 29:27
+		unsigned int	reserved1	: 1;	// bit 30
+		unsigned int	mr1en		: 1;	// bit 31	Enable Matching Rule 1
+	} bits;
+} GMAC_HASH_ENABLE_REG0_T;
+
+/*
+ * GMAC Hash Engine Enable/Action Register 1 Offset Register
+ * GMAC0 offset 0x8074
+ * GMAC1 offset 0xC074
+ */
+typedef union {
+	unsigned int bits32;
+	struct bit_8074 {
+		unsigned int	mr2hel		: 6;	// bit 5:0	match rule 2 hash entry size
+		unsigned int	mr2_action	: 5;	// bit 10:6	Matching Rule 2 action offset
+		unsigned int	reserved2	: 4;	// bit 14:11
+		unsigned int	mr2en		: 1;	// bit 15	Enable Matching Rule 2
+		unsigned int	mr3hel		: 6;	// bit 21:16	match rule 3 hash entry size
+		unsigned int	mr3_action	: 5;	// bit 26:22	Matching Rule 3 action offset
+		unsigned int	reserved1	: 4;	// bit 30:27
+		unsigned int	mr3en		: 1;	// bit 31	Enable Matching Rule 3
+	} bits;
+} GMAC_HASH_ENABLE_REG1_T;
+
+/*
+ * GMAC Matching Rule Control Register 0
+ * GMAC0 offset 0x8078
+ * GMAC1 offset 0xC078
+ */
+typedef union {
+	unsigned int bits32;
+	struct bit_8078 {
+		unsigned int	sprx		: 8;	// bit 7:0	Support Protocol Register 7:0
+		unsigned int	reserved2	: 4;	// bit 11:8
+		unsigned int	tos_traffic	: 1;	// bit 12	IPV4 TOS or IPV6 Traffice Class
+		unsigned int	flow_lable	: 1;	// bit 13	IPV6 Flow label
+		unsigned int	ip_hdr_len	: 1;	// bit 14	IPV4 Header length
+		unsigned int	ip_version	: 1;	// bit 15	0: IPV4, 1: IPV6
+		unsigned int	reserved1	: 3;	// bit 18:16
+		unsigned int	pppoe		: 1;	// bit 19	PPPoE Session ID enable
+		unsigned int	vlan		: 1;	// bit 20	VLAN ID enable
+		unsigned int	ether_type	: 1;	// bit 21	Ethernet type enable
+		unsigned int	sa		: 1;	// bit 22	MAC SA enable
+		unsigned int	da		: 1;	// bit 23	MAC DA enable
+		unsigned int	priority	: 3;	// bit 26:24	priority if multi-rules matched
+		unsigned int	port		: 1;	// bit 27	PORT ID matching enable
+		unsigned int	l7		: 1;	// bit 28	L7 matching enable
+		unsigned int	l4		: 1;	// bit 29	L4 matching enable
+		unsigned int	l3		: 1;	// bit 30	L3 matching enable
+		unsigned int	l2		: 1;	// bit 31	L2 matching enable
+	} bits;
+} GMAC_MRxCR0_T;
+
+#define MR_L2_BIT		BIT(31)
+#define MR_L3_BIT		BIT(30)
+#define MR_L4_BIT		BIT(29)
+#define MR_L7_BIT		BIT(28)
+#define MR_PORT_BIT		BIT(27)
+#define MR_PRIORITY_BIT		BIT(26)
+#define MR_DA_BIT		BIT(23)
+#define MR_SA_BIT		BIT(22)
+#define MR_ETHER_TYPE_BIT	BIT(21)
+#define MR_VLAN_BIT		BIT(20)
+#define MR_PPPOE_BIT		BIT(19)
+#define MR_IP_VER_BIT		BIT(15)
+#define MR_IP_HDR_LEN_BIT	BIT(14)
+#define MR_FLOW_LABLE_BIT	BIT(13)
+#define MR_TOS_TRAFFIC_BIT	BIT(12)
+#define MR_SPR_BIT(x)		BIT(x)
+#define MR_SPR_BITS		0xff
+
+/*
+ * GMAC Matching Rule Control Register 1
+ * GMAC0 offset 0x807C
+ * GMAC1 offset 0xC07C
+ */
+typedef union {
+	unsigned int bits32;
+	struct bit_807C {
+		unsigned int    l4_byte0_15	: 16;	// bit 15: 0
+		unsigned int	dip_netmask	: 7;	// bit 22:16	Dest IP net mask, number of mask bits
+		unsigned int	dip		: 1;	// bit 23		Dest IP
+		unsigned int	sip_netmask	: 7;	// bit 30:24	Srce IP net mask, number of mask bits
+		unsigned int	sip		: 1;	// bit 31		Srce IP
+	} bits;
+} GMAC_MRxCR1_T;
+
+/*
+ * GMAC Matching Rule Control Register 2
+ * GMAC0 offset 0x8080
+ * GMAC1 offset 0xC080
+ */
+typedef union {
+	unsigned int bits32;
+	struct bit_8080 {
+		unsigned int    l7_byte0_23	: 24;	// bit 23:0
+		unsigned int    l4_byte16_24	: 8;	// bit 31: 24
+	} bits;
+} GMAC_MRxCR2_T;
+
+/*
+ * GMAC Support registers
+ * GMAC0 offset 0x80A8
+ * GMAC1 offset 0xC0A8
+ */
+typedef union {
+	unsigned int bits32;
+	struct bit_80A8 {
+		unsigned int    protocol	: 8;	// bit 7:0		Supported protocol
+		unsigned int    swap		: 3;	// bit 10:8		Swap
+		unsigned int    reserved	: 21;	// bit 31:11
+	} bits;
+} GMAC_SPR_T;
+
+/*
+ * GMAC_AHB_WEIGHT registers
+ * GMAC0 offset 0x80C8
+ * GMAC1 offset 0xC0C8
+ */
+typedef union {
+	unsigned int bits32;
+	struct bit_80C8 {
+		unsigned int    hash_weight	: 5;	// 4:0
+		unsigned int    rx_weight	: 5;	// 9:5
+		unsigned int    tx_weight	: 5;	// 14:10
+		unsigned int    pre_req		: 5;	// 19:15 Rx Data Pre Request FIFO Threshold
+		unsigned int    tqDV_threshold	: 5;	// 24:20 DMA TqCtrl to Start tqDV FIFO Threshold
+		unsigned int    reserved	: 7;	// 31:25
+	} bits;
+} GMAC_AHB_WEIGHT_T;
+
+/*
+ * the register structure of GMAC
+ */
+
+/*
+ * GMAC RX FLTR
+ * GMAC0 Offset 0xA00C
+ * GMAC1 Offset 0xE00C
+ */
+typedef union {
+	unsigned int bits32;
+	struct bit1_000c {
+		unsigned int unicast		:  1;	/* enable receive of unicast frames that are sent to STA address */
+		unsigned int multicast		:  1;	/* enable receive of multicast frames that pass multicast filter */
+		unsigned int broadcast		:  1;	/* enable receive of broadcast frames */
+		unsigned int promiscuous	:  1;   /* enable receive of all frames */
+		unsigned int error		:  1;	/* enable receive of all error frames */
+		unsigned int 			: 27;
+	} bits;
+} GMAC_RX_FLTR_T;
+
+/*
+ * GMAC Configuration 0
+ * GMAC0 Offset 0xA018
+ * GMAC1 Offset 0xE018
+ */
+typedef union {
+	unsigned int bits32;
+	struct bit1_0018 {
+		unsigned int dis_tx		:  1;	/* 0: disable transmit */
+		unsigned int dis_rx		:  1;	/* 1: disable receive */
+		unsigned int loop_back		:  1;	/* 2: transmit data loopback enable */
+		unsigned int flow_ctrl		:  1;   /* 3: flow control also trigged by Rx queues */
+		unsigned int adj_ifg		:  4;	/* 4-7: adjust IFG from 96+/-56 */
+		unsigned int max_len		:  3;	/* 8-10 maximum receive frame length allowed */
+		unsigned int dis_bkoff		:  1;	/* 11: disable back-off function */
+		unsigned int dis_col		:  1;	/* 12: disable 16 collisions abort function */
+		unsigned int sim_test		:  1;	/* 13: speed up timers in simulation */
+		unsigned int rx_fc_en		:  1;	/* 14: RX flow control enable */
+		unsigned int tx_fc_en		:  1;	/* 15: TX flow control enable */
+		unsigned int rgmii_en		:  1;   /* 16: RGMII in-band status enable */
+		unsigned int ipv4_rx_chksum	:  1;   /* 17: IPv4 RX Checksum enable */
+		unsigned int ipv6_rx_chksum	:  1;   /* 18: IPv6 RX Checksum enable */
+		unsigned int rx_tag_remove	:  1;   /* 19: Remove Rx VLAN tag */
+		unsigned int rgmm_edge		:  1;	// 20
+		unsigned int rxc_inv		:  1;	// 21
+		unsigned int ipv6_exthdr_order	:  1;	// 22
+		unsigned int rx_err_detect	:  1;	// 23
+		unsigned int port0_chk_hwq	:  1;	// 24
+		unsigned int port1_chk_hwq	:  1;	// 25
+		unsigned int port0_chk_toeq	:  1;	// 26
+		unsigned int port1_chk_toeq	:  1;	// 27
+		unsigned int port0_chk_classq	:  1;	// 28
+		unsigned int port1_chk_classq	:  1;	// 29
+		unsigned int reserved		:  2;	// 31
+	} bits;
+} GMAC_CONFIG0_T;
+
+#define CONFIG0_TX_RX_DISABLE	(BIT(1)|BIT(0))
+#define CONFIG0_RX_CHKSUM	(BIT(18)|BIT(17))
+#define CONFIG0_FLOW_RX		(BIT(14))
+#define CONFIG0_FLOW_TX		(BIT(15))
+#define CONFIG0_FLOW_TX_RX	(BIT(14)|BIT(15))
+#define CONFIG0_FLOW_CTL	(BIT(14)|BIT(15))
+
+#define CONFIG0_MAXLEN_SHIFT	8
+#define CONFIG0_MAXLEN_MASK	(7 << CONFIG0_MAXLEN_SHIFT)
+#define  CONFIG0_MAXLEN_1536	0
+#define  CONFIG0_MAXLEN_1518	1
+#define  CONFIG0_MAXLEN_1522	2
+#define  CONFIG0_MAXLEN_1542	3
+#define  CONFIG0_MAXLEN_9k	4	/* 9212 */
+#define  CONFIG0_MAXLEN_10k	5	/* 10236 */
+#define  CONFIG0_MAXLEN_1518__6	6
+#define  CONFIG0_MAXLEN_1518__7	7
+
+/*
+ * GMAC Configuration 1
+ * GMAC0 Offset 0xA01C
+ * GMAC1 Offset 0xE01C
+ */
+typedef union {
+	unsigned int bits32;
+	struct bit1_001c {
+		unsigned int set_threshold	: 8; 	/* flow control set threshold */
+		unsigned int rel_threshold	: 8;	/* flow control release threshold */
+		unsigned int reserved		: 16;
+	} bits;
+} GMAC_CONFIG1_T;
+
+#define GMAC_FLOWCTRL_SET_MAX		32
+#define GMAC_FLOWCTRL_SET_MIN		0
+#define GMAC_FLOWCTRL_RELEASE_MAX	32
+#define GMAC_FLOWCTRL_RELEASE_MIN	0
+
+/*
+ * GMAC Configuration 2
+ * GMAC0 Offset 0xA020
+ * GMAC1 Offset 0xE020
+ */
+typedef union {
+	unsigned int bits32;
+	struct bit1_0020 {
+		unsigned int set_threshold	: 16; 	/* flow control set threshold */
+		unsigned int rel_threshold	: 16;	/* flow control release threshold */
+	} bits;
+} GMAC_CONFIG2_T;
+
+/*
+ * GMAC Configuration 3
+ * GMAC0 Offset 0xA024
+ * GMAC1 Offset 0xE024
+ */
+typedef union {
+	unsigned int bits32;
+	struct bit1_0024 {
+		unsigned int set_threshold	: 16; 	/* flow control set threshold */
+		unsigned int rel_threshold	: 16;	/* flow control release threshold */
+	} bits;
+} GMAC_CONFIG3_T;
+
+
+/*
+ * GMAC STATUS
+ * GMAC0 Offset 0xA02C
+ * GMAC1 Offset 0xE02C
+ */
+typedef union {
+	unsigned int bits32;
+	struct bit1_002c {
+		unsigned int link		:  1;	/* link status */
+		unsigned int speed		:  2;	/* link speed(00->2.5M 01->25M 10->125M) */
+		unsigned int duplex		:  1;	/* duplex mode */
+		unsigned int reserved		:  1;
+		unsigned int mii_rmii		:  2;   /* PHY interface type */
+		unsigned int 			: 25;
+	} bits;
+} GMAC_STATUS_T;
+
+#define GMAC_SPEED_10			0
+#define GMAC_SPEED_100			1
+#define GMAC_SPEED_1000			2
+
+#define GMAC_PHY_MII			0
+#define GMAC_PHY_GMII			1
+#define GMAC_PHY_RGMII_100_10		2
+#define GMAC_PHY_RGMII_1000		3
+
+/*
+ * Queue Header
+ *	(1) TOE Queue Header
+ *	(2) Non-TOE Queue Header
+ *	(3) Interrupt Queue Header
+ *
+ * memory Layout
+ *	TOE Queue Header
+ *	 0x60003000 +---------------------------+ 0x0000
+ *				|     TOE Queue 0 Header	|
+ *				|         8 * 4 Bytes	    |
+ *				+---------------------------+ 0x0020
+ *				|     TOE Queue 1 Header  	|
+ *				|         8 * 4 Bytes		|
+ *				+---------------------------+ 0x0040
+ *				|   	......  			|
+ *				|      						|
+ *				+---------------------------+
+ *
+ *	Non TOE Queue Header
+ *	 0x60002000 +---------------------------+ 0x0000
+ *				|   Default Queue 0 Header  |
+ *				|         2 * 4 Bytes		|
+ *				+---------------------------+ 0x0008
+ *				|   Default Queue 1 Header	|
+ *				|         2 * 4 Bytes		|
+ *				+---------------------------+ 0x0010
+ *				|   Classification Queue 0	|
+ *				|      	  2 * 4 Bytes		|
+ *				+---------------------------+
+ *				|   Classification Queue 1	|
+ *				|      	  2 * 4 Bytes		|
+ *				+---------------------------+ (n * 8 + 0x10)
+ *				|   		...				|
+ *				|      	  2 * 4 Bytes		|
+ *				+---------------------------+ (13 * 8 + 0x10)
+ *				|   Classification Queue 13	|
+ *				|      	  2 * 4 Bytes		|
+ *				+---------------------------+ 0x80
+ * 				|      Interrupt Queue 0	|
+ *				|      	  2 * 4 Bytes		|
+ *				+---------------------------+
+ * 				|      Interrupt Queue 1	|
+ *				|      	  2 * 4 Bytes		|
+ *				+---------------------------+
+ * 				|      Interrupt Queue 2	|
+ *				|      	  2 * 4 Bytes		|
+ *				+---------------------------+
+ * 				|      Interrupt Queue 3	|
+ *				|      	  2 * 4 Bytes		|
+ *				+---------------------------+
+ *
+ */
+#define TOE_QUEUE_HDR_ADDR(n)		(TOE_TOE_QUE_HDR_BASE + n * 32)
+#define TOE_Q_HDR_AREA_END		(TOE_QUEUE_HDR_ADDR(TOE_TOE_QUEUE_MAX + 1))
+#define TOE_DEFAULT_Q_HDR_BASE(x)	(TOE_NONTOE_QUE_HDR_BASE + 0x08 * (x))
+#define TOE_CLASS_Q_HDR_BASE		(TOE_NONTOE_QUE_HDR_BASE + 0x10)
+#define TOE_INTR_Q_HDR_BASE		(TOE_NONTOE_QUE_HDR_BASE + 0x80)
+#define INTERRUPT_QUEUE_HDR_ADDR(n)	(TOE_INTR_Q_HDR_BASE + n * 8)
+#define NONTOE_Q_HDR_AREA_END		(INTERRUPT_QUEUE_HDR_ADDR(TOE_INTR_QUEUE_MAX + 1))
+/*
+ * TOE Queue Header Word 0
+ */
+typedef union {
+	unsigned int bits32;
+	unsigned int base_size;
+} TOE_QHDR0_T;
+
+#define TOE_QHDR0_BASE_MASK 	(~0x0f)
+
+/*
+ * TOE Queue Header Word 1
+ */
+typedef union {
+	unsigned int bits32;
+	struct bit_qhdr1 {
+		unsigned int rptr	: 16;	// bit 15:0
+		unsigned int wptr	: 16;	// bit 31:16
+	} bits;
+} TOE_QHDR1_T;
+
+/*
+ * TOE Queue Header Word 2
+ */
+typedef union {
+	unsigned int bits32;
+	struct bit_qhdr2 {
+		unsigned int TotalPktSize	: 17;	// bit 16: 0	Total packet size
+		unsigned int reserved		: 7;	// bit 23:17
+		unsigned int dack		: 1;	// bit 24	1: Duplicated ACK
+		unsigned int abn		: 1;	// bit 25	1: Abnormal case Found
+		unsigned int tcp_opt		: 1;	// bit 26	1: Have TCP option
+		unsigned int ip_opt		: 1;	// bit 27	1: have IPV4 option or IPV6 Extension header
+		unsigned int sat		: 1;	// bit 28	1: SeqCnt > SeqThreshold, or AckCnt > AckThreshold
+		unsigned int osq		: 1;	// bit 29	1: out of sequence
+		unsigned int ctl		: 1;	// bit 30	1: have control flag bits (except ack)
+		unsigned int usd		: 1;	// bit 31	0: if no data assembled yet
+	} bits;
+} TOE_QHDR2_T;
+
+/*
+ * TOE Queue Header Word 3
+ */
+typedef union {
+	unsigned int bits32;
+	unsigned int seq_num;
+} TOE_QHDR3_T;
+
+/*
+ * TOE Queue Header Word 4
+ */
+typedef union {
+	unsigned int bits32;
+	unsigned int ack_num;
+} TOE_QHDR4_T;
+
+/*
+ * TOE Queue Header Word 5
+ */
+typedef union {
+	unsigned int bits32;
+	struct bit_qhdr5 {
+		unsigned int AckCnt	: 16;	// bit 15:0
+		unsigned int SeqCnt	: 16;	// bit 31:16
+	} bits;
+} TOE_QHDR5_T;
+
+/*
+ * TOE Queue Header Word 6
+ */
+typedef union {
+	unsigned int bits32;
+	struct bit_qhdr6 {
+		unsigned int WinSize	: 16;	// bit 15:0
+		unsigned int iq_num	: 2;	// bit 17:16
+		unsigned int MaxPktSize	: 14;	// bit 31:18
+	} bits;
+} TOE_QHDR6_T;
+
+/*
+ * TOE Queue Header Word 7
+ */
+typedef union {
+	unsigned int bits32;
+	struct bit_qhdr7 {
+		unsigned int AckThreshold	: 16;	// bit 15:0
+		unsigned int SeqThreshold	: 16;	// bit 31:16
+	} bits;
+} TOE_QHDR7_T;
+
+/*
+ * TOE Queue Header
+ */
+typedef struct {
+	TOE_QHDR0_T		word0;
+	TOE_QHDR1_T		word1;
+	TOE_QHDR2_T		word2;
+	TOE_QHDR3_T		word3;
+	TOE_QHDR4_T		word4;
+	TOE_QHDR5_T		word5;
+	TOE_QHDR6_T		word6;
+	TOE_QHDR7_T		word7;
+} TOE_QHDR_T;
+
+/*
+ * NONTOE Queue Header Word 0
+ */
+typedef union {
+	unsigned int bits32;
+	unsigned int base_size;
+} NONTOE_QHDR0_T;
+
+#define NONTOE_QHDR0_BASE_MASK 	(~0x0f)
+
+/*
+ * NONTOE Queue Header Word 1
+ */
+typedef union {
+	unsigned int bits32;
+	struct bit_nonqhdr1 {
+		unsigned int rptr	: 16;	// bit 15:0
+		unsigned int wptr	: 16;	// bit 31:16
+	} bits;
+} NONTOE_QHDR1_T;
+
+/*
+ * Non-TOE Queue Header
+ */
+typedef struct {
+	NONTOE_QHDR0_T		word0;
+	NONTOE_QHDR1_T		word1;
+} NONTOE_QHDR_T;
+
+/*
+ * Interrupt Queue Header Word 0
+ */
+typedef union {
+	unsigned int bits32;
+	struct bit_intrqhdr0 {
+		unsigned int win_size	: 16;	// bit 15:0 	Descriptor Ring Size
+		unsigned int wptr	: 16;	// bit 31:16	Write Pointer where hw stopped
+	} bits;
+} INTR_QHDR0_T;
+
+/*
+ * Interrupt Queue Header Word 1
+ */
+typedef union {
+	unsigned int bits32;
+	struct bit_intrqhdr1 {
+		unsigned int TotalPktSize	: 17;	// bit 16: 0	Total packet size
+		unsigned int tcp_qid		: 8;	// bit 24:17	TCP Queue ID
+		unsigned int dack		: 1;	// bit 25	1: Duplicated ACK
+		unsigned int abn		: 1;	// bit 26	1: Abnormal case Found
+		unsigned int tcp_opt		: 1;	// bit 27	1: Have TCP option
+		unsigned int ip_opt		: 1;	// bit 28	1: have IPV4 option or IPV6 Extension header
+		unsigned int sat		: 1;	// bit 29	1: SeqCnt > SeqThreshold, or AckCnt > AckThreshold
+		unsigned int osq		: 1;	// bit 30	1: out of sequence
+		unsigned int ctl		: 1;	// bit 31	1: have control flag bits (except ack)
+	} bits;
+} INTR_QHDR1_T;
+
+/*
+ * Interrupt Queue Header Word 2
+ */
+typedef union {
+	unsigned int bits32;
+	unsigned int seq_num;
+} INTR_QHDR2_T;
+
+/*
+ * Interrupt Queue Header Word 3
+ */
+typedef union {
+	unsigned int bits32;
+	unsigned int ack_num;
+} INTR_QHDR3_T;
+
+/*
+ * Interrupt Queue Header Word 4
+ */
+typedef union {
+	unsigned int bits32;
+	struct bit_intrqhdr4 {
+		unsigned int AckCnt		: 16;	// bit 15:0	Ack# change since last ack# intr.
+		unsigned int SeqCnt		: 16;	// bit 31:16	Seq# change since last seq# intr.
+	} bits;
+} INTR_QHDR4_T;
+
+/*
+ * Interrupt Queue Header
+ */
+typedef struct {
+	INTR_QHDR0_T		word0;
+	INTR_QHDR1_T		word1;
+	INTR_QHDR2_T		word2;
+	INTR_QHDR3_T		word3;
+	INTR_QHDR4_T		word4;
+	unsigned int		word5;
+	unsigned int		word6;
+	unsigned int		word7;
+} INTR_QHDR_T;
+
+#endif /* _GMAC_SL351x_H */


^ permalink raw reply related

* Re: [PATCH 03/10] net/fec: add mac field into platform data and consolidate fec_get_mac
From: Uwe Kleine-König @ 2010-12-29 12:42 UTC (permalink / raw)
  To: Shawn Guo
  Cc: Baruch Siach, davem, gerg, eric, bryan.wu, r64343, B32542, lw,
	w.sang, s.hauer, linux-arm-kernel, netdev
In-Reply-To: <20101229115808.GF19347@freescale.com>

Hello Shawn,

On Wed, Dec 29, 2010 at 07:58:09PM +0800, Shawn Guo wrote:
> On Wed, Dec 29, 2010 at 11:31:38AM +0100, Uwe Kleine-König wrote:
> > On Wed, Dec 29, 2010 at 06:05:21PM +0800, Shawn Guo wrote:
> > > On Wed, Dec 29, 2010 at 08:53:30AM +0200, Baruch Siach wrote:
> > > 	if (iap == fec_mac_default)
> > > 		dev->dev_addr[ETH_ALEN-1] = fec_mac_default[ETH_ALEN-1] + fep->pdev->id;
> > Can this overflow?  (I didn't check the code, so my concern might be
> > completely stupid here.)
> No. dev->dev_addr points to netdev_hw_addr->addr, which is a 32 bytes array.
I didn't mean an out-of-bound access, but what is if
fec_mac_default[ETH_ALEN-1] is 0xff and you add 1?  Does that result in
0x100 or 0?  What if id is <0?  For big ids you might even handle a
carry to indixes <ETH_ALEN-2.

Best regards
Uwe

-- 
Pengutronix e.K.                           | Uwe Kleine-König            |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |

^ permalink raw reply

* Re: [PATCH 03/10] net/fec: add mac field into platform data and consolidate fec_get_mac
From: Shawn Guo @ 2010-12-29 12:00 UTC (permalink / raw)
  To: Uwe Kleine-König
  Cc: Baruch Siach, davem, gerg, eric, bryan.wu, r64343, B32542, lw,
	w.sang, s.hauer, linux-arm-kernel, netdev
In-Reply-To: <20101229111019.GA14221@pengutronix.de>

Hi Uwe,

On Wed, Dec 29, 2010 at 12:10:19PM +0100, Uwe Kleine-König wrote:
> Hi Shawn,
> 
> On Wed, Dec 29, 2010 at 07:08:29PM +0800, Shawn Guo wrote:
> > On Wed, Dec 29, 2010 at 11:37:58AM +0100, Uwe Kleine-König wrote:
> > > On Wed, Dec 29, 2010 at 06:30:15PM +0800, Shawn Guo wrote:
> > > > > On Tue, Dec 28, 2010 at 10:55:48PM +0800, Shawn Guo wrote:
> > > > [...]
> > > > > > +	/*
> > > > > > +	 * try to get mac address in following order:
> > > > > > +	 *
> > > > > > +	 * 1) kernel command line fec_mac=xx:xx:xx...
> > > > > > +	 */
> > > > > > +	iap = fec_mac_default;
> > > > > > +
> > > > > > +	/*
> > > > > > +	 * 2) from flash or fuse (via platform data)
> > > > > > +	 */
> > > > > 
> > > > > Again, how do you handle the dual MAC case?
> > > > > 
> > > > For the platform data case, the following patch reads both mac
> > > > addresses.
> > > > 
> > > > [PATCH 09/10] ARM: mx28: read fec mac address from ocotp
> > > > 
> > > > +static int __init mx28evk_fec_get_mac(void)
> > > > +{
> > > > +       int i, ret;
> > > > +       u32 val;
> > > > +
> > > > +       /*
> > > > +        * OCOTP only stores the last 4 octets for each mac address,
> > > > +        * so hard-coding the first two octets as Freescale OUI (00:04:9f)
> > > > +        * is needed.
> > > > +        */
> > > > +       for (i = 0; i < 2; i++) {
> > > > +               ret = mxs_read_ocotp(0x20 + i * 0x10, 1, &val);
> > > > +               if (ret)
> > > > +                       goto error;
> > > > +
> > > > +               mx28_fec_pdata[i].mac[0] = 0x00;
> > > > +               mx28_fec_pdata[i].mac[1] = 0x04;
> > > > +               mx28_fec_pdata[i].mac[2] = (val >> 24) & 0xff;
> > > > +               mx28_fec_pdata[i].mac[3] = (val >> 16) & 0xff;
> > > > +               mx28_fec_pdata[i].mac[4] = (val >> 8) & 0xff;
> > > > +               mx28_fec_pdata[i].mac[5] = (val >> 0) & 0xff;
> > > uuh.  Is ((val >> 24) & 0xff) supposed to be 0x9f?  If not this might
> > > have unwanted effects (practical, don't know about legal ones).
> > Yes, it is 0x9f.
> Then maybe hardcode that, or at least warn if it's not?
> 
Will hard-code it.

-- 
Regards,
Shawn


^ permalink raw reply

* Re: [PATCH 03/10] net/fec: add mac field into platform data and consolidate fec_get_mac
From: Shawn Guo @ 2010-12-29 11:58 UTC (permalink / raw)
  To: Uwe Kleine-König
  Cc: Baruch Siach, davem, gerg, eric, bryan.wu, r64343, B32542, lw,
	w.sang, s.hauer, linux-arm-kernel, netdev
In-Reply-To: <20101229103138.GT14221@pengutronix.de>

Hi Uwe,

On Wed, Dec 29, 2010 at 11:31:38AM +0100, Uwe Kleine-König wrote:
> Hello Shawn,
> 
> On Wed, Dec 29, 2010 at 06:05:21PM +0800, Shawn Guo wrote:
> > On Wed, Dec 29, 2010 at 08:53:30AM +0200, Baruch Siach wrote:
> > > Hi Shawn,
> > > In the case of dual MAC that you later add support for, the address which one 
> > > is being set? Is there a way to set both in kernel command line?
> > > 
> > The fec0 gets fec_mac and fec1 gets fec_mac + 1. The following code
> > in function fec_get_mac takes care of it.
> > 
> > 	/* Adjust MAC if using default MAC address */
> default MAC address means the address passed via cmdline?  Sounds
> confusing to me.
Will change the comment and fec_mac_default name to address
the concern.

> 
> > 	if (iap == fec_mac_default)
> > 		dev->dev_addr[ETH_ALEN-1] = fec_mac_default[ETH_ALEN-1] + fep->pdev->id;
> Can this overflow?  (I didn't check the code, so my concern might be
> completely stupid here.)
No. dev->dev_addr points to netdev_hw_addr->addr, which is a 32 bytes array.

-- 
Regards,
Shawn


^ permalink raw reply

* Re: [PATCH 03/10] net/fec: add mac field into platform data and consolidate fec_get_mac
From: Uwe Kleine-König @ 2010-12-29 11:10 UTC (permalink / raw)
  To: Shawn Guo
  Cc: Baruch Siach, davem, gerg, eric, bryan.wu, r64343, B32542, lw,
	w.sang, s.hauer, linux-arm-kernel, netdev
In-Reply-To: <20101229110828.GE19347@freescale.com>

Hi Shawn,

On Wed, Dec 29, 2010 at 07:08:29PM +0800, Shawn Guo wrote:
> On Wed, Dec 29, 2010 at 11:37:58AM +0100, Uwe Kleine-König wrote:
> > On Wed, Dec 29, 2010 at 06:30:15PM +0800, Shawn Guo wrote:
> > > > On Tue, Dec 28, 2010 at 10:55:48PM +0800, Shawn Guo wrote:
> > > [...]
> > > > > +	/*
> > > > > +	 * try to get mac address in following order:
> > > > > +	 *
> > > > > +	 * 1) kernel command line fec_mac=xx:xx:xx...
> > > > > +	 */
> > > > > +	iap = fec_mac_default;
> > > > > +
> > > > > +	/*
> > > > > +	 * 2) from flash or fuse (via platform data)
> > > > > +	 */
> > > > 
> > > > Again, how do you handle the dual MAC case?
> > > > 
> > > For the platform data case, the following patch reads both mac
> > > addresses.
> > > 
> > > [PATCH 09/10] ARM: mx28: read fec mac address from ocotp
> > > 
> > > +static int __init mx28evk_fec_get_mac(void)
> > > +{
> > > +       int i, ret;
> > > +       u32 val;
> > > +
> > > +       /*
> > > +        * OCOTP only stores the last 4 octets for each mac address,
> > > +        * so hard-coding the first two octets as Freescale OUI (00:04:9f)
> > > +        * is needed.
> > > +        */
> > > +       for (i = 0; i < 2; i++) {
> > > +               ret = mxs_read_ocotp(0x20 + i * 0x10, 1, &val);
> > > +               if (ret)
> > > +                       goto error;
> > > +
> > > +               mx28_fec_pdata[i].mac[0] = 0x00;
> > > +               mx28_fec_pdata[i].mac[1] = 0x04;
> > > +               mx28_fec_pdata[i].mac[2] = (val >> 24) & 0xff;
> > > +               mx28_fec_pdata[i].mac[3] = (val >> 16) & 0xff;
> > > +               mx28_fec_pdata[i].mac[4] = (val >> 8) & 0xff;
> > > +               mx28_fec_pdata[i].mac[5] = (val >> 0) & 0xff;
> > uuh.  Is ((val >> 24) & 0xff) supposed to be 0x9f?  If not this might
> > have unwanted effects (practical, don't know about legal ones).
> Yes, it is 0x9f.
Then maybe hardcode that, or at least warn if it's not?

Best regards
Uwe

-- 
Pengutronix e.K.                           | Uwe Kleine-König            |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |

^ permalink raw reply

* Re: [PATCH 03/10] net/fec: add mac field into platform data and consolidate fec_get_mac
From: Shawn Guo @ 2010-12-29 11:08 UTC (permalink / raw)
  To: Uwe Kleine-König
  Cc: Baruch Siach, davem, gerg, eric, bryan.wu, r64343, B32542, lw,
	w.sang, s.hauer, linux-arm-kernel, netdev
In-Reply-To: <20101229103758.GU14221@pengutronix.de>

Hi Uwe,

On Wed, Dec 29, 2010 at 11:37:58AM +0100, Uwe Kleine-König wrote:
> Hello,
> 
> On Wed, Dec 29, 2010 at 06:30:15PM +0800, Shawn Guo wrote:
> > Sorry, Baruch.  I missed two comments in the last reply.
> > 
> > On Wed, Dec 29, 2010 at 08:53:30AM +0200, Baruch Siach wrote:
> > > Hi Shawn,
> > > 
> > > Please add netdev@vger.kernel.org to the Cc of all your fec driver patches.
> > > 
> > I was aware of this after I sent out the patch set last night.
> > Can I just resend all the patches in v2 and get netdev@vger.kernel.org
> > included?
> [I just added netdev for this reply.  I think adding it for v2 is OK
> then.  For those interested, the whole series can be found at
> 
> 	http://thread.gmane.org/gmane.linux.ports.arm.kernel/100902
> 
> .]
>  
Thanks.

> > > On Tue, Dec 28, 2010 at 10:55:48PM +0800, Shawn Guo wrote:
> > [...]
> > > > +	/*
> > > > +	 * try to get mac address in following order:
> > > > +	 *
> > > > +	 * 1) kernel command line fec_mac=xx:xx:xx...
> > > > +	 */
> > > > +	iap = fec_mac_default;
> > > > +
> > > > +	/*
> > > > +	 * 2) from flash or fuse (via platform data)
> > > > +	 */
> > > 
> > > Again, how do you handle the dual MAC case?
> > > 
> > For the platform data case, the following patch reads both mac
> > addresses.
> > 
> > [PATCH 09/10] ARM: mx28: read fec mac address from ocotp
> > 
> > +static int __init mx28evk_fec_get_mac(void)
> > +{
> > +       int i, ret;
> > +       u32 val;
> > +
> > +       /*
> > +        * OCOTP only stores the last 4 octets for each mac address,
> > +        * so hard-coding the first two octets as Freescale OUI (00:04:9f)
> > +        * is needed.
> > +        */
> > +       for (i = 0; i < 2; i++) {
> > +               ret = mxs_read_ocotp(0x20 + i * 0x10, 1, &val);
> > +               if (ret)
> > +                       goto error;
> > +
> > +               mx28_fec_pdata[i].mac[0] = 0x00;
> > +               mx28_fec_pdata[i].mac[1] = 0x04;
> > +               mx28_fec_pdata[i].mac[2] = (val >> 24) & 0xff;
> > +               mx28_fec_pdata[i].mac[3] = (val >> 16) & 0xff;
> > +               mx28_fec_pdata[i].mac[4] = (val >> 8) & 0xff;
> > +               mx28_fec_pdata[i].mac[5] = (val >> 0) & 0xff;
> uuh.  Is ((val >> 24) & 0xff) supposed to be 0x9f?  If not this might
> have unwanted effects (practical, don't know about legal ones).
Yes, it is 0x9f.

-- 
Regards,
Shawn


^ permalink raw reply

* Re: [PATCH 03/10] net/fec: add mac field into platform data and consolidate fec_get_mac
From: Uwe Kleine-König @ 2010-12-29 10:37 UTC (permalink / raw)
  To: Shawn Guo
  Cc: Baruch Siach, davem, gerg, eric, bryan.wu, r64343, B32542, lw,
	w.sang, s.hauer, linux-arm-kernel, netdev
In-Reply-To: <20101229103014.GD19347@freescale.com>

Hello,

On Wed, Dec 29, 2010 at 06:30:15PM +0800, Shawn Guo wrote:
> Sorry, Baruch.  I missed two comments in the last reply.
> 
> On Wed, Dec 29, 2010 at 08:53:30AM +0200, Baruch Siach wrote:
> > Hi Shawn,
> > 
> > Please add netdev@vger.kernel.org to the Cc of all your fec driver patches.
> > 
> I was aware of this after I sent out the patch set last night.
> Can I just resend all the patches in v2 and get netdev@vger.kernel.org
> included?
[I just added netdev for this reply.  I think adding it for v2 is OK
then.  For those interested, the whole series can be found at

	http://thread.gmane.org/gmane.linux.ports.arm.kernel/100902

.]
 
> > On Tue, Dec 28, 2010 at 10:55:48PM +0800, Shawn Guo wrote:
> [...]
> > > +	/*
> > > +	 * try to get mac address in following order:
> > > +	 *
> > > +	 * 1) kernel command line fec_mac=xx:xx:xx...
> > > +	 */
> > > +	iap = fec_mac_default;
> > > +
> > > +	/*
> > > +	 * 2) from flash or fuse (via platform data)
> > > +	 */
> > 
> > Again, how do you handle the dual MAC case?
> > 
> For the platform data case, the following patch reads both mac
> addresses.
> 
> [PATCH 09/10] ARM: mx28: read fec mac address from ocotp
> 
> +static int __init mx28evk_fec_get_mac(void)
> +{
> +       int i, ret;
> +       u32 val;
> +
> +       /*
> +        * OCOTP only stores the last 4 octets for each mac address,
> +        * so hard-coding the first two octets as Freescale OUI (00:04:9f)
> +        * is needed.
> +        */
> +       for (i = 0; i < 2; i++) {
> +               ret = mxs_read_ocotp(0x20 + i * 0x10, 1, &val);
> +               if (ret)
> +                       goto error;
> +
> +               mx28_fec_pdata[i].mac[0] = 0x00;
> +               mx28_fec_pdata[i].mac[1] = 0x04;
> +               mx28_fec_pdata[i].mac[2] = (val >> 24) & 0xff;
> +               mx28_fec_pdata[i].mac[3] = (val >> 16) & 0xff;
> +               mx28_fec_pdata[i].mac[4] = (val >> 8) & 0xff;
> +               mx28_fec_pdata[i].mac[5] = (val >> 0) & 0xff;
uuh.  Is ((val >> 24) & 0xff) supposed to be 0x9f?  If not this might
have unwanted effects (practical, don't know about legal ones).

Best regards
Uwe

-- 
Pengutronix e.K.                           | Uwe Kleine-König            |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |

^ permalink raw reply

* Re: 2.6.37-rc7: Regression: b43: crashes in hwrng_register()
From: Maciej Rutecki @ 2010-12-29 10:30 UTC (permalink / raw)
  To: Mario 'BitKoenig' Holbe, netdev; +Cc: linux-kernel
In-Reply-To: <slrnihjpnh.7t4.Mario.Holbe@darkside.dyn.samba-tng.org>

(CC added)

I created a Bugzilla entry at 
https://bugzilla.kernel.org/show_bug.cgi?id=25812
for your bug report, please add your address to the CC list in there, thanks!


On wtorek, 28 grudnia 2010 o 14:32:29 Mario 'BitKoenig' Holbe wrote:
> Hello,
> 
> on 2.6.37-rc7 the b43 driver crashes in hwrng_register(). This makes the
> system virtually unusable since it appears to block networking syscalls.
> This leads to, for example, ifconfig never return.
> This issue does also exist in 2.6.37-rc5.
> This issue does not exist in 2.6.36.2.
> 
> The hardware in question is:
> 02:00.0 Network controller [0280]: Broadcom Corporation BCM4312 802.11b/g
> LP-PHY [14e4:4315] (rev 01) on a Lenovo Ideapad S12 with VIA Nano.
> 
> dmesg excerpt:
> [    2.056847] b43-pci-bridge 0000:02:00.0: PCI INT A -> GSI 28 (level,
> low) -> IRQ 28 [    2.056864] b43-pci-bridge 0000:02:00.0: setting latency
> timer to 64 ...
> [    8.643695] b43-phy0: Broadcom 4312 WLAN found (core revision 15)
> [    9.047514] ieee80211 phy0: Selected rate control algorithm
> 'minstrel_ht' [    9.048441] Registered led device: b43-phy0::tx
> [    9.048479] Registered led device: b43-phy0::rx
> [    9.048518] Registered led device: b43-phy0::radio
> [    9.048542] Broadcom 43xx driver loaded [ Features: PMLS, Firmware-ID:
> FW13 ] ...
> [   24.312100] b43-phy0: Loading firmware version 410.2160 (2007-05-26
> 15:32:10) ...
> [   29.848400] b43-pci-bridge 0000:02:00.0: PCI: Disallowing DAC for device
> [   29.848407] b43-phy0: DMA mask fallback from 64-bit to 32-bit
> [   29.868632] BUG: unable to handle kernel paging request at 907cde0c
> [   29.868640] IP: [<f8d543cc>] hwrng_register+0x4c/0x139 [rng_core]
> [   29.868655] *pde = 00000000
> [   29.868659] Oops: 0000 [#1] SMP
> [   29.868664] last sysfs file: /sys/bus/pci/drivers/parport_pc/uevent
> [   29.868670] Modules linked in: parport_pc ppdev lp parport sbs sbshc
> power_meter pci_slot hed fan container acpi_cpufreq mperf
> cpufreq_conservative cpufreq_userspace cpufreq_stats cpufreq_powersave
> dm_crypt fuse loop eeprom via_cputemp i2c_dev nvram padlock_aes aes_i586
> aes_generic padlock_sha sha256_generic sha1_generic via_rng msr cpuid
> snd_hda_codec_realtek snd_hda_intel snd_hda_codec arc4 snd_hwdep ecb
> snd_pcm_oss snd_mixer_oss snd_pcm snd_seq_midi b43 snd_rawmidi uvcvideo
> snd_seq_midi_event joydev videodev btusb snd_seq rng_core video ac battery
> tpm_tis v4l1_compat tpm tpm_bios output power_supply i2c_viapro snd_timer
> ideapad_laptop snd_seq_device serio_raw wmi mac80211 cfg80211 processor
> snd pcspkr i2c_core psmouse button bluetooth evdev shpchp soundcore
> snd_page_alloc rfkill pci_hotplug ext3 jbd mbcache raid10 raid456
> async_raid6_recov async_pq raid6_pq async_xor xor async_memcpy async_tx
> raid1 raid0 multipath linear md_mod dm_mirror dm_region_hash dm_log dm_mod
> btrfs zli b_deflate crc32c libcrc32c sd_mod crc_t10dif ata_generic
> uhci_hcd pata_via libata ssb ehci_hcd tg3 scsi_mod usbcore pcmcia
> via_sdmmc mmc_core pcmcia_core libphy thermal thermal_sys nls_base [last
> unloaded: scsi_wait_scan] [   29.868810]
> [   29.868816] Pid: 1781, comm: NetworkManager Not tainted 2.6.37-rc7-686
> #1 MoutCook/20021,2959 [   29.868822] EIP: 0060:[<f8d543cc>] EFLAGS:
> 00010286 CPU: 0
> [   29.868829] EIP is at hwrng_register+0x4c/0x139 [rng_core]
> [   29.868834] EAX: 00000001 EBX: f4b17010 ECX: f6e5db6c EDX: f4b17035
> [   29.868839] ESI: 907cddf0 EDI: 00000000 EBP: 00000036 ESP: f6e5db54
> [   29.868844]  DS: 007b ES: 007b FS: 00d8 GS: 00e0 SS: 0068
> [   29.868850] Process NetworkManager (pid: 1781, ti=f6e5c000 task=f6eb6080
> task.ti=f6e5c000) [   29.868854] Stack:
> [   29.868856]  f4b16fc0 f4b17035 f8e5a870 f4b17035 0000001f f8e70095
> f8e6f9ca f4b71e70 [   29.868866]  0000000f f6c95000 f6c95000 f6e97400
> f4b162c0 f4b10240 f4b16fc8 f8e5ad67 [   29.868875]  f89e43da f4b162c0
> f6cab400 f8b80e44 f6cab000 f8b70889 f8b6fe7a 00000000 [   29.868884] Call
> Trace:
> [   29.868909]  [<f8e5a870>] ? b43_wireless_core_init+0xd0c/0xdd6 [b43]
> [   29.868925]  [<f8e5ad67>] ? b43_op_start+0xf8/0x142 [b43]
> [   29.868947]  [<f89e43da>] ? cfg80211_netdev_notifier_call+0x342/0x355
> [cfg80211] [   29.868984]  [<f8b70889>] ? ieee80211_do_open+0xed/0x45f
> [mac80211] [   29.869002]  [<f8b6fe7a>] ?
> ieee80211_check_concurrent_iface+0x1c/0x135 [mac80211] [   29.869015] 
> [<c11edcba>] ? __dev_open+0x7d/0xa7
> [   29.869022]  [<c11ec683>] ? __dev_change_flags+0x9a/0x10d
> [   29.869028]  [<c11edc12>] ? dev_change_flags+0x10/0x3b
> [   29.869036]  [<c11f7c77>] ? do_setlink+0x23e/0x532
> [   29.869044]  [<c11f803b>] ? rtnl_setlink+0xd0/0xe1
> [   29.869058]  [<c1145b00>] ? __strncpy_from_user+0x1d/0x2b
> [   29.869064]  [<c11f7f6b>] ? rtnl_setlink+0x0/0xe1
> [   29.869069]  [<c11f77a2>] ? rtnetlink_rcv_msg+0x186/0x19c
> [   29.869075]  [<c11f761c>] ? rtnetlink_rcv_msg+0x0/0x19c
> [   29.869082]  [<c1206818>] ? netlink_rcv_skb+0x2d/0x72
> [   29.869088]  [<c11f7616>] ? rtnetlink_rcv+0x18/0x1e
> [   29.869093]  [<c120666c>] ? netlink_unicast+0xba/0x10e
> [   29.869099]  [<c1207170>] ? netlink_sendmsg+0x23d/0x256
> [   29.869111]  [<c11dfe26>] ? __sock_sendmsg+0x48/0x4e
> [   29.869117]  [<c11e008f>] ? sock_sendmsg+0x78/0x8f
> [   29.869123]  [<c11e008f>] ? sock_sendmsg+0x78/0x8f
> [   29.869131]  [<c10c6785>] ? d_kill+0x38/0x3d
> [   29.869141]  [<c11e7f0c>] ? verify_iovec+0x3d/0x79
> [   29.869147]  [<c11e088d>] ? sys_sendmsg+0x15f/0x1c1
> [   29.869153]  [<c11e04c4>] ? sockfd_lookup_light+0x13/0x3f
> [   29.869160]  [<c11e0b25>] ? sys_sendto+0xfd/0x121
> [   29.869166]  [<c11e43eb>] ? sk_prot_alloc+0x62/0xd6
> [   29.869174]  [<c1001e6e>] ? __switch_to+0x6f/0xe2
> [   29.869183]  [<c12860de>] ? schedule+0x579/0x5b6
> [   29.869190]  [<c11e0723>] ? sys_recvmsg+0x3c/0x47
> [   29.869196]  [<c11e1afd>] ? sys_socketcall+0x17f/0x1cb
> [   29.869202]  [<c1002f9f>] ? sysenter_do_call+0x12/0x28
> [   29.869206] Code: f8 e8 46 25 53 c8 8b 35 ec 45 d5 f8 eb 1a 8b 13 8b 06
> e8 17 11 3f c8 85 c0 75 0a be ef ff ff ff e9 d3 00 00 00 8b 76 1c 83 ee 1c
> <8b> 46 1c 0f 18 00 90 81 fe d0 45 d5 f8 75 d4 83 3d ec 47 d5 f8 [  
> 29.869249] EIP: [<f8d543cc>] hwrng_register+0x4c/0x139 [rng_core] SS:ESP
> 0068:f6e5db54 [   29.869259] CR2: 00000000907cde0c
> [   29.869264] ---[ end trace 6719399ed79e8cc1 ]---
> 
> 
> regards
>    Mario

-- 
Maciej Rutecki
http://www.maciek.unixy.pl

^ permalink raw reply

* Re: Bypass flow control problems
From: Pasi Kärkkäinen @ 2010-12-29  8:49 UTC (permalink / raw)
  To: Alkis Georgopoulos; +Cc: netdev
In-Reply-To: <1293612435.1813.9.camel@alkis>

On Wed, Dec 29, 2010 at 10:47:15AM +0200, Alkis Georgopoulos wrote:
> ???????? 27-12-2010, ?????????? ??????, ?????? ?????? 15:43 +0200, ??/?? Pasi Kärkkäinen
> ????????????:
> > You could set up QoS rules on the server to limit the network speed per client..
> 
> That sounds promising. I'll try to limit the rate of packages that the
> server sends to each client to 90 Mbps and see if that works around the
> flow control problem. Any tips for a good method to do that?
> 

I guess you need to read the Linux Advanced Routing and Traffic Control howto..
it has examples how to use the Linux QoS stuff.

There are also some scripts out there that make it easier to set it up.

-- Pasi


^ permalink raw reply

* Re: dm9000 patch
From: Angelo Dureghello @ 2010-12-29  8:48 UTC (permalink / raw)
  To: Baruch Siach; +Cc: netdev, linux-kernel
In-Reply-To: <20101229060637.GA31010@jasper.tkos.co.il>

On 29/12/2010 07:06, Baruch Siach wrote:
> Hi Angelo,
>
> On Tue, Dec 28, 2010 at 10:52:42PM +0100, Angelo Dureghello wrote:
>    
>> sorry to contact you directly but i couldn't get any help from the
>> kernel.org mailing list, since i am not a developer my mails are
>> generally skipped.
>>      
> The best way to get the contact info for a piece of kernel code, is using the
> get_maintainer.pl script. Running 'scripts/get_maintainer.pl -f
> drivers/net/dm9000.c' gives the following output:
>
> netdev@vger.kernel.org
> linux-kernel@vger.kernel.org
>
> I added both to Cc.
>
>    
>> I am very near to have a custom board working with MCF5307 cpu and dm9000.
>> I am using kernel 2.6.36-rc3 with your last patch about
>> spinlock-recursion already included.
>>      
> You should try to update to the latest .36 kernel, which is currently
> 2.6.36.2. The problem that you experience might be unrelated to the dm9000
> driver (or to networking at all), so it might have been fixed in this version.
>
>    
>> I have "ping" and "telnet" to the embedded board fully working.
>> If i try to get a sample web page with some images from the board
>> httpd with a browser, in 80% of cases i get a trap/oops:
>>      
> Try to enable KALLSYMS in your kernel .config to make your stack trace more
> meaningful. This is under 'General setup ->  Configure standard kernel features
> (for small systems) ->  Load all symbols for debugging/ksymoops'.
>
> I hope this helps.
>
> baruch
>
>    
>> [    4.590000] eth0: link up, 100Mbps, full-duplex, lpa 0x45E1
>> [   67.630000] BUG: spinlock recursion on CPU#0, httpd/29
>> [   67.630000]  lock: 00c42c06, .magic: dead4ead, .owner: httpd/29,
>> .owner_cpu: 0
>> [   67.630000] Stack from 00d7b914:
>> [   67.630000]         00d7b940 000a8cf0 0015f693 00c42c06 dead4ead
>> 00dec1d4 0000001d 00000000
>> [   67.630000]         00c42c06 00006188 00c42800 00d7b974 000a8ec2
>> 00c42c06 0015f6f9 00002704
>> [   67.630000]         00000000 0000001f 00146fa4 00152f0c 00c42b60
>> 00006188 00c42800 0002b312
>> [   67.630000]         00d7b984 0014701e 00c42c06 00000000 00d7b9c4
>> 000df21c 00c42c06 00000000
>> [   67.630000]         00000000 0000001f 00146fa4 00152f0c 000005ea
>> 00cfc640 00006188 000096e8
>> [   67.630000]         0002b312 00146fa4 00c42b60 00002704 00d7b9ec
>> 00029d3a 0000001f 00c42800
>> [   67.630000] Call Trace:
>> [   67.630000]  [000a8cf0]  [000a8ec2]  [0014701e]  [000df21c]  [00029d3a]
>> [   67.630000]  [00029e84]  [00000bb6]  [0000336e]  [000df162]  [000effd6]
>> [   67.630000]  [00100482]  [000f312e]  [000f9ebc]  [0010dd2a]  [0010e4a0]
>> [   67.630000]  [0010dfb2]  [0010ef80]  [0011fed6]  [00121170]  [0012188e]
>> [   67.630000]  [0011ecc6]  [001249fe]  [000e4084]  [0011621c]  [00131a44]
>> [   67.630000]  [000e11ee]  [00041944]  [00041a1c]  [00041e46]  [00003218]
>> [   67.630000] BUG: spinlock lockup on CPU#0, httpd/29, 00c42c06
>> [   67.630000] Stack from 00d7b934:
>> [   67.630000]         00d7b974 000a8f66 0015f703 00000000 00dec1d4
>> 0000001d 00c42c06 00002704
>> [   67.630000]         00000000 0000001f 00146fa4 00152f0c 00c42b60
>> 00006188 00c42800 0002b312
>> [   67.630000]         00d7b984 0014701e 00c42c06 00000000 00d7b9c4
>> 000df21c 00c42c06 00000000
>> [   67.630000]         00000000 0000001f 00146fa4 00152f0c 000005ea
>> 00cfc640 00006188 000096e8
>> [   67.630000]         0002b312 00146fa4 00c42b60 00002704 00d7b9ec
>> 00029d3a 0000001f 00c42800
>> [   67.630000]         0016c1b4 00cfc640 0000001f 0016c178 00029d10
>> 00146fb8 00d7ba20 00029e84
>> [   67.630000] Call Trace:
>> [   67.630000]  [000a8f66]  [0014701e]  [000df21c]  [00029d3a]  [00029e84]
>> [   67.630000]  [00000bb6]  [0000336e]  [000df162]  [000effd6]  [00100482]
>> [   67.630000]  [000f312e]  [000f9ebc]  [0010dd2a]  [0010e4a0]  [0010dfb2]
>> [   67.630000]  [0010ef80]  [0011fed6]  [00121170]  [0012188e]  [0011ecc6]
>> [   67.630000]  [001249fe]  [000e4084]  [0011621c]  [00131a44]  [000e11ee]
>> [   67.630000]  [00041944]  [00041a1c]  [00041e46]  [00003218]
>>
>> As i said, i was hoping in your patch but i sadly discovered it is
>> already included in this kernel version.
>> Hope you can give me some help or can forward me to an appropriate
>> mailing list.
>>      
>    
Hi Baruch and all,
many thanks for the help,

i am going to try 2.6.36.2 and KALLSYMS.
Anyway, my purpose now is to learn as much as possible the interrupt 
handling and the spinlock part, and as much as possible about linux 
drivers, since i am very interested to enter on this and be able to fix 
issues and contribute.

regards,
angelo

^ permalink raw reply

* Re: Bypass flow control problems
From: Pasi Kärkkäinen @ 2010-12-29  8:47 UTC (permalink / raw)
  To: Kirsher, Jeffrey T; +Cc: Alkis Georgopoulos, netdev@vger.kernel.org
In-Reply-To: <5BBF316A-A162-4683-B6CA-6662F221F500@intel.com>

On Tue, Dec 28, 2010 at 09:05:42PM -0800, Kirsher, Jeffrey T wrote:
> On Dec 27, 2010, at 8:43, Pasi Kärkkäinen <pasik@iki.fi> wrote:
> 
> > On Wed, Dec 22, 2010 at 07:51:31PM +0200, Alkis Georgopoulos wrote:
> >> Hi,
> >> 
> >> I'm an IT teacher/LTSP developer. In LTSP, thin clients are netbooted
> >> from a server and receive a lot of X and remote disk traffic from it.
> >> 
> >> Many installations have a gigabit NIC on the server, an unmanaged
> >> gigabit switch, and 100 Mbps NICs on the clients.
> >> 
> >> With flow control on, the server is limited to sending 100 Mbps to all
> >> the clients. So with 10 thin clients the server can concurrently send
> >> only 10 Mbps to each one of them.
> >> 
> >> On NICs that support it, we turn flow control off and the server can
> >> properly send 100 Mbps to each client, i.e. 1 Gbps to 10 clients.
> >> 
> >> * Is there any way to bypass that problem on NICs that do not support
> >>   turning off flow control, like e.g. realteks?
> >>   I.e. when a client sends a pause signal to the server, instead of the
> >>   server pausing, to continue sending data to another client?
> >>   Or even to limit the amound of data the server sends to each client,
> >>   so that the clients never have to send pause signals?
> >> 
> > 
> > You could set up QoS rules on the server to limit the network speed per client..
> > 
> >> * I really don't understand why flow control is enabled by default on
> >>   NICs and switches. In which case does it help? As far as I
> >>   understand, all it does is ruin gigabit => 100 Mbps connections...
> >> 
> >> * As a side note, since rtl8169 is a very common chipset, is there a
> >>   way to disable flow control for that specific NIC?
> >> 
> >> This problem affects thousands of LTSP installations, we'd much
> >> appreciate your knowledge and feedback on it.
> >> 
> > 
> > Did you try disabling flow control from the switch? 
> 
> He stated the switch was a un-managed switch, so he would be unable to disable flow control on the switch.
> 

Oh, I missed that :) Never mind then.

-- Pasi


^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox