#include <linux/pagemap.h>
 #include <linux/init.h>
 #include <linux/string.h>
+#include <linux/smp_lock.h>
 
 #include <linux/nfs.h>
 #include <linux/nfsd_idmap.h>
 #include <linux/sunrpc/svc.h>
+#include <linux/sunrpc/svcsock.h>
 #include <linux/nfsd/nfsd.h>
 #include <linux/nfsd/cache.h>
 #include <linux/nfsd/xdr.h>
        NFSD_Fh,
        NFSD_Threads,
        NFSD_Versions,
+       NFSD_Ports,
        /*
         * The below MUST come last.  Otherwise we leave a hole in nfsd_files[]
         * with !CONFIG_NFSD_V4 and simple_fill_super() goes oops
 static ssize_t write_filehandle(struct file *file, char *buf, size_t size);
 static ssize_t write_threads(struct file *file, char *buf, size_t size);
 static ssize_t write_versions(struct file *file, char *buf, size_t size);
+static ssize_t write_ports(struct file *file, char *buf, size_t size);
 #ifdef CONFIG_NFSD_V4
 static ssize_t write_leasetime(struct file *file, char *buf, size_t size);
 static ssize_t write_recoverydir(struct file *file, char *buf, size_t size);
        [NFSD_Fh] = write_filehandle,
        [NFSD_Threads] = write_threads,
        [NFSD_Versions] = write_versions,
+       [NFSD_Ports] = write_ports,
 #ifdef CONFIG_NFSD_V4
        [NFSD_Leasetime] = write_leasetime,
        [NFSD_RecoveryDir] = write_recoverydir,
        return len;
 }
 
+static ssize_t write_ports(struct file *file, char *buf, size_t size)
+{
+       /* for now, ignore what was written and just
+        * return known ports
+        * AF proto address port
+        */
+       int len = 0;
+       lock_kernel();
+       if (nfsd_serv)
+               len = svc_sock_names(buf, nfsd_serv);
+       unlock_kernel();
+       return len;
+}
+
 #ifdef CONFIG_NFSD_V4
 extern time_t nfs4_leasetime(void);
 
                [NFSD_Fh] = {"filehandle", &transaction_ops, S_IWUSR|S_IRUSR},
                [NFSD_Threads] = {"threads", &transaction_ops, S_IWUSR|S_IRUSR},
                [NFSD_Versions] = {"versions", &transaction_ops, S_IWUSR|S_IRUSR},
+               [NFSD_Ports] = {"portlist", &transaction_ops, S_IWUSR|S_IRUGO},
 #ifdef CONFIG_NFSD_V4
                [NFSD_Leasetime] = {"nfsv4leasetime", &transaction_ops, S_IWUSR|S_IRUSR},
                [NFSD_RecoveryDir] = {"nfsv4recoverydir", &transaction_ops, S_IWUSR|S_IRUSR},
 
        return len;
 }
 
+/*
+ * Report socket names for nfsdfs
+ */
+static int one_sock_name(char *buf, struct svc_sock *svsk)
+{
+       int len;
+
+       switch(svsk->sk_sk->sk_family) {
+       case AF_INET:
+               len = sprintf(buf, "ipv4 %s %u.%u.%u.%u %d\n",
+                             svsk->sk_sk->sk_protocol==IPPROTO_UDP?
+                             "udp" : "tcp",
+                             NIPQUAD(inet_sk(svsk->sk_sk)->rcv_saddr),
+                             inet_sk(svsk->sk_sk)->num);
+               break;
+       default:
+               len = sprintf(buf, "*unknown-%d*\n",
+                              svsk->sk_sk->sk_family);
+       }
+       return len;
+}
+
+int
+svc_sock_names(char *buf, struct svc_serv *serv)
+{
+       struct svc_sock *svsk;
+       int len = 0;
+
+       if (!serv)
+               return 0;
+       spin_lock(&serv->sv_lock);
+       list_for_each_entry(svsk, &serv->sv_permsocks, sk_list) {
+               int onelen = one_sock_name(buf+len, svsk);
+               len += onelen;
+       }
+       spin_unlock(&serv->sv_lock);
+       return len;
+}
+EXPORT_SYMBOL(svc_sock_names);
+
 /*
  * Check input queue length
  */