| /* |
| * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. |
| * |
| * Copyright (c) 2011, 2012, Intel Corporation. |
| * |
| * This file is part of Portals |
| * http://sourceforge.net/projects/sandiaportals/ |
| * |
| * Portals is free software; you can redistribute it and/or |
| * modify it under the terms of version 2 of the GNU General Public |
| * License as published by the Free Software Foundation. |
| * |
| * Portals is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with Portals; if not, write to the Free Software |
| * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
| * |
| */ |
| |
| #define DEBUG_SUBSYSTEM S_LNET |
| #include "../../include/linux/libcfs/libcfs.h" |
| #include "../../include/linux/lnet/lib-lnet.h" |
| |
| #if defined(LNET_ROUTER) |
| |
| /* This is really lnet_proc.c. You might need to update sanity test 215 |
| * if any file format is changed. */ |
| |
| static struct ctl_table_header *lnet_table_header; |
| |
| #define CTL_LNET (0x100) |
| enum { |
| PSDEV_LNET_STATS = 100, |
| PSDEV_LNET_ROUTES, |
| PSDEV_LNET_ROUTERS, |
| PSDEV_LNET_PEERS, |
| PSDEV_LNET_BUFFERS, |
| PSDEV_LNET_NIS, |
| PSDEV_LNET_PTL_ROTOR, |
| }; |
| |
| #define LNET_LOFFT_BITS (sizeof(loff_t) * 8) |
| /* |
| * NB: max allowed LNET_CPT_BITS is 8 on 64-bit system and 2 on 32-bit system |
| */ |
| #define LNET_PROC_CPT_BITS (LNET_CPT_BITS + 1) |
| /* change version, 16 bits or 8 bits */ |
| #define LNET_PROC_VER_BITS MAX(((MIN(LNET_LOFFT_BITS, 64)) / 4), 8) |
| |
| #define LNET_PROC_HASH_BITS LNET_PEER_HASH_BITS |
| /* |
| * bits for peer hash offset |
| * NB: we don't use the highest bit of *ppos because it's signed |
| */ |
| #define LNET_PROC_HOFF_BITS (LNET_LOFFT_BITS - \ |
| LNET_PROC_CPT_BITS - \ |
| LNET_PROC_VER_BITS - \ |
| LNET_PROC_HASH_BITS - 1) |
| /* bits for hash index + position */ |
| #define LNET_PROC_HPOS_BITS (LNET_PROC_HASH_BITS + LNET_PROC_HOFF_BITS) |
| /* bits for peer hash table + hash version */ |
| #define LNET_PROC_VPOS_BITS (LNET_PROC_HPOS_BITS + LNET_PROC_VER_BITS) |
| |
| #define LNET_PROC_CPT_MASK ((1ULL << LNET_PROC_CPT_BITS) - 1) |
| #define LNET_PROC_VER_MASK ((1ULL << LNET_PROC_VER_BITS) - 1) |
| #define LNET_PROC_HASH_MASK ((1ULL << LNET_PROC_HASH_BITS) - 1) |
| #define LNET_PROC_HOFF_MASK ((1ULL << LNET_PROC_HOFF_BITS) - 1) |
| |
| #define LNET_PROC_CPT_GET(pos) \ |
| (int)(((pos) >> LNET_PROC_VPOS_BITS) & LNET_PROC_CPT_MASK) |
| |
| #define LNET_PROC_VER_GET(pos) \ |
| (int)(((pos) >> LNET_PROC_HPOS_BITS) & LNET_PROC_VER_MASK) |
| |
| #define LNET_PROC_HASH_GET(pos) \ |
| (int)(((pos) >> LNET_PROC_HOFF_BITS) & LNET_PROC_HASH_MASK) |
| |
| #define LNET_PROC_HOFF_GET(pos) \ |
| (int)((pos) & LNET_PROC_HOFF_MASK) |
| |
| #define LNET_PROC_POS_MAKE(cpt, ver, hash, off) \ |
| (((((loff_t)(cpt)) & LNET_PROC_CPT_MASK) << LNET_PROC_VPOS_BITS) | \ |
| ((((loff_t)(ver)) & LNET_PROC_VER_MASK) << LNET_PROC_HPOS_BITS) | \ |
| ((((loff_t)(hash)) & LNET_PROC_HASH_MASK) << LNET_PROC_HOFF_BITS) | \ |
| ((off) & LNET_PROC_HOFF_MASK)) |
| |
| #define LNET_PROC_VERSION(v) ((unsigned int)((v) & LNET_PROC_VER_MASK)) |
| |
| static int proc_call_handler(void *data, int write, loff_t *ppos, |
| void __user *buffer, size_t *lenp, |
| int (*handler)(void *data, int write, |
| loff_t pos, void __user *buffer, int len)) |
| { |
| int rc = handler(data, write, *ppos, buffer, *lenp); |
| |
| if (rc < 0) |
| return rc; |
| |
| if (write) { |
| *ppos += *lenp; |
| } else { |
| *lenp = rc; |
| *ppos += rc; |
| } |
| return 0; |
| } |
| |
| static int __proc_lnet_stats(void *data, int write, |
| loff_t pos, void __user *buffer, int nob) |
| { |
| int rc; |
| lnet_counters_t *ctrs; |
| int len; |
| char *tmpstr; |
| const int tmpsiz = 256; /* 7 %u and 4 %llu */ |
| |
| if (write) { |
| lnet_counters_reset(); |
| return 0; |
| } |
| |
| /* read */ |
| |
| LIBCFS_ALLOC(ctrs, sizeof(*ctrs)); |
| if (ctrs == NULL) |
| return -ENOMEM; |
| |
| LIBCFS_ALLOC(tmpstr, tmpsiz); |
| if (tmpstr == NULL) { |
| LIBCFS_FREE(ctrs, sizeof(*ctrs)); |
| return -ENOMEM; |
| } |
| |
| lnet_counters_get(ctrs); |
| |
| len = snprintf(tmpstr, tmpsiz, |
| "%u %u %u %u %u %u %u %llu %llu %llu %llu", |
| ctrs->msgs_alloc, ctrs->msgs_max, |
| ctrs->errors, |
| ctrs->send_count, ctrs->recv_count, |
| ctrs->route_count, ctrs->drop_count, |
| ctrs->send_length, ctrs->recv_length, |
| ctrs->route_length, ctrs->drop_length); |
| |
| if (pos >= min_t(int, len, strlen(tmpstr))) |
| rc = 0; |
| else |
| rc = cfs_trace_copyout_string(buffer, nob, |
| tmpstr + pos, "\n"); |
| |
| LIBCFS_FREE(tmpstr, tmpsiz); |
| LIBCFS_FREE(ctrs, sizeof(*ctrs)); |
| return rc; |
| } |
| |
| static int proc_lnet_stats(struct ctl_table *table, int write, |
| void __user *buffer, size_t *lenp, loff_t *ppos) |
| { |
| return proc_call_handler(table->data, write, ppos, buffer, lenp, |
| __proc_lnet_stats); |
| } |
| |
| int proc_lnet_routes(struct ctl_table *table, int write, void __user *buffer, |
| size_t *lenp, loff_t *ppos) |
| { |
| const int tmpsiz = 256; |
| char *tmpstr; |
| char *s; |
| int rc = 0; |
| int len; |
| int ver; |
| int off; |
| |
| CLASSERT(sizeof(loff_t) >= 4); |
| |
| off = LNET_PROC_HOFF_GET(*ppos); |
| ver = LNET_PROC_VER_GET(*ppos); |
| |
| LASSERT(!write); |
| |
| if (*lenp == 0) |
| return 0; |
| |
| LIBCFS_ALLOC(tmpstr, tmpsiz); |
| if (tmpstr == NULL) |
| return -ENOMEM; |
| |
| s = tmpstr; /* points to current position in tmpstr[] */ |
| |
| if (*ppos == 0) { |
| s += snprintf(s, tmpstr + tmpsiz - s, "Routing %s\n", |
| the_lnet.ln_routing ? "enabled" : "disabled"); |
| LASSERT(tmpstr + tmpsiz - s > 0); |
| |
| s += snprintf(s, tmpstr + tmpsiz - s, "%-8s %4s %8s %7s %s\n", |
| "net", "hops", "priority", "state", "router"); |
| LASSERT(tmpstr + tmpsiz - s > 0); |
| |
| lnet_net_lock(0); |
| ver = (unsigned int)the_lnet.ln_remote_nets_version; |
| lnet_net_unlock(0); |
| *ppos = LNET_PROC_POS_MAKE(0, ver, 0, off); |
| } else { |
| struct list_head *n; |
| struct list_head *r; |
| lnet_route_t *route = NULL; |
| lnet_remotenet_t *rnet = NULL; |
| int skip = off - 1; |
| struct list_head *rn_list; |
| int i; |
| |
| lnet_net_lock(0); |
| |
| if (ver != LNET_PROC_VERSION(the_lnet.ln_remote_nets_version)) { |
| lnet_net_unlock(0); |
| LIBCFS_FREE(tmpstr, tmpsiz); |
| return -ESTALE; |
| } |
| |
| for (i = 0; i < LNET_REMOTE_NETS_HASH_SIZE && route == NULL; |
| i++) { |
| rn_list = &the_lnet.ln_remote_nets_hash[i]; |
| |
| n = rn_list->next; |
| |
| while (n != rn_list && route == NULL) { |
| rnet = list_entry(n, lnet_remotenet_t, |
| lrn_list); |
| |
| r = rnet->lrn_routes.next; |
| |
| while (r != &rnet->lrn_routes) { |
| lnet_route_t *re = |
| list_entry(r, lnet_route_t, |
| lr_list); |
| if (skip == 0) { |
| route = re; |
| break; |
| } |
| |
| skip--; |
| r = r->next; |
| } |
| |
| n = n->next; |
| } |
| } |
| |
| if (route != NULL) { |
| __u32 net = rnet->lrn_net; |
| unsigned int hops = route->lr_hops; |
| unsigned int priority = route->lr_priority; |
| lnet_nid_t nid = route->lr_gateway->lp_nid; |
| int alive = route->lr_gateway->lp_alive; |
| |
| s += snprintf(s, tmpstr + tmpsiz - s, |
| "%-8s %4u %8u %7s %s\n", |
| libcfs_net2str(net), hops, |
| priority, |
| alive ? "up" : "down", |
| libcfs_nid2str(nid)); |
| LASSERT(tmpstr + tmpsiz - s > 0); |
| } |
| |
| lnet_net_unlock(0); |
| } |
| |
| len = s - tmpstr; /* how many bytes was written */ |
| |
| if (len > *lenp) { /* linux-supplied buffer is too small */ |
| rc = -EINVAL; |
| } else if (len > 0) { /* wrote something */ |
| if (copy_to_user(buffer, tmpstr, len)) |
| rc = -EFAULT; |
| else { |
| off += 1; |
| *ppos = LNET_PROC_POS_MAKE(0, ver, 0, off); |
| } |
| } |
| |
| LIBCFS_FREE(tmpstr, tmpsiz); |
| |
| if (rc == 0) |
| *lenp = len; |
| |
| return rc; |
| } |
| |
| int proc_lnet_routers(struct ctl_table *table, int write, void __user *buffer, |
| size_t *lenp, loff_t *ppos) |
| { |
| int rc = 0; |
| char *tmpstr; |
| char *s; |
| const int tmpsiz = 256; |
| int len; |
| int ver; |
| int off; |
| |
| off = LNET_PROC_HOFF_GET(*ppos); |
| ver = LNET_PROC_VER_GET(*ppos); |
| |
| LASSERT(!write); |
| |
| if (*lenp == 0) |
| return 0; |
| |
| LIBCFS_ALLOC(tmpstr, tmpsiz); |
| if (tmpstr == NULL) |
| return -ENOMEM; |
| |
| s = tmpstr; /* points to current position in tmpstr[] */ |
| |
| if (*ppos == 0) { |
| s += snprintf(s, tmpstr + tmpsiz - s, |
| "%-4s %7s %9s %6s %12s %9s %8s %7s %s\n", |
| "ref", "rtr_ref", "alive_cnt", "state", |
| "last_ping", "ping_sent", "deadline", |
| "down_ni", "router"); |
| LASSERT(tmpstr + tmpsiz - s > 0); |
| |
| lnet_net_lock(0); |
| ver = (unsigned int)the_lnet.ln_routers_version; |
| lnet_net_unlock(0); |
| *ppos = LNET_PROC_POS_MAKE(0, ver, 0, off); |
| } else { |
| struct list_head *r; |
| struct lnet_peer *peer = NULL; |
| int skip = off - 1; |
| |
| lnet_net_lock(0); |
| |
| if (ver != LNET_PROC_VERSION(the_lnet.ln_routers_version)) { |
| lnet_net_unlock(0); |
| |
| LIBCFS_FREE(tmpstr, tmpsiz); |
| return -ESTALE; |
| } |
| |
| r = the_lnet.ln_routers.next; |
| |
| while (r != &the_lnet.ln_routers) { |
| lnet_peer_t *lp = list_entry(r, lnet_peer_t, |
| lp_rtr_list); |
| |
| if (skip == 0) { |
| peer = lp; |
| break; |
| } |
| |
| skip--; |
| r = r->next; |
| } |
| |
| if (peer != NULL) { |
| lnet_nid_t nid = peer->lp_nid; |
| unsigned long now = cfs_time_current(); |
| unsigned long deadline = peer->lp_ping_deadline; |
| int nrefs = peer->lp_refcount; |
| int nrtrrefs = peer->lp_rtr_refcount; |
| int alive_cnt = peer->lp_alive_count; |
| int alive = peer->lp_alive; |
| int pingsent = !peer->lp_ping_notsent; |
| int last_ping = cfs_duration_sec(cfs_time_sub(now, |
| peer->lp_ping_timestamp)); |
| int down_ni = 0; |
| lnet_route_t *rtr; |
| |
| if ((peer->lp_ping_feats & |
| LNET_PING_FEAT_NI_STATUS) != 0) { |
| list_for_each_entry(rtr, &peer->lp_routes, |
| lr_gwlist) { |
| /* downis on any route should be the |
| * number of downis on the gateway */ |
| if (rtr->lr_downis != 0) { |
| down_ni = rtr->lr_downis; |
| break; |
| } |
| } |
| } |
| |
| if (deadline == 0) |
| s += snprintf(s, tmpstr + tmpsiz - s, |
| "%-4d %7d %9d %6s %12d %9d %8s %7d %s\n", |
| nrefs, nrtrrefs, alive_cnt, |
| alive ? "up" : "down", last_ping, |
| pingsent, "NA", down_ni, |
| libcfs_nid2str(nid)); |
| else |
| s += snprintf(s, tmpstr + tmpsiz - s, |
| "%-4d %7d %9d %6s %12d %9d %8lu %7d %s\n", |
| nrefs, nrtrrefs, alive_cnt, |
| alive ? "up" : "down", last_ping, |
| pingsent, |
| cfs_duration_sec(cfs_time_sub(deadline, now)), |
| down_ni, libcfs_nid2str(nid)); |
| LASSERT(tmpstr + tmpsiz - s > 0); |
| } |
| |
| lnet_net_unlock(0); |
| } |
| |
| len = s - tmpstr; /* how many bytes was written */ |
| |
| if (len > *lenp) { /* linux-supplied buffer is too small */ |
| rc = -EINVAL; |
| } else if (len > 0) { /* wrote something */ |
| if (copy_to_user(buffer, tmpstr, len)) |
| rc = -EFAULT; |
| else { |
| off += 1; |
| *ppos = LNET_PROC_POS_MAKE(0, ver, 0, off); |
| } |
| } |
| |
| LIBCFS_FREE(tmpstr, tmpsiz); |
| |
| if (rc == 0) |
| *lenp = len; |
| |
| return rc; |
| } |
| |
| int proc_lnet_peers(struct ctl_table *table, int write, void __user *buffer, |
| size_t *lenp, loff_t *ppos) |
| { |
| const int tmpsiz = 256; |
| struct lnet_peer_table *ptable; |
| char *tmpstr; |
| char *s; |
| int cpt = LNET_PROC_CPT_GET(*ppos); |
| int ver = LNET_PROC_VER_GET(*ppos); |
| int hash = LNET_PROC_HASH_GET(*ppos); |
| int hoff = LNET_PROC_HOFF_GET(*ppos); |
| int rc = 0; |
| int len; |
| |
| CLASSERT(LNET_PROC_HASH_BITS >= LNET_PEER_HASH_BITS); |
| LASSERT(!write); |
| |
| if (*lenp == 0) |
| return 0; |
| |
| if (cpt >= LNET_CPT_NUMBER) { |
| *lenp = 0; |
| return 0; |
| } |
| |
| LIBCFS_ALLOC(tmpstr, tmpsiz); |
| if (tmpstr == NULL) |
| return -ENOMEM; |
| |
| s = tmpstr; /* points to current position in tmpstr[] */ |
| |
| if (*ppos == 0) { |
| s += snprintf(s, tmpstr + tmpsiz - s, |
| "%-24s %4s %5s %5s %5s %5s %5s %5s %5s %s\n", |
| "nid", "refs", "state", "last", "max", |
| "rtr", "min", "tx", "min", "queue"); |
| LASSERT(tmpstr + tmpsiz - s > 0); |
| |
| hoff++; |
| } else { |
| struct lnet_peer *peer; |
| struct list_head *p; |
| int skip; |
| again: |
| p = NULL; |
| peer = NULL; |
| skip = hoff - 1; |
| |
| lnet_net_lock(cpt); |
| ptable = the_lnet.ln_peer_tables[cpt]; |
| if (hoff == 1) |
| ver = LNET_PROC_VERSION(ptable->pt_version); |
| |
| if (ver != LNET_PROC_VERSION(ptable->pt_version)) { |
| lnet_net_unlock(cpt); |
| LIBCFS_FREE(tmpstr, tmpsiz); |
| return -ESTALE; |
| } |
| |
| while (hash < LNET_PEER_HASH_SIZE) { |
| if (p == NULL) |
| p = ptable->pt_hash[hash].next; |
| |
| while (p != &ptable->pt_hash[hash]) { |
| lnet_peer_t *lp = list_entry(p, lnet_peer_t, |
| lp_hashlist); |
| if (skip == 0) { |
| peer = lp; |
| |
| /* minor optimization: start from idx+1 |
| * on next iteration if we've just |
| * drained lp_hashlist */ |
| if (lp->lp_hashlist.next == |
| &ptable->pt_hash[hash]) { |
| hoff = 1; |
| hash++; |
| } else { |
| hoff++; |
| } |
| |
| break; |
| } |
| |
| skip--; |
| p = lp->lp_hashlist.next; |
| } |
| |
| if (peer != NULL) |
| break; |
| |
| p = NULL; |
| hoff = 1; |
| hash++; |
| } |
| |
| if (peer != NULL) { |
| lnet_nid_t nid = peer->lp_nid; |
| int nrefs = peer->lp_refcount; |
| int lastalive = -1; |
| char *aliveness = "NA"; |
| int maxcr = peer->lp_ni->ni_peertxcredits; |
| int txcr = peer->lp_txcredits; |
| int mintxcr = peer->lp_mintxcredits; |
| int rtrcr = peer->lp_rtrcredits; |
| int minrtrcr = peer->lp_minrtrcredits; |
| int txqnob = peer->lp_txqnob; |
| |
| if (lnet_isrouter(peer) || |
| lnet_peer_aliveness_enabled(peer)) |
| aliveness = peer->lp_alive ? "up" : "down"; |
| |
| if (lnet_peer_aliveness_enabled(peer)) { |
| unsigned long now = cfs_time_current(); |
| long delta; |
| |
| delta = cfs_time_sub(now, peer->lp_last_alive); |
| lastalive = cfs_duration_sec(delta); |
| |
| /* No need to mess up peers contents with |
| * arbitrarily long integers - it suffices to |
| * know that lastalive is more than 10000s old |
| */ |
| if (lastalive >= 10000) |
| lastalive = 9999; |
| } |
| |
| lnet_net_unlock(cpt); |
| |
| s += snprintf(s, tmpstr + tmpsiz - s, |
| "%-24s %4d %5s %5d %5d %5d %5d %5d %5d %d\n", |
| libcfs_nid2str(nid), nrefs, aliveness, |
| lastalive, maxcr, rtrcr, minrtrcr, txcr, |
| mintxcr, txqnob); |
| LASSERT(tmpstr + tmpsiz - s > 0); |
| |
| } else { /* peer is NULL */ |
| lnet_net_unlock(cpt); |
| } |
| |
| if (hash == LNET_PEER_HASH_SIZE) { |
| cpt++; |
| hash = 0; |
| hoff = 1; |
| if (peer == NULL && cpt < LNET_CPT_NUMBER) |
| goto again; |
| } |
| } |
| |
| len = s - tmpstr; /* how many bytes was written */ |
| |
| if (len > *lenp) { /* linux-supplied buffer is too small */ |
| rc = -EINVAL; |
| } else if (len > 0) { /* wrote something */ |
| if (copy_to_user(buffer, tmpstr, len)) |
| rc = -EFAULT; |
| else |
| *ppos = LNET_PROC_POS_MAKE(cpt, ver, hash, hoff); |
| } |
| |
| LIBCFS_FREE(tmpstr, tmpsiz); |
| |
| if (rc == 0) |
| *lenp = len; |
| |
| return rc; |
| } |
| |
| static int __proc_lnet_buffers(void *data, int write, |
| loff_t pos, void __user *buffer, int nob) |
| { |
| char *s; |
| char *tmpstr; |
| int tmpsiz; |
| int idx; |
| int len; |
| int rc; |
| int i; |
| |
| LASSERT(!write); |
| |
| /* (4 %d) * 4 * LNET_CPT_NUMBER */ |
| tmpsiz = 64 * (LNET_NRBPOOLS + 1) * LNET_CPT_NUMBER; |
| LIBCFS_ALLOC(tmpstr, tmpsiz); |
| if (tmpstr == NULL) |
| return -ENOMEM; |
| |
| s = tmpstr; /* points to current position in tmpstr[] */ |
| |
| s += snprintf(s, tmpstr + tmpsiz - s, |
| "%5s %5s %7s %7s\n", |
| "pages", "count", "credits", "min"); |
| LASSERT(tmpstr + tmpsiz - s > 0); |
| |
| if (the_lnet.ln_rtrpools == NULL) |
| goto out; /* I'm not a router */ |
| |
| for (idx = 0; idx < LNET_NRBPOOLS; idx++) { |
| lnet_rtrbufpool_t *rbp; |
| |
| lnet_net_lock(LNET_LOCK_EX); |
| cfs_percpt_for_each(rbp, i, the_lnet.ln_rtrpools) { |
| s += snprintf(s, tmpstr + tmpsiz - s, |
| "%5d %5d %7d %7d\n", |
| rbp[idx].rbp_npages, |
| rbp[idx].rbp_nbuffers, |
| rbp[idx].rbp_credits, |
| rbp[idx].rbp_mincredits); |
| LASSERT(tmpstr + tmpsiz - s > 0); |
| } |
| lnet_net_unlock(LNET_LOCK_EX); |
| } |
| |
| out: |
| len = s - tmpstr; |
| |
| if (pos >= min_t(int, len, strlen(tmpstr))) |
| rc = 0; |
| else |
| rc = cfs_trace_copyout_string(buffer, nob, |
| tmpstr + pos, NULL); |
| |
| LIBCFS_FREE(tmpstr, tmpsiz); |
| return rc; |
| } |
| |
| static int proc_lnet_buffers(struct ctl_table *table, int write, |
| void __user *buffer, size_t *lenp, loff_t *ppos) |
| { |
| return proc_call_handler(table->data, write, ppos, buffer, lenp, |
| __proc_lnet_buffers); |
| } |
| |
| int proc_lnet_nis(struct ctl_table *table, int write, void __user *buffer, |
| size_t *lenp, loff_t *ppos) |
| { |
| int tmpsiz = 128 * LNET_CPT_NUMBER; |
| int rc = 0; |
| char *tmpstr; |
| char *s; |
| int len; |
| |
| LASSERT(!write); |
| |
| if (*lenp == 0) |
| return 0; |
| |
| LIBCFS_ALLOC(tmpstr, tmpsiz); |
| if (tmpstr == NULL) |
| return -ENOMEM; |
| |
| s = tmpstr; /* points to current position in tmpstr[] */ |
| |
| if (*ppos == 0) { |
| s += snprintf(s, tmpstr + tmpsiz - s, |
| "%-24s %6s %5s %4s %4s %4s %5s %5s %5s\n", |
| "nid", "status", "alive", "refs", "peer", |
| "rtr", "max", "tx", "min"); |
| LASSERT(tmpstr + tmpsiz - s > 0); |
| } else { |
| struct list_head *n; |
| lnet_ni_t *ni = NULL; |
| int skip = *ppos - 1; |
| |
| lnet_net_lock(0); |
| |
| n = the_lnet.ln_nis.next; |
| |
| while (n != &the_lnet.ln_nis) { |
| lnet_ni_t *a_ni = list_entry(n, lnet_ni_t, ni_list); |
| |
| if (skip == 0) { |
| ni = a_ni; |
| break; |
| } |
| |
| skip--; |
| n = n->next; |
| } |
| |
| if (ni != NULL) { |
| struct lnet_tx_queue *tq; |
| char *stat; |
| long now = get_seconds(); |
| int last_alive = -1; |
| int i; |
| int j; |
| |
| if (the_lnet.ln_routing) |
| last_alive = now - ni->ni_last_alive; |
| |
| /* @lo forever alive */ |
| if (ni->ni_lnd->lnd_type == LOLND) |
| last_alive = 0; |
| |
| lnet_ni_lock(ni); |
| LASSERT(ni->ni_status != NULL); |
| stat = (ni->ni_status->ns_status == |
| LNET_NI_STATUS_UP) ? "up" : "down"; |
| lnet_ni_unlock(ni); |
| |
| /* we actually output credits information for |
| * TX queue of each partition */ |
| cfs_percpt_for_each(tq, i, ni->ni_tx_queues) { |
| for (j = 0; ni->ni_cpts != NULL && |
| j < ni->ni_ncpts; j++) { |
| if (i == ni->ni_cpts[j]) |
| break; |
| } |
| |
| if (j == ni->ni_ncpts) |
| continue; |
| |
| if (i != 0) |
| lnet_net_lock(i); |
| |
| s += snprintf(s, tmpstr + tmpsiz - s, |
| "%-24s %6s %5d %4d %4d %4d %5d %5d %5d\n", |
| libcfs_nid2str(ni->ni_nid), stat, |
| last_alive, *ni->ni_refs[i], |
| ni->ni_peertxcredits, |
| ni->ni_peerrtrcredits, |
| tq->tq_credits_max, |
| tq->tq_credits, tq->tq_credits_min); |
| if (i != 0) |
| lnet_net_unlock(i); |
| } |
| LASSERT(tmpstr + tmpsiz - s > 0); |
| } |
| |
| lnet_net_unlock(0); |
| } |
| |
| len = s - tmpstr; /* how many bytes was written */ |
| |
| if (len > *lenp) { /* linux-supplied buffer is too small */ |
| rc = -EINVAL; |
| } else if (len > 0) { /* wrote something */ |
| if (copy_to_user(buffer, tmpstr, len)) |
| rc = -EFAULT; |
| else |
| *ppos += 1; |
| } |
| |
| LIBCFS_FREE(tmpstr, tmpsiz); |
| |
| if (rc == 0) |
| *lenp = len; |
| |
| return rc; |
| } |
| |
| struct lnet_portal_rotors { |
| int pr_value; |
| const char *pr_name; |
| const char *pr_desc; |
| }; |
| |
| static struct lnet_portal_rotors portal_rotors[] = { |
| { |
| .pr_value = LNET_PTL_ROTOR_OFF, |
| .pr_name = "OFF", |
| .pr_desc = "Turn off message rotor for wildcard portals" |
| }, |
| { |
| .pr_value = LNET_PTL_ROTOR_ON, |
| .pr_name = "ON", |
| .pr_desc = "round-robin dispatch all PUT messages for " |
| "wildcard portals" |
| }, |
| { |
| .pr_value = LNET_PTL_ROTOR_RR_RT, |
| .pr_name = "RR_RT", |
| .pr_desc = "round-robin dispatch routed PUT message for " |
| "wildcard portals" |
| }, |
| { |
| .pr_value = LNET_PTL_ROTOR_HASH_RT, |
| .pr_name = "HASH_RT", |
| .pr_desc = "dispatch routed PUT message by hashing source " |
| "NID for wildcard portals" |
| }, |
| { |
| .pr_value = -1, |
| .pr_name = NULL, |
| .pr_desc = NULL |
| }, |
| }; |
| |
| extern int portal_rotor; |
| |
| static int __proc_lnet_portal_rotor(void *data, int write, |
| loff_t pos, void __user *buffer, int nob) |
| { |
| const int buf_len = 128; |
| char *buf; |
| char *tmp; |
| int rc; |
| int i; |
| |
| LIBCFS_ALLOC(buf, buf_len); |
| if (buf == NULL) |
| return -ENOMEM; |
| |
| if (!write) { |
| lnet_res_lock(0); |
| |
| for (i = 0; portal_rotors[i].pr_value >= 0; i++) { |
| if (portal_rotors[i].pr_value == portal_rotor) |
| break; |
| } |
| |
| LASSERT(portal_rotors[i].pr_value == portal_rotor); |
| lnet_res_unlock(0); |
| |
| rc = snprintf(buf, buf_len, |
| "{\n\tportals: all\n" |
| "\trotor: %s\n\tdescription: %s\n}", |
| portal_rotors[i].pr_name, |
| portal_rotors[i].pr_desc); |
| |
| if (pos >= min_t(int, rc, buf_len)) { |
| rc = 0; |
| } else { |
| rc = cfs_trace_copyout_string(buffer, nob, |
| buf + pos, "\n"); |
| } |
| goto out; |
| } |
| |
| rc = cfs_trace_copyin_string(buf, buf_len, buffer, nob); |
| if (rc < 0) |
| goto out; |
| |
| tmp = cfs_trimwhite(buf); |
| |
| rc = -EINVAL; |
| lnet_res_lock(0); |
| for (i = 0; portal_rotors[i].pr_name != NULL; i++) { |
| if (strncasecmp(portal_rotors[i].pr_name, tmp, |
| strlen(portal_rotors[i].pr_name)) == 0) { |
| portal_rotor = portal_rotors[i].pr_value; |
| rc = 0; |
| break; |
| } |
| } |
| lnet_res_unlock(0); |
| out: |
| LIBCFS_FREE(buf, buf_len); |
| return rc; |
| } |
| |
| static int proc_lnet_portal_rotor(struct ctl_table *table, int write, |
| void __user *buffer, size_t *lenp, |
| loff_t *ppos) |
| { |
| return proc_call_handler(table->data, write, ppos, buffer, lenp, |
| __proc_lnet_portal_rotor); |
| } |
| |
| static struct ctl_table lnet_table[] = { |
| /* |
| * NB No .strategy entries have been provided since sysctl(8) prefers |
| * to go via /proc for portability. |
| */ |
| { |
| .procname = "stats", |
| .mode = 0644, |
| .proc_handler = &proc_lnet_stats, |
| }, |
| { |
| .procname = "routes", |
| .mode = 0444, |
| .proc_handler = &proc_lnet_routes, |
| }, |
| { |
| .procname = "routers", |
| .mode = 0444, |
| .proc_handler = &proc_lnet_routers, |
| }, |
| { |
| .procname = "peers", |
| .mode = 0444, |
| .proc_handler = &proc_lnet_peers, |
| }, |
| { |
| .procname = "buffers", |
| .mode = 0444, |
| .proc_handler = &proc_lnet_buffers, |
| }, |
| { |
| .procname = "nis", |
| .mode = 0444, |
| .proc_handler = &proc_lnet_nis, |
| }, |
| { |
| .procname = "portal_rotor", |
| .mode = 0644, |
| .proc_handler = &proc_lnet_portal_rotor, |
| }, |
| { |
| } |
| }; |
| |
| static struct ctl_table top_table[] = { |
| { |
| .procname = "lnet", |
| .mode = 0555, |
| .data = NULL, |
| .maxlen = 0, |
| .child = lnet_table, |
| }, |
| { |
| } |
| }; |
| |
| void |
| lnet_proc_init(void) |
| { |
| if (lnet_table_header == NULL) |
| lnet_table_header = register_sysctl_table(top_table); |
| } |
| |
| void |
| lnet_proc_fini(void) |
| { |
| if (lnet_table_header != NULL) |
| unregister_sysctl_table(lnet_table_header); |
| |
| lnet_table_header = NULL; |
| } |
| |
| #else |
| |
| void |
| lnet_proc_init(void) |
| { |
| } |
| |
| void |
| lnet_proc_fini(void) |
| { |
| } |
| |
| #endif |