Commit 66771c8f authored by Pavel Emelyanov's avatar Pavel Emelyanov

sockets: Dump socket queus without parasite

Since now we have local copies of a remove FDs we can dump socket queus without entering
a parasite code. This makes the code MUCH simpler.
Signed-off-by: 's avatarPavel Emelyanov <xemul@parallels.com>
parent 3b0397db
......@@ -370,8 +370,7 @@ static int dump_one_chrdev(struct fd_parms *p, int lfd, const struct cr_fdset *s
}
static int dump_one_fd(pid_t pid, int fd, int lfd,
const struct cr_fdset *cr_fdset,
struct sk_queue *sk_queue)
const struct cr_fdset *cr_fdset)
{
struct fd_parms p;
......@@ -381,7 +380,7 @@ static int dump_one_fd(pid_t pid, int fd, int lfd,
}
if (S_ISSOCK(p.stat.st_mode))
return dump_socket(&p, lfd, cr_fdset, sk_queue);
return dump_socket(&p, lfd, cr_fdset);
if (S_ISCHR(p.stat.st_mode))
return dump_one_chrdev(&p, lfd, cr_fdset);
......@@ -396,7 +395,7 @@ static int dump_one_fd(pid_t pid, int fd, int lfd,
}
static int dump_task_files_seized(struct parasite_ctl *ctl, const struct cr_fdset *cr_fdset,
int *fds, int nr_fds, struct sk_queue *sk_queue)
int *fds, int nr_fds)
{
int *lfds;
int i, ret = -1;
......@@ -425,8 +424,7 @@ static int dump_task_files_seized(struct parasite_ctl *ctl, const struct cr_fdse
}
for (i = 0; i < nr_fds; i++) {
ret = dump_one_fd(ctl->pid, fds[i], lfds[i],
cr_fdset, sk_queue);
ret = dump_one_fd(ctl->pid, fds[i], lfds[i], cr_fdset);
close(lfds[i]);
if (ret)
goto err;
......@@ -1299,7 +1297,6 @@ static int dump_one_task(const struct pstree_item *item)
struct parasite_ctl *parasite_ctl;
int ret = -1;
struct parasite_dump_misc misc;
struct sk_queue sk_queue = { };
struct cr_fdset *cr_fdset = NULL;
int nr_fds = PARASITE_MAX_FDS;
......@@ -1349,7 +1346,7 @@ static int dump_one_task(const struct pstree_item *item)
goto err;
}
ret = dump_task_files_seized(parasite_ctl, cr_fdset, fds, nr_fds, &sk_queue);
ret = dump_task_files_seized(parasite_ctl, cr_fdset, fds, nr_fds);
if (ret) {
pr_err("Dump files (pid: %d) failed with %d\n", pid, ret);
goto err;
......@@ -1391,12 +1388,6 @@ static int dump_one_task(const struct pstree_item *item)
goto err;
}
ret = parasite_dump_socket_info(parasite_ctl, cr_fdset, &sk_queue);
if (ret) {
pr_err("Can't dump socket info (pid: %d)\n", pid);
goto err;
}
ret = parasite_cure_seized(parasite_ctl);
if (ret) {
pr_err("Can't cure (pid: %d) from parasite\n", pid);
......
......@@ -34,7 +34,6 @@ extern int parasite_dump_itimers_seized(struct parasite_ctl *ctl, struct cr_fdse
struct parasite_dump_misc;
extern int parasite_dump_misc_seized(struct parasite_ctl *ctl, struct parasite_dump_misc *misc);
extern int parasite_dump_socket_info(struct parasite_ctl *ctl, struct cr_fdset *fdset, struct sk_queue *queue);
extern int parasite_dump_pages_seized(struct parasite_ctl *ctl,
struct list_head *vma_area_list,
struct cr_fdset *cr_fdset);
......
......@@ -31,7 +31,6 @@ enum {
PARASITE_CMD_DUMP_ITIMERS,
PARASITE_CMD_DUMP_MISC,
PARASITE_CMD_DUMP_TID_ADDR,
PARASITE_CMD_DUMP_SK_QUEUES,
PARASITE_CMD_DRAIN_FDS,
PARASITE_CMD_MAX,
......@@ -81,12 +80,6 @@ struct parasite_dump_tid_addr {
unsigned int *tid_addr;
};
struct parasite_dump_sk_queues {
parasite_status_t status;
unsigned nr_items;
struct sk_queue_item items[0];
};
#define PARASITE_MAX_FDS (PAGE_SIZE / sizeof(int))
struct parasite_drain_fd {
......
......@@ -5,26 +5,10 @@
#include <unistd.h>
#include <stdbool.h>
struct sk_queue_item {
int fd;
int type;
unsigned int sk_id;
};
struct sk_queue_entry {
struct sk_queue_item item;
struct sk_queue_entry *next;
};
struct sk_queue {
unsigned int entries;
struct sk_queue_entry *list;
};
struct cr_fdset;
struct fd_parms;
extern int dump_socket(struct fd_parms *p, int lfd,
const struct cr_fdset *cr_fdset, struct sk_queue *queue);
const struct cr_fdset *cr_fdset);
extern int collect_sockets(void);
extern int prepare_sockets(int pid);
......
......@@ -462,61 +462,6 @@ int parasite_dump_misc_seized(struct parasite_ctl *ctl, struct parasite_dump_mis
sizeof(struct parasite_dump_misc));
}
int parasite_dump_socket_info(struct parasite_ctl *ctl, struct cr_fdset *fdset,
struct sk_queue *queue)
{
int ret, i;
struct cr_fdset *fds;
unsigned arg_size;
struct parasite_dump_sk_queues *arg;
struct sk_queue_entry *sk_entry;
if (queue->entries == 0)
return 0;
pr_info("Dumping socket queues\n");
arg_size = sizeof(struct parasite_dump_sk_queues) +
queue->entries * sizeof(struct sk_queue_item);
/* FIXME arg size is only enough for ~1k of sockets */
if (arg_size > PARASITE_ARG_SIZE) {
pr_err("Too many sockets to drain queue from\n");
return -1;
}
ret = -1;
arg = xzalloc(arg_size);
if (arg == NULL)
goto err_alloc;
sk_entry = queue->list;
for (i = 0; i < queue->entries; i++, arg->nr_items++) {
struct sk_queue_entry *tmp = sk_entry;
memcpy(&arg->items[i], &sk_entry->item, sizeof(struct sk_queue_item));
sk_entry = tmp->next;
xfree(tmp);
}
ret = parasite_prep_file(fdset_fd(glob_fdset, CR_FD_SK_QUEUES), ctl);
if (ret < 0)
goto err_prepf;
ret = parasite_execute(PARASITE_CMD_DUMP_SK_QUEUES, ctl,
(parasite_status_t *)arg, arg_size);
if (ret < 0)
goto err_exec;
return 0;
err_exec:
err_prepf:
xfree(arg);
err_alloc:
return -1;
}
/*
* This routine drives parasite code (been previously injected into a victim
* process) and tells it to dump pages into the file.
......
......@@ -374,126 +374,6 @@ static int dump_tid_addr(struct parasite_dump_tid_addr *args)
return 0;
}
static int dump_socket_queue(int img_fd, struct sk_queue_item *item)
{
struct sk_packet_entry *pe;
unsigned long size;
socklen_t tmp;
int ret, orig_peek_off;
int sock_fd = item->fd;
/*
* Save original peek offset.
*/
ret = sys_getsockopt(sock_fd, SOL_SOCKET, SO_PEEK_OFF, &orig_peek_off, &tmp);
if (ret < 0) {
sys_write_msg("getsockopt failed\n");
return ret;
}
/*
* Discover max DGRAM size
*/
ret = sys_getsockopt(sock_fd, SOL_SOCKET, SO_SNDBUF, &ret, &tmp);
if (ret < 0) {
sys_write_msg("getsockopt failed\n");
return ret;
}
/*
* Note: 32 bytes will be used by kernel for protocol header.
*/
size = ret - 32;
/*
* Try to alloc buffer for max supported DGRAM + our header.
* Note: STREAM queue will be written by chunks of this size.
*/
pe = brk_alloc(size + sizeof(struct sk_packet_entry));
if (!pe) {
sys_write_msg("not enough mem for skb\n");
return -ENOMEM;
}
/*
* Enable peek offset incrementation.
*/
ret = sys_setsockopt(sock_fd, SOL_SOCKET, SO_PEEK_OFF, &ret, sizeof(int));
if (ret < 0) {
sys_write_msg("setsockopt fail\n");
goto err_brk;
}
pe->id_for = item->sk_id;
while (1) {
struct iovec iov = {
.iov_base = pe->data,
.iov_len = size,
};
struct msghdr msg = {
.msg_iov = &iov,
.msg_iovlen = 1,
};
ret = pe->length = sys_recvmsg(sock_fd, &msg, MSG_DONTWAIT | MSG_PEEK);
if (ret < 0) {
if (ret == -EAGAIN)
break; /* we're done */
sys_write_msg("sys_recvmsg fail: error\n");
goto err_set_sock;
}
if (msg.msg_flags & MSG_TRUNC) {
/*
* DGRAM thuncated. This should not happen. But we have
* to check...
*/
sys_write_msg("sys_recvmsg failed: truncated\n");
ret = -E2BIG;
goto err_set_sock;
}
ret = sys_write(img_fd, pe, sizeof(pe) + pe->length);
if (ret != sizeof(pe) + pe->length) {
sys_write_msg("sys_write failed\n");
ret = -EIO;
goto err_set_sock;
}
}
ret = 0;
err_set_sock:
/*
* Restore original peek offset.
*/
ret = sys_setsockopt(sock_fd, SOL_SOCKET, SO_PEEK_OFF, &orig_peek_off, sizeof(int));
if (ret < 0)
sys_write_msg("setsockopt failed on restore\n");
err_brk:
brk_free(size + sizeof(struct sk_packet_entry));
return ret;
}
static int dump_skqueues(struct parasite_dump_sk_queues *args)
{
parasite_status_t *st = &args->status;
int img_fd, i, ret = -1;
img_fd = recv_fd(tsock);
if (img_fd < 0) {
SET_PARASITE_RET(st, img_fd);
return img_fd;
}
for (i = 0; i < args->nr_items; i++) {
ret = dump_socket_queue(img_fd, &args->items[i]);
if (ret < 0) {
SET_PARASITE_RET(st, ret);
goto err_dmp;
}
}
ret = 0;
err_dmp:
sys_close(img_fd);
return ret;
}
static int drain_fds(struct parasite_drain_fd *args)
{
parasite_status_t *st = &args->status;
......@@ -577,7 +457,6 @@ static int __used parasite_service(unsigned long cmd, void *args)
BUILD_BUG_ON(sizeof(struct parasite_init_args) > PARASITE_ARG_SIZE);
BUILD_BUG_ON(sizeof(struct parasite_dump_misc) > PARASITE_ARG_SIZE);
BUILD_BUG_ON(sizeof(struct parasite_dump_tid_addr) > PARASITE_ARG_SIZE);
BUILD_BUG_ON(sizeof(struct parasite_dump_sk_queues) > PARASITE_ARG_SIZE);
BUILD_BUG_ON(sizeof(struct parasite_drain_fd) > PARASITE_ARG_SIZE);
switch (cmd) {
......@@ -601,8 +480,6 @@ static int __used parasite_service(unsigned long cmd, void *args)
return dump_misc((struct parasite_dump_misc *)args);
case PARASITE_CMD_DUMP_TID_ADDR:
return dump_tid_addr((struct parasite_dump_tid_addr *)args);
case PARASITE_CMD_DUMP_SK_QUEUES:
return dump_skqueues((struct parasite_dump_sk_queues *)args);
case PARASITE_CMD_DRAIN_FDS:
return drain_fds((struct parasite_drain_fd *)args);
default:
......
#include <sys/socket.h>
#include <linux/netlink.h>
#include <linux/types.h>
#include <linux/net.h>
#include <linux/un.h>
#include <sys/types.h>
#include <sys/vfs.h>
#include <sys/stat.h>
......@@ -23,6 +21,7 @@
#include "util.h"
#include "inet_diag.h"
#include "files.h"
#include "util-net.h"
static char buf[4096];
......@@ -87,23 +86,98 @@ struct inet_sk_desc {
unsigned int dst_addr[4];
};
static int unix_sk_queue_add(int fd, const struct unix_sk_desc *sd,
struct sk_queue *queue)
static int dump_socket_queue(int sock_fd, int sock_id)
{
struct sk_queue_entry *next, *new;
struct sk_packet_entry *pe;
unsigned long size;
socklen_t tmp;
int ret, orig_peek_off;
new = xmalloc(sizeof(struct sk_queue_entry));
if (!new)
return -1;
/*
* Save original peek offset.
*/
tmp = sizeof(orig_peek_off);
ret = getsockopt(sock_fd, SOL_SOCKET, SO_PEEK_OFF, &orig_peek_off, &tmp);
if (ret < 0) {
pr_perror("getsockopt failed\n");
return ret;
}
/*
* Discover max DGRAM size
*/
tmp = sizeof(size);
ret = getsockopt(sock_fd, SOL_SOCKET, SO_SNDBUF, &size, &tmp);
if (ret < 0) {
pr_perror("getsockopt failed\n");
return ret;
}
new->item.fd = fd;
new->item.type = sd->type;
new->item.sk_id = sd->sd.ino;
new->next = queue->list;
/* Note: 32 bytes will be used by kernel for protocol header. */
size -= 32;
/*
* Try to alloc buffer for max supported DGRAM + our header.
* Note: STREAM queue will be written by chunks of this size.
*/
pe = xmalloc(size + sizeof(struct sk_packet_entry));
if (!pe)
return -ENOMEM;
queue->list = new;
queue->entries++;
return 0;
/*
* Enable peek offset incrementation.
*/
ret = setsockopt(sock_fd, SOL_SOCKET, SO_PEEK_OFF, &ret, sizeof(int));
if (ret < 0) {
pr_perror("setsockopt fail\n");
goto err_brk;
}
pe->id_for = sock_id;
while (1) {
struct iovec iov = {
.iov_base = pe->data,
.iov_len = size,
};
struct msghdr msg = {
.msg_iov = &iov,
.msg_iovlen = 1,
};
ret = pe->length = recvmsg(sock_fd, &msg, MSG_DONTWAIT | MSG_PEEK);
if (ret < 0) {
if (ret == -EAGAIN)
break; /* we're done */
pr_perror("sys_recvmsg fail: error\n");
goto err_set_sock;
}
if (msg.msg_flags & MSG_TRUNC) {
/*
* DGRAM thuncated. This should not happen. But we have
* to check...
*/
pr_err("sys_recvmsg failed: truncated\n");
ret = -E2BIG;
goto err_set_sock;
}
ret = write_img_buf(fdset_fd(glob_fdset, CR_FD_SK_QUEUES),
pe, sizeof(pe) + pe->length);
if (ret < 0) {
ret = -EIO;
goto err_set_sock;
}
}
ret = 0;
err_set_sock:
/*
* Restore original peek offset.
*/
ret = setsockopt(sock_fd, SOL_SOCKET, SO_PEEK_OFF, &orig_peek_off, sizeof(int));
if (ret < 0)
pr_perror("setsockopt failed on restore\n");
err_brk:
xfree(pe);
return ret;
}
#define SK_HASH_SIZE 32
......@@ -240,8 +314,7 @@ static int can_dump_inet_sk(const struct inet_sk_desc *sk)
}
static int dump_one_inet(struct socket_desc *_sk, int fd,
const struct cr_fdset *cr_fdset,
struct sk_queue *queue)
const struct cr_fdset *cr_fdset)
{
struct inet_sk_desc *sk = (struct inet_sk_desc *)_sk;
struct inet_sk_entry ie;
......@@ -311,9 +384,8 @@ static int can_dump_unix_sk(const struct unix_sk_desc *sk)
return 1;
}
static int dump_one_unix(const struct socket_desc *_sk, int fd,
const struct cr_fdset *cr_fdset,
struct sk_queue *queue)
static int dump_one_unix(const struct socket_desc *_sk, int fd, int lfd,
const struct cr_fdset *cr_fdset)
{
const struct unix_sk_desc *sk = (struct unix_sk_desc *)_sk;
struct unix_sk_entry ue;
......@@ -369,7 +441,7 @@ static int dump_one_unix(const struct socket_desc *_sk, int fd,
if (sk->rqlen != 0 && !(sk->type == SOCK_STREAM &&
sk->state == TCP_LISTEN))
if (unix_sk_queue_add(fd, sk, queue))
if (dump_socket_queue(lfd, ue.id))
goto err;
pr_info("Dumping unix socket at %d\n", fd);
......@@ -382,8 +454,7 @@ err:
return -1;
}
int dump_socket(struct fd_parms *p, int lfd, const struct cr_fdset *cr_fdset,
struct sk_queue *queue)
int dump_socket(struct fd_parms *p, int lfd, const struct cr_fdset *cr_fdset)
{
struct socket_desc *sk;
struct statfs fst;
......@@ -398,9 +469,9 @@ int dump_socket(struct fd_parms *p, int lfd, const struct cr_fdset *cr_fdset,
switch (sk->family) {
case AF_UNIX:
return dump_one_unix(sk, p->fd_name, cr_fdset, queue);
return dump_one_unix(sk, p->fd_name, lfd, cr_fdset);
case AF_INET:
return dump_one_inet(sk, p->fd_name, cr_fdset, queue);
return dump_one_inet(sk, p->fd_name, cr_fdset);
default:
pr_err("BUG! Unknown socket collected\n");
break;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment