Skip to content

Commit 9812fa4

Browse files
committed
Use writev for remote consoles
We really don't want to "leak" the protocol requirement for remote consoles to the rest of the code base. To hide that, and avoid two `write_all()` method calls in a row, we promote `writev_buffer_flush()` to a `utils` method called `writev_all()` to leverage I/O vectors in one call (memory copies will need to occur for remote sockets, so using `writev()` avoids that). Further, we use the `g_ptr_array_foreach()` method instead of doing it ourselves. Signed-off-by: Peter Portante <peter.portante@redhat.com>
1 parent d40339b commit 9812fa4

5 files changed

Lines changed: 95 additions & 92 deletions

File tree

src/conn_sock.c

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ static char *setup_socket(int *fd, const char *path);
3939
setup_attach_socket() is responsible for setting the correct remote FD and pushing it onto the queue.
4040
*/
4141
static struct local_sock_s local_mainfd_stdin = {&mainfd_stdin, true, NULL, "container stdin", NULL};
42-
struct remote_sock_s remote_attach_sock = {
42+
static struct remote_sock_s remote_attach_sock = {
4343
SOCK_TYPE_CONSOLE, /* sock_type */
4444
-1, /* fd */
4545
&local_mainfd_stdin, /* dest */
@@ -349,33 +349,35 @@ char *socket_parent_dir(gboolean use_full_attach_path, size_t desired_len)
349349
return base_path;
350350
}
351351

352-
353352
void schedule_main_stdin_write()
354353
{
355354
schedule_local_sock_write(&local_mainfd_stdin);
356355
}
357356

358-
void write_back_to_remote_consoles(stdpipe_t pipe, char *buf, int len)
357+
static void write_remote_sock(gpointer data, G_GNUC_UNUSED gpointer user_data)
359358
{
360-
if (local_mainfd_stdin.readers == NULL)
359+
struct remote_sock_s *remote_sock = (struct remote_sock_s *)data;
360+
if (!remote_sock->writable)
361361
return;
362-
363-
for (int i = local_mainfd_stdin.readers->len; i > 0; i--) {
364-
struct remote_sock_s *remote_sock = g_ptr_array_index(local_mainfd_stdin.readers, i - 1);
365-
366-
if (remote_sock->writable) {
367-
if (write_all(remote_sock->fd, &pipe, 1) < 0) {
368-
nwarn("Failed to write to remote console socket");
369-
remote_sock_shutdown(remote_sock, SHUT_WR);
370-
}
371-
if (write_all(remote_sock->fd, buf, len) < 0) {
372-
nwarn("Failed to write to remote console socket");
373-
remote_sock_shutdown(remote_sock, SHUT_WR);
374-
}
375-
}
362+
writev_iov_t *iov = (writev_iov_t *)user_data;
363+
if (writev_all(remote_sock->fd, iov) < 0) {
364+
nwarn("Failed to write to remote console socket");
365+
remote_sock_shutdown(remote_sock, SHUT_WR);
376366
}
377367
}
378368

369+
void write_back_to_remote_consoles(stdpipe_t pipe, char *buf, size_t buflen)
370+
{
371+
if (local_mainfd_stdin.readers == NULL)
372+
return;
373+
struct iovec iov[2] = {
374+
{ &pipe, 1 },
375+
{ buf, buflen }
376+
};
377+
writev_iov_t wviov = { 2, 2, iov };
378+
g_ptr_array_foreach(local_mainfd_stdin.readers, write_remote_sock, &wviov);
379+
}
380+
379381
/* Internal */
380382
static gboolean attach_cb(int fd, G_GNUC_UNUSED GIOCondition condition, gpointer user_data)
381383
{

src/conn_sock.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ char *setup_seccomp_socket(const char *socket);
5353
char *setup_attach_socket(void);
5454
void setup_notify_socket(char *);
5555
void schedule_main_stdin_write();
56-
void write_back_to_remote_consoles(stdpipe_t pipe, char *buf, int len);
56+
void write_back_to_remote_consoles(stdpipe_t pipe, char *buf, size_t buflen);
5757
void close_all_readers();
5858

5959
#endif // CONN_SOCK_H

src/ctr_logging.c

Lines changed: 12 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -79,19 +79,13 @@ static size_t syslog_identifier_len;
7979

8080
#define WRITEV_BUFFER_N_IOV 128
8181

82-
typedef struct {
83-
int iovcnt;
84-
struct iovec iov[WRITEV_BUFFER_N_IOV];
85-
} writev_buffer_t;
86-
8782
static void parse_log_path(char *log_config);
8883
static const char *stdpipe_name(stdpipe_t pipe);
8984
static int write_journald(int pipe, char *buf, ssize_t num_read);
9085
static int write_k8s_log(stdpipe_t pipe, const char *buf, ssize_t buflen);
9186
static bool get_line_len(ptrdiff_t *line_len, const char *buf, ssize_t buflen);
92-
static ssize_t writev_buffer_append_segment(int fd, writev_buffer_t *buf, const void *data, ssize_t len);
93-
static ssize_t writev_buffer_append_segment_no_flush(writev_buffer_t *buf, const void *data, ssize_t len);
94-
static ssize_t writev_buffer_flush(int fd, writev_buffer_t *buf);
87+
static ssize_t writev_buffer_append_segment(int fd, writev_iov_t *buf, const void *data, ssize_t len);
88+
static ssize_t writev_buffer_append_segment_no_flush(writev_iov_t *buf, const void *data, ssize_t len);
9589
static void set_k8s_timestamp(char *buf, ssize_t buflen, const char *pipename);
9690
static void reopen_k8s_file(void);
9791
static int parse_priority_prefix(const char *buf, ssize_t buflen, int *priority, const char **message_start);
@@ -386,7 +380,8 @@ static int write_journald(int pipe, char *buf, ssize_t buflen)
386380
ptrdiff_t line_len = 0;
387381

388382
while (buflen > 0 || *partial_buf_len > 0) {
389-
writev_buffer_t bufv = {0};
383+
struct iovec vecs[WRITEV_BUFFER_N_IOV];
384+
writev_iov_t bufv = { 0, WRITEV_BUFFER_N_IOV, vecs };
390385

391386
bool partial = buflen == 0 || get_line_len(&line_len, buf, buflen);
392387

@@ -489,7 +484,8 @@ static int write_k8s_log(stdpipe_t pipe, const char *buf, ssize_t buflen)
489484
static bool stdout_has_partial = false;
490485
static bool stderr_has_partial = false;
491486

492-
writev_buffer_t bufv = {0};
487+
struct iovec vecs[WRITEV_BUFFER_N_IOV];
488+
writev_iov_t bufv = { 0, WRITEV_BUFFER_N_IOV, vecs };
493489
int64_t bytes_to_be_written = 0;
494490

495491
bool *has_partial = (pipe == STDOUT_PIPE) ? &stdout_has_partial : &stderr_has_partial;
@@ -550,7 +546,7 @@ static int write_k8s_log(stdpipe_t pipe, const char *buf, ssize_t buflen)
550546
* a timestamp.
551547
*/
552548
if ((log_size_max > 0) && (k8s_bytes_written + bytes_to_be_written) > log_size_max) {
553-
if (writev_buffer_flush(k8s_log_fd, &bufv) < 0) {
549+
if (writev_all(k8s_log_fd, &bufv) < 0) {
554550
nwarn("failed to flush buffer to log");
555551
}
556552
reopen_k8s_file();
@@ -600,7 +596,7 @@ static int write_k8s_log(stdpipe_t pipe, const char *buf, ssize_t buflen)
600596
buflen -= line_len;
601597
}
602598

603-
if (writev_buffer_flush(k8s_log_fd, &bufv) < 0) {
599+
if (writev_all(k8s_log_fd, &bufv) < 0) {
604600
nwarn("failed to flush buffer to log");
605601
}
606602

@@ -622,66 +618,12 @@ static bool get_line_len(ptrdiff_t *line_len, const char *buf, ssize_t buflen)
622618
return partial;
623619
}
624620

625-
626-
static ssize_t writev_buffer_flush(int fd, writev_buffer_t *buf)
627-
{
628-
ssize_t count = 0;
629-
int iovcnt = buf->iovcnt;
630-
struct iovec *iov = buf->iov;
631-
632-
/*
633-
* By definition, flushing the buffers will either be entirely successful, or will fail at some point
634-
* along the way. There is no facility to attempt to retry a writev() system call outside of an EINTR
635-
* errno. Therefore, no matter the outcome, always reset the writev_buffer_t data structure.
636-
*/
637-
buf->iovcnt = 0;
638-
639-
while (iovcnt > 0) {
640-
ssize_t res;
641-
do {
642-
res = writev(fd, iov, iovcnt);
643-
} while (res == -1 && errno == EINTR);
644-
645-
if (res <= 0) {
646-
/*
647-
* Any unflushed data is lost (this would be a good place to add a counter for how many times
648-
* this occurs and another count for how much data is lost).
649-
*
650-
* Note that if writev() returns a 0, this logic considers it an error.
651-
*/
652-
return -1;
653-
}
654-
655-
count += res;
656-
657-
while (res > 0) {
658-
size_t iov_len = iov->iov_len;
659-
size_t from_this = MIN((size_t)res, iov_len);
660-
res -= from_this;
661-
iov_len -= from_this;
662-
663-
if (iov_len == 0) {
664-
iov++;
665-
iovcnt--;
666-
/* continue, res still > 0 */
667-
} else {
668-
iov->iov_len = iov_len;
669-
iov->iov_base += from_this;
670-
/* break, res is 0 */
671-
}
672-
}
673-
}
674-
675-
return count;
676-
}
677-
678-
679-
ssize_t writev_buffer_append_segment(int fd, writev_buffer_t *buf, const void *data, ssize_t len)
621+
ssize_t writev_buffer_append_segment(int fd, writev_iov_t *buf, const void *data, ssize_t len)
680622
{
681623
if (data == NULL)
682624
return 1;
683625

684-
if (buf->iovcnt == WRITEV_BUFFER_N_IOV && writev_buffer_flush(fd, buf) < 0)
626+
if (buf->iovcnt == buf->max_iovcnt && writev_all(fd, buf) < 0)
685627
return -1;
686628

687629
if (len > 0) {
@@ -693,12 +635,12 @@ ssize_t writev_buffer_append_segment(int fd, writev_buffer_t *buf, const void *d
693635
return 1;
694636
}
695637

696-
ssize_t writev_buffer_append_segment_no_flush(writev_buffer_t *buf, const void *data, ssize_t len)
638+
ssize_t writev_buffer_append_segment_no_flush(writev_iov_t *buf, const void *data, ssize_t len)
697639
{
698640
if (data == NULL)
699641
return 1;
700642

701-
if (buf->iovcnt == WRITEV_BUFFER_N_IOV)
643+
if (buf->iovcnt == buf->max_iovcnt)
702644
return -1;
703645

704646
if (len > 0) {

src/utils.c

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,9 @@ static void get_signal_descriptor_mask(sigset_t *set)
6262
sigprocmask(SIG_BLOCK, set, NULL);
6363
}
6464

65-
ssize_t write_all(int fd, const void *buf, size_t count)
65+
ssize_t write_all(int fd, const void *buf, size_t buflen)
6666
{
67-
size_t remaining = count;
67+
size_t remaining = buflen;
6868
const char *p = buf;
6969
ssize_t res;
7070

@@ -80,6 +80,59 @@ ssize_t write_all(int fd, const void *buf, size_t count)
8080
p += res;
8181
}
8282

83+
return buflen;
84+
}
85+
86+
ssize_t writev_all(int fd, writev_iov_t *buf)
87+
{
88+
ssize_t count = 0;
89+
size_t iovcnt = buf->iovcnt;
90+
struct iovec *iov = buf->iov;
91+
92+
/*
93+
* By definition, flushing the buffers will either be entirely successful, or will fail at some point
94+
* along the way. There is no facility to attempt to retry a writev() system call outside of an retryable
95+
* errno. Therefore, no matter the outcome, always reset the given writev_iov_t data structure.
96+
*/
97+
buf->iovcnt = 0;
98+
99+
while (iovcnt > 0) {
100+
ssize_t res;
101+
do {
102+
res = writev(fd, iov, iovcnt);
103+
} while (res == -1 && errno == EINTR/*retryable_error(errno)*/);
104+
105+
if (res <= 0) {
106+
/*
107+
* Any unflushed data is lost (this would be a good place to add a counter for how many times
108+
* this occurs and another count for how much data is lost).
109+
*
110+
* Note that if writev() returns a 0, this logic considers it an error.
111+
*/
112+
ntracef("writev_all(fd = %d, iovcnt = %lu) errno = %d", fd, iovcnt, errno);
113+
return -1;
114+
}
115+
116+
count += res;
117+
118+
while (res > 0) {
119+
size_t iov_len = iov->iov_len;
120+
size_t from_this = MIN((size_t)res, iov_len);
121+
res -= from_this;
122+
iov_len -= from_this;
123+
124+
if (iov_len == 0) {
125+
iov++;
126+
iovcnt--;
127+
/* continue, res still > 0 */
128+
} else {
129+
iov->iov_len = iov_len;
130+
iov->iov_base += from_this;
131+
/* break, res is 0 */
132+
}
133+
}
134+
}
135+
83136
return count;
84137
}
85138

src/utils.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,13 @@ static inline void hashtable_free_cleanup(GHashTable **tbl)
255255
#define _cleanup_gerror_ _cleanup_(gerror_free_cleanup)
256256

257257

258-
ssize_t write_all(int fd, const void *buf, size_t count);
258+
ssize_t write_all(int fd, const void *buf, size_t buflen);
259+
typedef struct {
260+
size_t iovcnt;
261+
size_t max_iovcnt;
262+
struct iovec *iov;
263+
} writev_iov_t;
264+
ssize_t writev_all(int fd, writev_iov_t *iov);
259265

260266
int set_subreaper(gboolean enabled);
261267

0 commit comments

Comments
 (0)