Viewing: gnilnd_proc.c
// SPDX-License-Identifier: GPL-2.0
/* Copyright (C) 2009-2012, 2016 Cray, Inc.
*
* Copyright (c) 2013, 2015, Intel Corporation.
*/
/* This file is part of Lustre, http://www.lustre.org.
*
* Author: Nic Henke <nic@cray.com>
* Author: James Shimek <jshimek@cray.com>
*/
/* this code liberated and modified from lnet/lnet/router_proc.c */
#define DEBUG_SUBSYSTEM S_LND
#include "gnilnd.h"
#include <linux/seq_file.h>
#include <lprocfs_status.h>
#define GNILND_PROC_STATS "stats"
#define GNILND_PROC_MDD "mdd"
#define GNILND_PROC_SMSG "smsg"
#define GNILND_PROC_CONN "conn"
#define GNILND_PROC_PEER_CONNS "peer_conns"
#define GNILND_PROC_PEER "peer"
#define GNILND_PROC_CKSUM_TEST "cksum_test"
static int
_kgnilnd_proc_run_cksum_test(int caseno, int nloops, int nob)
{
struct bio_vec *src, *dest;
struct timespec begin, end, diff;
struct iov_iter to;
int niov;
int rc = 0;
int i = 0, j = 0, n;
__u16 cksum, cksum2;
__u64 mbytes;
CFS_ALLOC_PTR_ARRAY(src, GNILND_MAX_IOV);
CFS_ALLOC_PTR_ARRAY(dest, GNILND_MAX_IOV);
if (src == NULL || dest == NULL) {
CERROR("couldn't allocate iovs\n");
GOTO(unwind, rc = -ENOMEM);
}
for (i = 0; i < GNILND_MAX_IOV; i++) {
src[i].bv_offset = 0;
src[i].bv_len = PAGE_SIZE;
src[i].bv_page = alloc_page(GFP_KERNEL | __GFP_ZERO);
if (src[i].bv_page == NULL) {
CERROR("couldn't allocate page %d\n", i);
GOTO(unwind, rc = -ENOMEM);
}
dest[i].bv_offset = 0;
dest[i].bv_len = PAGE_SIZE;
dest[i].bv_page = alloc_page(GFP_KERNEL | __GFP_ZERO);
if (dest[i].bv_page == NULL) {
CERROR("couldn't allocate page %d\n", i);
GOTO(unwind, rc = -ENOMEM);
}
}
iov_iter_bvec(&to, WRITE, dest, GNILND_MAX_IOV,
GNILND_MAX_IOV * PAGE_SIZE);
/* add extra 2 pages - one for offset of src, 2nd to allow dest offset */
niov = (nob / PAGE_SIZE) + 2;
if (niov > GNILND_MAX_IOV) {
CERROR("bytes %d too large, requires niov %d > %d\n",
nob, niov, GNILND_MAX_IOV);
GOTO(unwind, rc = -E2BIG);
}
/* setup real data */
src[0].bv_offset = 317;
dest[0].bv_offset = 592;
switch (caseno) {
default:
/* odd -> even */
break;
case 1:
/* odd -> odd */
dest[0].bv_offset -= 1;
break;
case 2:
/* even -> even */
src[0].bv_offset += 1;
break;
case 3:
/* even -> odd */
src[0].bv_offset += 1;
dest[0].bv_offset -= 1;
}
src[0].bv_len = PAGE_SIZE - src[0].bv_offset;
dest[0].bv_len = PAGE_SIZE - dest[0].bv_offset;
iov_iter_advance(&to, dest[0].bv_offset);
for (i = 0; i < niov; i++) {
memset(page_address(src[i].bv_page) + src[i].bv_offset,
0xf0 + i, src[i].bv_len);
}
lnet_copy_kiov2iter(&to, GNILND_MAX_IOV, src, 0, nob);
getnstimeofday(&begin);
for (n = 0; n < nloops; n++) {
CDEBUG(D_BUFFS,
"case %d loop %d src %d dest %d nob %d niov %d\n",
caseno, n, src[0].bv_offset, dest[0].bv_offset, nob,
niov);
cksum = kgnilnd_cksum_kiov(niov, src, 0, nob - (n % nob), 1);
cksum2 = kgnilnd_cksum_kiov(niov, dest, 0, nob - (n % nob), 1);
if (cksum != cksum2) {
CERROR("case %d loop %d different checksums %x expected %x\n",
j, n, cksum2, cksum);
GOTO(unwind, rc = -ENOKEY);
}
}
getnstimeofday(&end);
mbytes = ((__u64)nloops * nob * 2) / (1024*1024);
diff = kgnilnd_ts_sub(end, begin);
LCONSOLE_INFO("running %lldMB took %ld.%ld seconds\n",
mbytes, diff.tv_sec, diff.tv_nsec);
unwind:
CDEBUG(D_NET, "freeing %d pages\n", i);
for (i -= 1; i >= 0; i--) {
if (src[i].bv_page)
__free_page(src[i].bv_page);
if (dest[i].bv_page)
__free_page(dest[i].bv_page);
}
CFS_FREE_PTR_ARRAY(src, GNILND_MAX_IOV);
CFS_FREE_PTR_ARRAY(dest, GNILND_MAX_IOV);
return rc;
}
static ssize_t
kgnilnd_proc_cksum_test_write(struct file *file, const char __user *ubuffer,
size_t count, loff_t *ppos)
{
char dummy[256 + 1] = { '\0' };
int testno, nloops, nbytes;
int rc;
ENTRY;
if (kgnilnd_data.kgn_init < GNILND_INIT_ALL) {
CERROR("can't run cksum test, kgnilnd is not initialized yet\n");
RETURN(-ENOSYS);
}
if (count >= sizeof(dummy) || count == 0)
RETURN(-EINVAL);
if (copy_from_user(dummy, ubuffer, count))
RETURN(-EFAULT);
if (sscanf(dummy, "%d:%d:%d", &testno, &nloops, &nbytes) == 3) {
rc = _kgnilnd_proc_run_cksum_test(testno, nloops, nbytes);
if (rc < 0) {
RETURN(rc);
} else {
/* spurious, but lets us know the parse was ok */
RETURN(count);
}
}
RETURN(count);
}
static int
kgnilnd_cksum_test_seq_open(struct inode *inode, struct file *file)
{
return single_open(file, NULL, pde_data(inode));
}
static const struct file_operations kgn_cksum_test_fops = {
.owner = THIS_MODULE,
.open = kgnilnd_cksum_test_seq_open,
.write = kgnilnd_proc_cksum_test_write,
.llseek = seq_lseek,
.release = seq_release,
};
static int
kgnilnd_stats_seq_show(struct seq_file *sf, void *v)
{
kgn_device_t *dev;
struct timespec64 now;
if (kgnilnd_data.kgn_init < GNILND_INIT_ALL) {
seq_printf(sf, "kgnilnd is not initialized yet\n");
return 0;
}
/* only do the first device */
dev = &kgnilnd_data.kgn_devices[0];
/* sampling is racy, but so is reading this file! */
smp_rmb();
ktime_get_ts64(&now);
seq_printf(sf, "time: %llu.%lu\n"
"ntx: %d\n"
"npeers: %d\n"
"nconns: %d\n"
"nEPs: %d\n"
"ndgrams: %d\n"
"nfmablk: %d\n"
"n_mdd: %d\n"
"n_mdd_held: %d\n"
"n_eager_allocs: %d\n"
"GART map bytes: %lld\n"
"TX queued maps: %d\n"
"TX phys nmaps: %d\n"
"TX phys bytes: %lu\n"
"RDMAQ bytes_auth: %lld\n"
"RDMAQ bytes_left: %lld\n"
"RDMAQ nstalls: %d\n"
"dev mutex delay: %ld\n"
"dev n_yield: %d\n"
"dev n_schedule: %d\n"
"SMSG fast_try: %d\n"
"SMSG fast_ok: %d\n"
"SMSG fast_block: %d\n"
"SMSG ntx: %u\n"
"SMSG tx_bytes: %llu\n"
"SMSG nrx: %u\n"
"SMSG rx_bytes: %llu\n"
"RDMA ntx: %u\n"
"RDMA tx_bytes: %llu\n"
"RDMA nrx: %u\n"
"RDMA rx_bytes: %llu\n"
"VMAP short: %d\n"
"VMAP cksum: %d\n"
"KMAP short: %d\n"
"RDMA REV length: %d\n"
"RDMA REV offset: %d\n"
"RDMA REV copy: %d\n",
(s64)now.tv_sec, now.tv_nsec,
atomic_read(&kgnilnd_data.kgn_ntx),
atomic_read(&kgnilnd_data.kgn_npeers),
atomic_read(&kgnilnd_data.kgn_nconns),
atomic_read(&dev->gnd_neps),
atomic_read(&dev->gnd_ndgrams),
atomic_read(&dev->gnd_nfmablk),
atomic_read(&dev->gnd_n_mdd), atomic_read(&dev->gnd_n_mdd_held),
atomic_read(&kgnilnd_data.kgn_neager_allocs),
(s64)atomic64_read(&dev->gnd_nbytes_map),
atomic_read(&dev->gnd_nq_map),
dev->gnd_map_nphys, dev->gnd_map_physnop * PAGE_SIZE,
(s64)atomic64_read(&dev->gnd_rdmaq_bytes_out),
(s64)atomic64_read(&dev->gnd_rdmaq_bytes_ok),
atomic_read(&dev->gnd_rdmaq_nstalls),
dev->gnd_mutex_delay,
atomic_read(&dev->gnd_n_yield),
atomic_read(&dev->gnd_n_schedule),
atomic_read(&dev->gnd_fast_try),
atomic_read(&dev->gnd_fast_ok),
atomic_read(&dev->gnd_fast_block),
atomic_read(&dev->gnd_short_ntx),
(s64)atomic64_read(&dev->gnd_short_txbytes),
atomic_read(&dev->gnd_short_nrx),
(s64)atomic64_read(&dev->gnd_short_rxbytes),
atomic_read(&dev->gnd_rdma_ntx),
(s64)atomic64_read(&dev->gnd_rdma_txbytes),
atomic_read(&dev->gnd_rdma_nrx),
(s64)atomic64_read(&dev->gnd_rdma_rxbytes),
atomic_read(&kgnilnd_data.kgn_nvmap_short),
atomic_read(&kgnilnd_data.kgn_nvmap_cksum),
atomic_read(&kgnilnd_data.kgn_nkmap_short),
atomic_read(&kgnilnd_data.kgn_rev_length),
atomic_read(&kgnilnd_data.kgn_rev_offset),
atomic_read(&kgnilnd_data.kgn_rev_copy_buff));
return 0;
}
static ssize_t
kgnilnd_proc_stats_write(struct file *file, const char __user *ubuffer,
size_t count, loff_t *ppos)
{
kgn_device_t *dev;
if (kgnilnd_data.kgn_init < GNILND_INIT_ALL) {
CERROR("kgnilnd is not initialized for stats write\n");
return -EINVAL;
}
/* only do the first device */
dev = &kgnilnd_data.kgn_devices[0];
atomic_set(&dev->gnd_short_ntx, 0);
atomic_set(&dev->gnd_short_nrx, 0);
atomic64_set(&dev->gnd_short_txbytes, 0);
atomic64_set(&dev->gnd_short_rxbytes, 0);
atomic_set(&dev->gnd_rdma_ntx, 0);
atomic_set(&dev->gnd_rdma_nrx, 0);
atomic_set(&dev->gnd_fast_ok, 0);
atomic_set(&dev->gnd_fast_try, 0);
atomic_set(&dev->gnd_fast_block, 0);
atomic64_set(&dev->gnd_rdma_txbytes, 0);
atomic64_set(&dev->gnd_rdma_rxbytes, 0);
atomic_set(&dev->gnd_rdmaq_nstalls, 0);
set_mb(dev->gnd_mutex_delay, 0);
atomic_set(&dev->gnd_n_yield, 0);
atomic_set(&dev->gnd_n_schedule, 0);
atomic_set(&kgnilnd_data.kgn_nvmap_short, 0);
atomic_set(&kgnilnd_data.kgn_nvmap_cksum, 0);
atomic_set(&kgnilnd_data.kgn_nkmap_short, 0);
/* sampling is racy, but so is writing this file! */
smp_wmb();
return count;
}
static int
kgnilnd_stats_seq_open(struct inode *inode, struct file *file)
{
return single_open(file, kgnilnd_stats_seq_show, pde_data(inode));
}
static const struct file_operations kgn_stats_fops = {
.owner = THIS_MODULE,
.open = kgnilnd_stats_seq_open,
.read = seq_read,
.write = kgnilnd_proc_stats_write,
.llseek = seq_lseek,
.release = seq_release,
};
typedef struct {
kgn_device_t *gmdd_dev;
kgn_tx_t *gmdd_tx;
loff_t gmdd_off;
} kgn_mdd_seq_iter_t;
int
kgnilnd_mdd_seq_seek(kgn_mdd_seq_iter_t *gseq, loff_t off)
{
kgn_tx_t *tx;
struct list_head *r;
loff_t here;
int rc = 0;
if (off == 0) {
gseq->gmdd_tx = NULL;
gseq->gmdd_off = 0;
return 0;
}
tx = gseq->gmdd_tx;
if (tx == NULL || gseq->gmdd_off > off) {
/* search from start */
r = gseq->gmdd_dev->gnd_map_list.next;
here = 1;
} else {
/* continue current search */
r = &tx->tx_map_list;
here = gseq->gmdd_off;
}
gseq->gmdd_off = off;
while (r != &gseq->gmdd_dev->gnd_map_list) {
kgn_tx_t *t;
t = list_entry(r, kgn_tx_t, tx_map_list);
if (here == off) {
gseq->gmdd_tx = t;
rc = 0;
goto out;
}
r = r->next;
here++;
}
gseq->gmdd_tx = NULL;
rc = -ENOENT;
out:
return rc;
}
static void *
kgnilnd_mdd_seq_start(struct seq_file *s, loff_t *pos)
{
kgn_mdd_seq_iter_t *gseq;
int rc;
if (kgnilnd_data.kgn_init < GNILND_INIT_ALL) {
return NULL;
}
LIBCFS_ALLOC(gseq, sizeof(*gseq));
if (gseq == NULL) {
CERROR("could not allocate mdd sequence iterator\n");
return NULL;
}
/* only doing device 0 for now */
gseq->gmdd_dev = &kgnilnd_data.kgn_devices[0];
gseq->gmdd_tx = NULL;
/* need to lock map while we poke - huge disturbance
* but without it, no way to get the data printed */
spin_lock(&gseq->gmdd_dev->gnd_map_lock);
/* set private to gseq for stop */
s->private = gseq;
rc = kgnilnd_mdd_seq_seek(gseq, *pos);
if (rc == 0)
return gseq;
else
return NULL;
}
static void
kgnilnd_mdd_seq_stop(struct seq_file *s, void *iter)
{
kgn_mdd_seq_iter_t *gseq = s->private;
if (gseq != NULL) {
spin_unlock(&gseq->gmdd_dev->gnd_map_lock);
LIBCFS_FREE(gseq, sizeof(*gseq));
}
}
static void *
kgnilnd_mdd_seq_next(struct seq_file *s, void *iter, loff_t *pos)
{
kgn_mdd_seq_iter_t *gseq = iter;
int rc;
loff_t next = *pos + 1;
rc = kgnilnd_mdd_seq_seek(gseq, next);
*pos = next;
if (rc != 0) {
return NULL;
}
return gseq;
}
static int
kgnilnd_mdd_seq_show(struct seq_file *s, void *iter)
{
kgn_mdd_seq_iter_t *gseq = iter;
kgn_tx_t *tx;
__u64 nob;
__u32 physnop;
int id;
int buftype;
gni_mem_handle_t hndl;
if (gseq->gmdd_off == 0) {
seq_printf(s, "%s %22s %16s %8s %8s %37s\n",
"tx", "tx_id", "nob", "physnop",
"buftype", "mem handle");
return 0;
}
tx = gseq->gmdd_tx;
LASSERT(tx != NULL);
id = tx->tx_id.txe_smsg_id;
nob = tx->tx_nob;
physnop = tx->tx_phys_npages;
buftype = tx->tx_buftype;
hndl.qword1 = tx->tx_map_key.qword1;
hndl.qword2 = tx->tx_map_key.qword2;
seq_printf(s, "%pK %x %16llu %8d %#8x %#llx.%#llxx\n",
tx, id, nob, physnop, buftype,
hndl.qword1, hndl.qword2);
return 0;
}
static const struct seq_operations kgn_mdd_sops = {
.start = kgnilnd_mdd_seq_start,
.stop = kgnilnd_mdd_seq_stop,
.next = kgnilnd_mdd_seq_next,
.show = kgnilnd_mdd_seq_show,
};
static int
kgnilnd_mdd_seq_open(struct inode *inode, struct file *file)
{
struct seq_file *sf;
int rc;
rc = seq_open(file, &kgn_mdd_sops);
if (rc == 0) {
sf = file->private_data;
/* NULL means we've not yet open() */
sf->private = NULL;
}
return rc;
}
static const struct file_operations kgn_mdd_fops = {
.owner = THIS_MODULE,
.open = kgnilnd_mdd_seq_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
};
typedef struct {
__u64 gsmsg_version;
kgn_device_t *gsmsg_dev;
kgn_fma_memblock_t *gsmsg_fmablk;
loff_t gsmsg_off;
} kgn_smsg_seq_iter_t;
int
kgnilnd_smsg_seq_seek(kgn_smsg_seq_iter_t *gseq, loff_t off)
{
kgn_fma_memblock_t *fmablk;
kgn_device_t *dev;
struct list_head *r;
loff_t here;
int rc = 0;
/* offset 0 is the header, so we start real entries at
* here == off == 1 */
if (off == 0) {
gseq->gsmsg_fmablk = NULL;
gseq->gsmsg_off = 0;
return 0;
}
fmablk = gseq->gsmsg_fmablk;
dev = gseq->gsmsg_dev;
spin_lock(&dev->gnd_fmablk_lock);
if (fmablk != NULL &&
gseq->gsmsg_version != atomic_read(&dev->gnd_fmablk_vers)) {
/* list changed */
rc = -ESTALE;
goto out;
}
if (fmablk == NULL || gseq->gsmsg_off > off) {
/* search from start */
r = dev->gnd_fma_buffs.next;
here = 1;
} else {
/* continue current search */
r = &fmablk->gnm_bufflist;
here = gseq->gsmsg_off;
}
gseq->gsmsg_version = atomic_read(&dev->gnd_fmablk_vers);
gseq->gsmsg_off = off;
while (r != &dev->gnd_fma_buffs) {
kgn_fma_memblock_t *t;
t = list_entry(r, kgn_fma_memblock_t, gnm_bufflist);
if (here == off) {
gseq->gsmsg_fmablk = t;
rc = 0;
goto out;
}
r = r->next;
here++;
}
gseq->gsmsg_fmablk = NULL;
rc = -ENOENT;
out:
spin_unlock(&dev->gnd_fmablk_lock);
return rc;
}
static void *
kgnilnd_smsg_seq_start(struct seq_file *s, loff_t *pos)
{
kgn_smsg_seq_iter_t *gseq;
int rc;
if (kgnilnd_data.kgn_init < GNILND_INIT_ALL) {
return NULL;
}
LIBCFS_ALLOC(gseq, sizeof(*gseq));
if (gseq == NULL) {
CERROR("could not allocate smsg sequence iterator\n");
return NULL;
}
/* only doing device 0 for now */
gseq->gsmsg_dev = &kgnilnd_data.kgn_devices[0];
gseq->gsmsg_fmablk = NULL;
rc = kgnilnd_smsg_seq_seek(gseq, *pos);
if (rc == 0)
return gseq;
LIBCFS_FREE(gseq, sizeof(*gseq));
return NULL;
}
static void
kgnilnd_smsg_seq_stop(struct seq_file *s, void *iter)
{
kgn_smsg_seq_iter_t *gseq = iter;
LIBCFS_FREE(gseq, sizeof(*gseq));
}
static void *
kgnilnd_smsg_seq_next(struct seq_file *s, void *iter, loff_t *pos)
{
kgn_smsg_seq_iter_t *gseq = iter;
int rc;
loff_t next = *pos + 1;
rc = kgnilnd_smsg_seq_seek(gseq, next);
*pos = next;
if (rc != 0) {
LIBCFS_FREE(gseq, sizeof(*gseq));
return NULL;
}
return gseq;
}
static int
kgnilnd_smsg_seq_show(struct seq_file *s, void *iter)
{
kgn_smsg_seq_iter_t *gseq = iter;
kgn_fma_memblock_t *fmablk;
kgn_device_t *dev;
int avail_mboxs, held_mboxs, num_mboxs;
unsigned int blk_size;
int live;
kgn_fmablk_state_t state;
gni_mem_handle_t hndl;
if (gseq->gsmsg_off == 0) {
seq_printf(s, "%5s %4s %6s/%5s/%5s %9s %18s %37s\n",
"blk#", "type", "avail", "held", "total", "size",
"fmablk", "mem handle");
return 0;
}
fmablk = gseq->gsmsg_fmablk;
dev = gseq->gsmsg_dev;
LASSERT(fmablk != NULL);
spin_lock(&dev->gnd_fmablk_lock);
if (gseq->gsmsg_version != atomic_read(&dev->gnd_fmablk_vers)) {
/* list changed */
spin_unlock(&dev->gnd_fmablk_lock);
return -ESTALE;
}
live = fmablk->gnm_hold_timeout == 0;
/* none are available if it isn't live... */
avail_mboxs = live ? fmablk->gnm_avail_mboxs : 0;
held_mboxs = fmablk->gnm_held_mboxs;
num_mboxs = fmablk->gnm_num_mboxs;
blk_size = fmablk->gnm_blk_size;
state = fmablk->gnm_state;
hndl.qword1 = fmablk->gnm_hndl.qword1;
hndl.qword2 = fmablk->gnm_hndl.qword2;
spin_unlock(&dev->gnd_fmablk_lock);
if (live) {
seq_printf(s, "%5d %4s %6d/%5d/%5d %9d %18p %#llx.%#llx\n",
(int) gseq->gsmsg_off, kgnilnd_fmablk_state2str(state),
avail_mboxs, held_mboxs, num_mboxs, blk_size,
fmablk, hndl.qword1, hndl.qword2);
} else {
seq_printf(s, "%5d %4s %6d/%5d/%5d %9d %18p %37s\n",
(int) gseq->gsmsg_off, kgnilnd_fmablk_state2str(state),
avail_mboxs, held_mboxs, num_mboxs, blk_size,
fmablk, "PURGATORY.HOLD");
}
return 0;
}
static const struct seq_operations kgn_smsg_sops = {
.start = kgnilnd_smsg_seq_start,
.stop = kgnilnd_smsg_seq_stop,
.next = kgnilnd_smsg_seq_next,
.show = kgnilnd_smsg_seq_show,
};
static int
kgnilnd_smsg_seq_open(struct inode *inode, struct file *file)
{
struct seq_file *sf;
int rc;
rc = seq_open(file, &kgn_smsg_sops);
if (rc == 0) {
sf = file->private_data;
sf->private = pde_data(inode);
}
return rc;
}
static const struct file_operations kgn_smsg_fops = {
.owner = THIS_MODULE,
.open = kgnilnd_smsg_seq_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
};
typedef struct {
__u64 gconn_version;
struct list_head *gconn_list;
kgn_conn_t *gconn_conn;
loff_t gconn_off;
int gconn_hashidx;
} kgn_conn_seq_iter_t;
int
kgnilnd_conn_seq_seek(kgn_conn_seq_iter_t *gseq, loff_t off)
{
struct list_head *list;
kgn_conn_t *conn;
loff_t here = 0;
int rc = 0;
if (off == 0) {
gseq->gconn_hashidx = 0;
gseq->gconn_list = NULL;
}
if (off > atomic_read(&kgnilnd_data.kgn_nconns)) {
gseq->gconn_list = NULL;
rc = -ENOENT;
}
read_lock(&kgnilnd_data.kgn_peer_conn_lock);
if (gseq->gconn_list != NULL &&
gseq->gconn_version != kgnilnd_data.kgn_conn_version) {
/* list changed */
rc = -ESTALE;
goto out;
}
if ((gseq->gconn_list == NULL) ||
(gseq->gconn_off > off) ||
(gseq->gconn_hashidx >= *kgnilnd_tunables.kgn_peer_hash_size)) {
/* search from start */
gseq->gconn_hashidx = 0;
list = &kgnilnd_data.kgn_conns[gseq->gconn_hashidx];
here = 0;
} else {
/* continue current search */
list = gseq->gconn_list;
}
gseq->gconn_version = kgnilnd_data.kgn_conn_version;
gseq->gconn_off = off;
start_list:
list_for_each_entry(conn, list, gnc_hashlist) {
if (here == off) {
gseq->gconn_conn = conn;
rc = 0;
goto out;
}
here++;
}
/* if we got through this hash bucket with 'off' still to go, try next*/
gseq->gconn_hashidx++;
if ((here <= off) &&
(gseq->gconn_hashidx < *kgnilnd_tunables.kgn_peer_hash_size)) {
list = &kgnilnd_data.kgn_conns[gseq->gconn_hashidx];
goto start_list;
}
gseq->gconn_list = NULL;
rc = -ENOENT;
out:
read_unlock(&kgnilnd_data.kgn_peer_conn_lock);
return rc;
}
static void *
kgnilnd_conn_seq_start(struct seq_file *s, loff_t *pos)
{
kgn_conn_seq_iter_t *gseq;
int rc;
if (kgnilnd_data.kgn_init < GNILND_INIT_ALL) {
return NULL;
}
LIBCFS_ALLOC(gseq, sizeof(*gseq));
if (gseq == NULL) {
CERROR("could not allocate conn sequence iterator\n");
return NULL;
}
/* only doing device 0 for now */
gseq->gconn_list = NULL;
rc = kgnilnd_conn_seq_seek(gseq, *pos);
if (rc == 0)
return gseq;
LIBCFS_FREE(gseq, sizeof(*gseq));
return NULL;
}
static void
kgnilnd_conn_seq_stop(struct seq_file *s, void *iter)
{
kgn_conn_seq_iter_t *gseq = iter;
LIBCFS_FREE(gseq, sizeof(*gseq));
}
static void *
kgnilnd_conn_seq_next(struct seq_file *s, void *iter, loff_t *pos)
{
kgn_conn_seq_iter_t *gseq = iter;
int rc;
loff_t next = *pos + 1;
rc = kgnilnd_conn_seq_seek(gseq, next);
*pos = next;
if (rc != 0) {
LIBCFS_FREE(gseq, sizeof(*gseq));
return NULL;
}
return gseq;
}
static int
kgnilnd_conn_seq_show(struct seq_file *s, void *iter)
{
kgn_conn_seq_iter_t *gseq = iter;
kgn_peer_t *peer = NULL;
kgn_conn_t *conn;
/* there is no header data for conns, so offset 0 is the first
* real entry. */
conn = gseq->gconn_conn;
LASSERT(conn != NULL);
read_lock(&kgnilnd_data.kgn_peer_conn_lock);
if (gseq->gconn_list != NULL &&
gseq->gconn_version != kgnilnd_data.kgn_conn_version) {
/* list changed */
read_unlock(&kgnilnd_data.kgn_peer_conn_lock);
return -ESTALE;
}
/* instead of saving off the data, just refcount */
kgnilnd_conn_addref(conn);
if (conn->gnc_peer) {
/* don't use link - after unlock it could get nuked */
peer = conn->gnc_peer;
kgnilnd_peer_addref(peer);
}
read_unlock(&kgnilnd_data.kgn_peer_conn_lock);
seq_printf(s, "%pK->%s [%d] q %d/%d/%d "
"tx sq %u %dms/%dms "
"rx sq %u %dms/%dms "
"noop r/s %d/%d w/s/cq %lds/%lds/%lds "
"sched a/d %lds/%lds "
"tx_re %lld TO %ds %s\n",
conn, peer ? libcfs_nid2str(peer->gnp_nid) : "<?>",
atomic_read(&conn->gnc_refcount),
kgnilnd_count_list(&conn->gnc_fmaq),
atomic_read(&conn->gnc_nlive_fma),
atomic_read(&conn->gnc_nlive_rdma),
atomic_read(&conn->gnc_tx_seq),
jiffies_to_msecs(jiffies - conn->gnc_last_tx),
jiffies_to_msecs(jiffies - conn->gnc_last_tx_cq),
atomic_read(&conn->gnc_rx_seq),
jiffies_to_msecs(jiffies - conn->gnc_last_rx),
jiffies_to_msecs(jiffies - conn->gnc_last_rx_cq),
atomic_read(&conn->gnc_reaper_noop),
atomic_read(&conn->gnc_sched_noop),
cfs_duration_sec(jiffies - conn->gnc_last_noop_want),
cfs_duration_sec(jiffies - conn->gnc_last_noop_sent),
cfs_duration_sec(jiffies - conn->gnc_last_noop_cq),
cfs_duration_sec(jiffies - conn->gnc_last_sched_ask),
cfs_duration_sec(jiffies - conn->gnc_last_sched_do),
conn->gnc_tx_retrans, conn->gnc_timeout,
kgnilnd_conn_state2str(conn));
if (peer)
kgnilnd_peer_decref(peer);
kgnilnd_conn_decref(conn);
return 0;
}
static const struct seq_operations kgn_conn_sops = {
.start = kgnilnd_conn_seq_start,
.stop = kgnilnd_conn_seq_stop,
.next = kgnilnd_conn_seq_next,
.show = kgnilnd_conn_seq_show,
};
#define KGN_DEBUG_PEER_NID_DEFAULT -1
static int kgnilnd_debug_peer_nid = KGN_DEBUG_PEER_NID_DEFAULT;
static ssize_t
kgnilnd_proc_peer_conns_write(struct file *file, const char __user *ubuffer,
size_t count, loff_t *ppos)
{
char dummy[8];
int rc;
if (count >= sizeof(dummy) || count == 0)
return -EINVAL;
if (copy_from_user(dummy, ubuffer, count))
return -EFAULT;
rc = sscanf(dummy, "%d", &kgnilnd_debug_peer_nid);
if (rc != 1) {
return -EINVAL;
}
RETURN(count);
}
/* debug data to print from conns associated with peer nid
* - date/time
* - peer nid
* - mbox_addr (msg_buffer + mbox_offset)
* - gnc_dgram_type
* - gnc_in_purgatory
* - gnc_state
* - gnc_error
* - gnc_peer_error
* - gnc_tx_seq
* - gnc_last_tx
* - gnc_last_tx_cq
* - gnc_rx_seq
* - gnc_first_rx
* - gnc_last_rx
* - gnc_last_rx_cq
* - gnc_tx_retrans
* - gnc_close_sent
* - gnc_close_recvd
*/
static int
kgnilnd_proc_peer_conns_seq_show(struct seq_file *sf, void *v)
{
kgn_peer_t *peer;
kgn_conn_t *conn;
struct tm ctm;
struct timespec now;
unsigned long jifs;
if (kgnilnd_debug_peer_nid == KGN_DEBUG_PEER_NID_DEFAULT) {
seq_printf(sf, "peer_conns not initialized\n");
return 0;
}
/* sample date/time stamp - print time in UTC
* 2012-12-11T16:06:16.966751 123@gni ...
*/
getnstimeofday(&now);
time64_to_tm(now.tv_sec, 0, &ctm);
jifs = jiffies;
write_lock(&kgnilnd_data.kgn_peer_conn_lock);
peer = kgnilnd_find_peer_locked(kgnilnd_debug_peer_nid);
if (peer == NULL) {
seq_printf(sf, "peer not found for this nid %d\n",
kgnilnd_debug_peer_nid);
write_unlock(&kgnilnd_data.kgn_peer_conn_lock);
return 0;
}
list_for_each_entry(conn, &peer->gnp_conns, gnc_list) {
seq_printf(sf,
"%04ld-%02d-%02dT%02d:%02d:%02d.%06ld %s mbox adr %pK dg type %s %s purg %d close s/r %d/%d err %d peer err %d tx sq %u %dms/%dms rx sq %u %dms/%dms/%dms tx retran %lld\n",
ctm.tm_year+1900, ctm.tm_mon+1, ctm.tm_mday,
ctm.tm_hour, ctm.tm_min, ctm.tm_sec, now.tv_nsec,
libcfs_nid2str(peer->gnp_nid),
conn->remote_mbox_addr,
kgnilnd_conn_dgram_type2str(conn->gnc_dgram_type),
kgnilnd_conn_state2str(conn),
conn->gnc_in_purgatory,
conn->gnc_close_sent,
conn->gnc_close_recvd,
conn->gnc_error,
conn->gnc_peer_error,
atomic_read(&conn->gnc_tx_seq),
jiffies_to_msecs(jifs - conn->gnc_last_tx),
jiffies_to_msecs(jifs - conn->gnc_last_tx_cq),
atomic_read(&conn->gnc_rx_seq),
jiffies_to_msecs(jifs - conn->gnc_first_rx),
jiffies_to_msecs(jifs - conn->gnc_last_rx),
jiffies_to_msecs(jifs - conn->gnc_last_rx_cq),
conn->gnc_tx_retrans);
}
write_unlock(&kgnilnd_data.kgn_peer_conn_lock);
return 0;
}
static int
kgnilnd_peer_conns_seq_open(struct inode *inode, struct file *file)
{
return single_open(file, kgnilnd_proc_peer_conns_seq_show,
pde_data(inode));
}
static const struct file_operations kgn_peer_conns_fops = {
.owner = THIS_MODULE,
.open = kgnilnd_peer_conns_seq_open,
.read = seq_read,
.write = kgnilnd_proc_peer_conns_write,
.llseek = seq_lseek,
.release = seq_release,
};
static int
kgnilnd_conn_seq_open(struct inode *inode, struct file *file)
{
struct seq_file *sf;
int rc;
rc = seq_open(file, &kgn_conn_sops);
if (rc == 0) {
sf = file->private_data;
sf->private = pde_data(inode);
}
return rc;
}
static const struct file_operations kgn_conn_fops = {
.owner = THIS_MODULE,
.open = kgnilnd_conn_seq_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
};
typedef struct {
__u64 gpeer_version;
struct list_head *gpeer_list;
kgn_peer_t *gpeer_peer;
loff_t gpeer_off;
int gpeer_hashidx;
} kgn_peer_seq_iter_t;
int
kgnilnd_peer_seq_seek(kgn_peer_seq_iter_t *gseq, loff_t off)
{
struct list_head *list;
kgn_peer_t *peer;
loff_t here = 0;
int rc = 0;
if (off == 0) {
gseq->gpeer_hashidx = 0;
gseq->gpeer_list = NULL;
}
if (off > atomic_read(&kgnilnd_data.kgn_npeers)) {
gseq->gpeer_list = NULL;
rc = -ENOENT;
}
read_lock(&kgnilnd_data.kgn_peer_conn_lock);
if (gseq->gpeer_list != NULL &&
gseq->gpeer_version != kgnilnd_data.kgn_peer_version) {
/* list changed */
rc = -ESTALE;
goto out;
}
if ((gseq->gpeer_list == NULL) ||
(gseq->gpeer_off > off) ||
(gseq->gpeer_hashidx >= *kgnilnd_tunables.kgn_peer_hash_size)) {
/* search from start */
gseq->gpeer_hashidx = 0;
list = &kgnilnd_data.kgn_peers[gseq->gpeer_hashidx];
here = 0;
} else {
/* continue current search */
list = gseq->gpeer_list;
}
gseq->gpeer_version = kgnilnd_data.kgn_peer_version;
gseq->gpeer_off = off;
start_list:
list_for_each_entry(peer, list, gnp_list) {
if (here == off) {
gseq->gpeer_peer = peer;
rc = 0;
goto out;
}
here++;
}
/* if we got through this hash bucket with 'off' still to go, try next*/
gseq->gpeer_hashidx++;
if ((here <= off) &&
(gseq->gpeer_hashidx < *kgnilnd_tunables.kgn_peer_hash_size)) {
list = &kgnilnd_data.kgn_peers[gseq->gpeer_hashidx];
goto start_list;
}
gseq->gpeer_list = NULL;
rc = -ENOENT;
out:
read_unlock(&kgnilnd_data.kgn_peer_conn_lock);
return rc;
}
static void *
kgnilnd_peer_seq_start(struct seq_file *s, loff_t *pos)
{
kgn_peer_seq_iter_t *gseq;
int rc;
if (kgnilnd_data.kgn_init < GNILND_INIT_ALL) {
return NULL;
}
LIBCFS_ALLOC(gseq, sizeof(*gseq));
if (gseq == NULL) {
CERROR("could not allocate peer sequence iterator\n");
return NULL;
}
/* only doing device 0 for now */
gseq->gpeer_list = NULL;
rc = kgnilnd_peer_seq_seek(gseq, *pos);
if (rc == 0)
return gseq;
LIBCFS_FREE(gseq, sizeof(*gseq));
return NULL;
}
static void
kgnilnd_peer_seq_stop(struct seq_file *s, void *iter)
{
kgn_peer_seq_iter_t *gseq = iter;
LIBCFS_FREE(gseq, sizeof(*gseq));
}
static void *
kgnilnd_peer_seq_next(struct seq_file *s, void *iter, loff_t *pos)
{
kgn_peer_seq_iter_t *gseq = iter;
int rc;
loff_t next = *pos + 1;
rc = kgnilnd_peer_seq_seek(gseq, next);
*pos = next;
if (rc != 0) {
LIBCFS_FREE(gseq, sizeof(*gseq));
return NULL;
}
return gseq;
}
static int
kgnilnd_peer_seq_show(struct seq_file *s, void *iter)
{
kgn_peer_seq_iter_t *gseq = iter;
kgn_peer_t *peer;
kgn_conn_t *conn;
char conn_str;
int purg_count = 0;
/* there is no header data for peers, so offset 0 is the first
* real entry. */
peer = gseq->gpeer_peer;
LASSERT(peer != NULL);
read_lock(&kgnilnd_data.kgn_peer_conn_lock);
if (gseq->gpeer_list != NULL &&
gseq->gpeer_version != kgnilnd_data.kgn_peer_version) {
/* list changed */
read_unlock(&kgnilnd_data.kgn_peer_conn_lock);
return -ESTALE;
}
/* instead of saving off the data, just refcount */
kgnilnd_peer_addref(peer);
conn = kgnilnd_find_conn_locked(peer);
if (peer->gnp_connecting) {
conn_str = 'S';
} else if (conn != NULL) {
conn_str = 'C';
} else {
conn_str = 'D';
}
list_for_each_entry(conn, &peer->gnp_conns, gnc_list) {
if (conn->gnc_in_purgatory) {
purg_count++;
}
}
read_unlock(&kgnilnd_data.kgn_peer_conn_lock);
seq_printf(s, "%pK->%s [%d] %s NIC 0x%x q %d conn %c purg %d last %d@%lldms dgram %d@%dms reconn %dms to %lus\n",
peer, libcfs_nid2str(peer->gnp_nid),
atomic_read(&peer->gnp_refcount),
(peer->gnp_state == GNILND_PEER_DOWN) ? "down" :
peer->gnp_state == GNILND_PEER_TIMED_OUT ? "timedout" : "up",
peer->gnp_host_id,
kgnilnd_count_list(&peer->gnp_tx_queue),
conn_str,
purg_count,
peer->gnp_last_errno,
(ktime_get_seconds() - peer->gnp_last_alive) * MSEC_PER_SEC,
peer->gnp_last_dgram_errno,
jiffies_to_msecs(jiffies - peer->gnp_last_dgram_time),
peer->gnp_reconnect_interval != 0
? jiffies_to_msecs(jiffies - peer->gnp_reconnect_time)
: 0,
peer->gnp_reconnect_interval);
kgnilnd_peer_decref(peer);
return 0;
}
static const struct seq_operations kgn_peer_sops = {
.start = kgnilnd_peer_seq_start,
.stop = kgnilnd_peer_seq_stop,
.next = kgnilnd_peer_seq_next,
.show = kgnilnd_peer_seq_show,
};
static int
kgnilnd_peer_seq_open(struct inode *inode, struct file *file)
{
struct seq_file *sf;
int rc;
rc = seq_open(file, &kgn_peer_sops);
if (rc == 0) {
sf = file->private_data;
sf->private = pde_data(inode);
}
return rc;
}
static const struct file_operations kgn_peer_fops = {
.owner = THIS_MODULE,
.open = kgnilnd_peer_seq_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
};
static struct proc_dir_entry *kgn_proc_root;
void
kgnilnd_proc_init(void)
{
struct proc_dir_entry *pde;
int rc = 0;
ENTRY;
/* setup dir */
kgn_proc_root = proc_mkdir(libcfs_lnd2modname(GNILND), NULL);
if (kgn_proc_root == NULL) {
CERROR("couldn't create proc dir %s\n",
libcfs_lnd2modname(GNILND));
return;
}
/* Initialize CKSUM_TEST */
pde = proc_create(GNILND_PROC_CKSUM_TEST, 0200, kgn_proc_root,
&kgn_cksum_test_fops);
if (pde == NULL) {
CERROR("couldn't create proc entry %s\n",
GNILND_PROC_CKSUM_TEST);
GOTO(remove_dir, rc = -ENOENT);
}
/* Initialize STATS */
pde = proc_create(GNILND_PROC_STATS, 0644, kgn_proc_root,
&kgn_stats_fops);
if (pde == NULL) {
CERROR("couldn't create proc entry %s\n", GNILND_PROC_STATS);
GOTO(remove_test, rc = -ENOENT);
}
/* Initialize MDD */
pde = proc_create(GNILND_PROC_MDD, 0444, kgn_proc_root, &kgn_mdd_fops);
if (pde == NULL) {
CERROR("couldn't create proc entry %s\n", GNILND_PROC_MDD);
GOTO(remove_stats, rc = -ENOENT);
}
/* Initialize SMSG */
pde = proc_create(GNILND_PROC_SMSG, 0444, kgn_proc_root,
&kgn_smsg_fops);
if (pde == NULL) {
CERROR("couldn't create proc entry %s\n", GNILND_PROC_SMSG);
GOTO(remove_mdd, rc = -ENOENT);
}
/* Initialize CONN */
pde = proc_create(GNILND_PROC_CONN, 0444, kgn_proc_root,
&kgn_conn_fops);
if (pde == NULL) {
CERROR("couldn't create proc entry %s\n", GNILND_PROC_CONN);
GOTO(remove_smsg, rc = -ENOENT);
}
/* Initialize peer conns debug */
pde = proc_create(GNILND_PROC_PEER_CONNS, 0644, kgn_proc_root,
&kgn_peer_conns_fops);
if (pde == NULL) {
CERROR("couldn't create proc entry %s\n", GNILND_PROC_PEER_CONNS);
GOTO(remove_conn, rc = -ENOENT);
}
/* Initialize PEER */
pde = proc_create(GNILND_PROC_PEER, 0444, kgn_proc_root,
&kgn_peer_fops);
if (pde == NULL) {
CERROR("couldn't create proc entry %s\n", GNILND_PROC_PEER);
GOTO(remove_pc, rc = -ENOENT);
}
RETURN_EXIT;
remove_pc:
remove_proc_entry(GNILND_PROC_PEER_CONNS, kgn_proc_root);
remove_conn:
remove_proc_entry(GNILND_PROC_CONN, kgn_proc_root);
remove_smsg:
remove_proc_entry(GNILND_PROC_SMSG, kgn_proc_root);
remove_mdd:
remove_proc_entry(GNILND_PROC_MDD, kgn_proc_root);
remove_stats:
remove_proc_entry(GNILND_PROC_STATS, kgn_proc_root);
remove_test:
remove_proc_entry(GNILND_PROC_CKSUM_TEST, kgn_proc_root);
remove_dir:
remove_proc_entry(libcfs_lnd2modname(GNILND), NULL);
RETURN_EXIT;
}
void
kgnilnd_proc_fini(void)
{
remove_proc_entry(GNILND_PROC_PEER_CONNS, kgn_proc_root);
remove_proc_entry(GNILND_PROC_PEER, kgn_proc_root);
remove_proc_entry(GNILND_PROC_CONN, kgn_proc_root);
remove_proc_entry(GNILND_PROC_MDD, kgn_proc_root);
remove_proc_entry(GNILND_PROC_SMSG, kgn_proc_root);
remove_proc_entry(GNILND_PROC_STATS, kgn_proc_root);
remove_proc_entry(GNILND_PROC_CKSUM_TEST, kgn_proc_root);
remove_proc_entry(libcfs_lnd2modname(GNILND), NULL);
}