}
#endif
+static void xs_format_peer_addresses(struct rpc_xprt *xprt)
+{
+ struct sockaddr_in *addr = (struct sockaddr_in *) &xprt->addr;
+ char *buf;
+
+ buf = kzalloc(20, GFP_KERNEL);
+ if (buf) {
+ snprintf(buf, 20, "%u.%u.%u.%u",
+ NIPQUAD(addr->sin_addr.s_addr));
+ }
+ xprt->address_strings[RPC_DISPLAY_ADDR] = buf;
+
+ buf = kzalloc(8, GFP_KERNEL);
+ if (buf) {
+ snprintf(buf, 8, "%u",
+ ntohs(addr->sin_port));
+ }
+ xprt->address_strings[RPC_DISPLAY_PORT] = buf;
+
+ if (xprt->prot == IPPROTO_UDP)
+ xprt->address_strings[RPC_DISPLAY_PROTO] = "udp";
+ else
+ xprt->address_strings[RPC_DISPLAY_PROTO] = "tcp";
+
+ buf = kzalloc(48, GFP_KERNEL);
+ if (buf) {
+ snprintf(buf, 48, "addr=%u.%u.%u.%u port=%u proto=%s",
+ NIPQUAD(addr->sin_addr.s_addr),
+ ntohs(addr->sin_port),
+ xprt->prot == IPPROTO_UDP ? "udp" : "tcp");
+ }
+ xprt->address_strings[RPC_DISPLAY_ALL] = buf;
+}
+
+static void xs_free_peer_addresses(struct rpc_xprt *xprt)
+{
+ kfree(xprt->address_strings[RPC_DISPLAY_ADDR]);
+ kfree(xprt->address_strings[RPC_DISPLAY_PORT]);
+ kfree(xprt->address_strings[RPC_DISPLAY_ALL]);
+}
+
#define XS_SENDMSG_FLAGS (MSG_DONTWAIT | MSG_NOSIGNAL)
-static inline int xs_send_head(struct socket *sock, struct sockaddr *addr, int addrlen, struct xdr_buf *xdr, unsigned int base, unsigned int len)
+static int xs_send_kvec(struct socket *sock, struct sockaddr *addr, int addrlen, struct kvec *vec, unsigned int base, int more)
{
- struct kvec iov = {
- .iov_base = xdr->head[0].iov_base + base,
- .iov_len = len - base,
- };
struct msghdr msg = {
.msg_name = addr,
.msg_namelen = addrlen,
- .msg_flags = XS_SENDMSG_FLAGS,
+ .msg_flags = XS_SENDMSG_FLAGS | (more ? MSG_MORE : 0),
+ };
+ struct kvec iov = {
+ .iov_base = vec->iov_base + base,
+ .iov_len = vec->iov_len - base,
};
- if (xdr->len > len)
- msg.msg_flags |= MSG_MORE;
-
- if (likely(iov.iov_len))
+ if (iov.iov_len != 0)
return kernel_sendmsg(sock, &msg, &iov, 1, iov.iov_len);
return kernel_sendmsg(sock, &msg, NULL, 0, 0);
}
-static int xs_send_tail(struct socket *sock, struct xdr_buf *xdr, unsigned int base, unsigned int len)
+static int xs_send_pagedata(struct socket *sock, struct xdr_buf *xdr, unsigned int base, int more)
{
- struct kvec iov = {
- .iov_base = xdr->tail[0].iov_base + base,
- .iov_len = len - base,
- };
- struct msghdr msg = {
- .msg_flags = XS_SENDMSG_FLAGS,
- };
+ struct page **ppage;
+ unsigned int remainder;
+ int err, sent = 0;
+
+ remainder = xdr->page_len - base;
+ base += xdr->page_base;
+ ppage = xdr->pages + (base >> PAGE_SHIFT);
+ base &= ~PAGE_MASK;
+ for(;;) {
+ unsigned int len = min_t(unsigned int, PAGE_SIZE - base, remainder);
+ int flags = XS_SENDMSG_FLAGS;
- return kernel_sendmsg(sock, &msg, &iov, 1, iov.iov_len);
+ remainder -= len;
+ if (remainder != 0 || more)
+ flags |= MSG_MORE;
+ err = sock->ops->sendpage(sock, *ppage, base, len, flags);
+ if (remainder == 0 || err != len)
+ break;
+ sent += err;
+ ppage++;
+ base = 0;
+ }
+ if (sent == 0)
+ return err;
+ if (err > 0)
+ sent += err;
+ return sent;
}
/**
* @base: starting position in the buffer
*
*/
-static inline int xs_sendpages(struct socket *sock, struct sockaddr *addr, int addrlen, struct xdr_buf *xdr, unsigned int base)
+static int xs_sendpages(struct socket *sock, struct sockaddr *addr, int addrlen, struct xdr_buf *xdr, unsigned int base)
{
- struct page **ppage = xdr->pages;
- unsigned int len, pglen = xdr->page_len;
- int err, ret = 0;
+ unsigned int remainder = xdr->len - base;
+ int err, sent = 0;
if (unlikely(!sock))
return -ENOTCONN;
clear_bit(SOCK_ASYNC_NOSPACE, &sock->flags);
+ if (base != 0) {
+ addr = NULL;
+ addrlen = 0;
+ }
- len = xdr->head[0].iov_len;
- if (base < len || (addr != NULL && base == 0)) {
- err = xs_send_head(sock, addr, addrlen, xdr, base, len);
- if (ret == 0)
- ret = err;
- else if (err > 0)
- ret += err;
- if (err != (len - base))
+ if (base < xdr->head[0].iov_len || addr != NULL) {
+ unsigned int len = xdr->head[0].iov_len - base;
+ remainder -= len;
+ err = xs_send_kvec(sock, addr, addrlen, &xdr->head[0], base, remainder != 0);
+ if (remainder == 0 || err != len)
goto out;
+ sent += err;
base = 0;
} else
- base -= len;
-
- if (unlikely(pglen == 0))
- goto copy_tail;
- if (unlikely(base >= pglen)) {
- base -= pglen;
- goto copy_tail;
- }
- if (base || xdr->page_base) {
- pglen -= base;
- base += xdr->page_base;
- ppage += base >> PAGE_CACHE_SHIFT;
- base &= ~PAGE_CACHE_MASK;
- }
-
- do {
- int flags = XS_SENDMSG_FLAGS;
-
- len = PAGE_CACHE_SIZE;
- if (base)
- len -= base;
- if (pglen < len)
- len = pglen;
-
- if (pglen != len || xdr->tail[0].iov_len != 0)
- flags |= MSG_MORE;
+ base -= xdr->head[0].iov_len;
- err = kernel_sendpage(sock, *ppage, base, len, flags);
- if (ret == 0)
- ret = err;
- else if (err > 0)
- ret += err;
- if (err != len)
+ if (base < xdr->page_len) {
+ unsigned int len = xdr->page_len - base;
+ remainder -= len;
+ err = xs_send_pagedata(sock, xdr, base, remainder != 0);
+ if (remainder == 0 || err != len)
goto out;
+ sent += err;
base = 0;
- ppage++;
- } while ((pglen -= len) != 0);
-copy_tail:
- len = xdr->tail[0].iov_len;
- if (base < len) {
- err = xs_send_tail(sock, xdr, base, len);
- if (ret == 0)
- ret = err;
- else if (err > 0)
- ret += err;
- }
+ } else
+ base -= xdr->page_len;
+
+ if (base >= xdr->tail[0].iov_len)
+ return sent;
+ err = xs_send_kvec(sock, NULL, 0, &xdr->tail[0], base, 0);
out:
- return ret;
+ if (sent == 0)
+ return err;
+ if (err > 0)
+ sent += err;
+ return sent;
}
/**
req->rq_xtime = jiffies;
status = xs_sendpages(xprt->sock, (struct sockaddr *) &xprt->addr,
- sizeof(xprt->addr), xdr, req->rq_bytes_sent);
+ xprt->addrlen, xdr, req->rq_bytes_sent);
dprintk("RPC: xs_udp_send_request(%u) = %d\n",
xdr->len - req->rq_bytes_sent, status);
xprt_disconnect(xprt);
xs_close(xprt);
+ xs_free_peer_addresses(xprt);
kfree(xprt->slot);
+ kfree(xprt);
}
static inline struct rpc_xprt *xprt_from_sock(struct sock *sk)
struct rpc_rqst *rovr;
struct sk_buff *skb;
int err, repsize, copied;
- u32 _xid, *xp;
+ u32 _xid;
+ __be32 *xp;
read_lock(&sk->sk_callback_lock);
dprintk("RPC: xs_udp_data_ready...\n");
}
/**
+ * xs_print_peer_address - format an IPv4 address for printing
+ * @xprt: generic transport
+ * @format: flags field indicating which parts of the address to render
+ */
+static char *xs_print_peer_address(struct rpc_xprt *xprt, enum rpc_display_format_t format)
+{
+ if (xprt->address_strings[format] != NULL)
+ return xprt->address_strings[format];
+ else
+ return "unprintable";
+}
+
+/**
* xs_set_port - reset the port number in the remote endpoint address
* @xprt: generic transport
* @port: new port number
*/
static void xs_set_port(struct rpc_xprt *xprt, unsigned short port)
{
+ struct sockaddr_in *sap = (struct sockaddr_in *) &xprt->addr;
+
dprintk("RPC: setting port for xprt %p to %u\n", xprt, port);
- xprt->addr.sin_port = htons(port);
+
+ sap->sin_port = htons(port);
}
static int xs_bindresvport(struct rpc_xprt *xprt, struct socket *sock)
struct socket *sock = xprt->sock;
int err, status = -EIO;
- if (xprt->shutdown || xprt->addr.sin_port == 0)
+ if (xprt->shutdown || !xprt_bound(xprt))
goto out;
- dprintk("RPC: xs_udp_connect_worker for xprt %p\n", xprt);
-
/* Start by resetting any existing state */
xs_close(xprt);
goto out;
}
+ dprintk("RPC: worker connecting xprt %p to address: %s\n",
+ xprt, xs_print_peer_address(xprt, RPC_DISPLAY_ALL));
+
if (!xprt->inet) {
struct sock *sk = sock->sk;
struct socket *sock = xprt->sock;
int err, status = -EIO;
- if (xprt->shutdown || xprt->addr.sin_port == 0)
+ if (xprt->shutdown || !xprt_bound(xprt))
goto out;
- dprintk("RPC: xs_tcp_connect_worker for xprt %p\n", xprt);
-
if (!xprt->sock) {
/* start from scratch */
if ((err = sock_create_kern(PF_INET, SOCK_STREAM, IPPROTO_TCP, &sock)) < 0) {
/* "close" the socket, preserving the local port */
xs_tcp_reuse_connection(xprt);
+ dprintk("RPC: worker connecting xprt %p to address: %s\n",
+ xprt, xs_print_peer_address(xprt, RPC_DISPLAY_ALL));
+
if (!xprt->inet) {
struct sock *sk = sock->sk;
xprt->stat.connect_count++;
xprt->stat.connect_start = jiffies;
status = kernel_connect(sock, (struct sockaddr *) &xprt->addr,
- sizeof(xprt->addr), O_NONBLOCK);
+ xprt->addrlen, O_NONBLOCK);
dprintk("RPC: %p connect status %d connected %d sock state %d\n",
xprt, -status, xprt_connected(xprt), sock->sk->sk_state);
if (status < 0) {
static struct rpc_xprt_ops xs_udp_ops = {
.set_buffer_size = xs_udp_set_buffer_size,
+ .print_addr = xs_print_peer_address,
.reserve_xprt = xprt_reserve_xprt_cong,
.release_xprt = xprt_release_xprt_cong,
+ .rpcbind = rpc_getport,
.set_port = xs_set_port,
.connect = xs_connect,
.buf_alloc = rpc_malloc,
};
static struct rpc_xprt_ops xs_tcp_ops = {
+ .print_addr = xs_print_peer_address,
.reserve_xprt = xprt_reserve_xprt,
.release_xprt = xs_tcp_release_xprt,
+ .rpcbind = rpc_getport,
.set_port = xs_set_port,
.connect = xs_connect,
.buf_alloc = rpc_malloc,
.print_stats = xs_tcp_print_stats,
};
+static struct rpc_xprt *xs_setup_xprt(struct sockaddr *addr, size_t addrlen, unsigned int slot_table_size)
+{
+ struct rpc_xprt *xprt;
+
+ if (addrlen > sizeof(xprt->addr)) {
+ dprintk("RPC: xs_setup_xprt: address too large\n");
+ return ERR_PTR(-EBADF);
+ }
+
+ xprt = kzalloc(sizeof(struct rpc_xprt), GFP_KERNEL);
+ if (xprt == NULL) {
+ dprintk("RPC: xs_setup_xprt: couldn't allocate rpc_xprt\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ xprt->max_reqs = slot_table_size;
+ xprt->slot = kcalloc(xprt->max_reqs, sizeof(struct rpc_rqst), GFP_KERNEL);
+ if (xprt->slot == NULL) {
+ kfree(xprt);
+ dprintk("RPC: xs_setup_xprt: couldn't allocate slot table\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ memcpy(&xprt->addr, addr, addrlen);
+ xprt->addrlen = addrlen;
+ xprt->port = xs_get_random_port();
+
+ return xprt;
+}
+
/**
* xs_setup_udp - Set up transport to use a UDP socket
- * @xprt: transport to set up
+ * @addr: address of remote server
+ * @addrlen: length of address in bytes
* @to: timeout parameters
*
*/
-int xs_setup_udp(struct rpc_xprt *xprt, struct rpc_timeout *to)
+struct rpc_xprt *xs_setup_udp(struct sockaddr *addr, size_t addrlen, struct rpc_timeout *to)
{
- size_t slot_table_size;
+ struct rpc_xprt *xprt;
- dprintk("RPC: setting up udp-ipv4 transport...\n");
+ xprt = xs_setup_xprt(addr, addrlen, xprt_udp_slot_table_entries);
+ if (IS_ERR(xprt))
+ return xprt;
- xprt->max_reqs = xprt_udp_slot_table_entries;
- slot_table_size = xprt->max_reqs * sizeof(xprt->slot[0]);
- xprt->slot = kzalloc(slot_table_size, GFP_KERNEL);
- if (xprt->slot == NULL)
- return -ENOMEM;
+ if (ntohs(((struct sockaddr_in *)addr)->sin_port) != 0)
+ xprt_set_bound(xprt);
xprt->prot = IPPROTO_UDP;
- xprt->port = xs_get_random_port();
xprt->tsh_size = 0;
- xprt->resvport = capable(CAP_NET_BIND_SERVICE) ? 1 : 0;
/* XXX: header size can vary due to auth type, IPv6, etc. */
xprt->max_payload = (1U << 16) - (MAX_HEADER << 3);
else
xprt_set_timeout(&xprt->timeout, 5, 5 * HZ);
- return 0;
+ xs_format_peer_addresses(xprt);
+ dprintk("RPC: set up transport to address %s\n",
+ xs_print_peer_address(xprt, RPC_DISPLAY_ALL));
+
+ return xprt;
}
/**
* xs_setup_tcp - Set up transport to use a TCP socket
- * @xprt: transport to set up
+ * @addr: address of remote server
+ * @addrlen: length of address in bytes
* @to: timeout parameters
*
*/
-int xs_setup_tcp(struct rpc_xprt *xprt, struct rpc_timeout *to)
+struct rpc_xprt *xs_setup_tcp(struct sockaddr *addr, size_t addrlen, struct rpc_timeout *to)
{
- size_t slot_table_size;
+ struct rpc_xprt *xprt;
- dprintk("RPC: setting up tcp-ipv4 transport...\n");
+ xprt = xs_setup_xprt(addr, addrlen, xprt_tcp_slot_table_entries);
+ if (IS_ERR(xprt))
+ return xprt;
- xprt->max_reqs = xprt_tcp_slot_table_entries;
- slot_table_size = xprt->max_reqs * sizeof(xprt->slot[0]);
- xprt->slot = kzalloc(slot_table_size, GFP_KERNEL);
- if (xprt->slot == NULL)
- return -ENOMEM;
+ if (ntohs(((struct sockaddr_in *)addr)->sin_port) != 0)
+ xprt_set_bound(xprt);
xprt->prot = IPPROTO_TCP;
- xprt->port = xs_get_random_port();
xprt->tsh_size = sizeof(rpc_fraghdr) / sizeof(u32);
- xprt->resvport = capable(CAP_NET_BIND_SERVICE) ? 1 : 0;
xprt->max_payload = RPC_MAX_FRAGMENT_SIZE;
INIT_WORK(&xprt->connect_worker, xs_tcp_connect_worker, xprt);
else
xprt_set_timeout(&xprt->timeout, 2, 60 * HZ);
- return 0;
+ xs_format_peer_addresses(xprt);
+ dprintk("RPC: set up transport to address %s\n",
+ xs_print_peer_address(xprt, RPC_DISPLAY_ALL));
+
+ return xprt;
}