How do I fill out the iov_iter structure for use with msghdr?

Viewed 1669

I am trying to send a UDP packet from a driver using sock_sendmsg().

There are several guides on how to use sock_sendmsg() and msghdr to fill out at a UDP packet and transmit it, but they are all for older kernel versions. Sometime between 3.13 and 4.8, the struct msghdr changed from:

struct msghdr {
    void    *   msg_name;   /* Socket name          */
    int     msg_namelen;    /* Length of name       */
    struct iovec *  msg_iov;    /* Data blocks          */
    __kernel_size_t msg_iovlen; /* Number of blocks     */
    void    *   msg_control;    /* Per protocol magic (eg BSD file descriptor passing) */
    __kernel_size_t msg_controllen; /* Length of cmsg list */
    unsigned int    msg_flags;
};

To:

struct msghdr {
    void        *msg_name;  /* ptr to socket address structure */
    int     msg_namelen;    /* size of socket address structure */
    struct iov_iter msg_iter;   /* data */
    void        *msg_control;   /* ancillary data */
    __kernel_size_t msg_controllen; /* ancillary data buffer length */
    unsigned int    msg_flags;  /* flags on received message */
    struct kiocb    *msg_iocb;  /* ptr to iocb for async requests */
};

Instead of setting msg_iov to point to an iovec with the data:

struct msghdr msg = {};
struct iovec iov = {}; 

iov.iov_base = data;
iov.iov_len = dataLen;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;

There is now an iov_iter structure:

struct iov_iter {
    int type;
    size_t iov_offset;
    size_t count;
    union {
        const struct iovec *iov;
        const struct kvec *kvec;
        const struct bio_vec *bvec;
        struct pipe_inode_info *pipe;
    };
    union {
        unsigned long nr_segs;
        struct {
            int idx;
            int start_idx;
        };
    };
};

How do I fill out the iov_iter structure inside of msghdr with data so that I can pass it to sock_sendmsg?

3 Answers

After some playing around and reading the guide posted by Ctx (lwn.net/Articles/625077), I figured it out.

unsigned long nr_segments = 1;
size_t count = 1;
struct msghdr msg = {};
struct iovec iov = {}; 

iov.iov_base = data;
iov.iov_len = dataLen;

iov_iter_init(&msg.msg_iter, READ, &iov, nr_segments, count);

Adding to charlesw's answer, I think that count (the last argument for iov_iter_init()) should be set to dataLen (same value as iov.iov_len). According to the guide posted by Ctx: "The total amount of data pointed to by the iovec array is stored in count".

Such use of iov_iter_init() can be found in numerous places in the kernel, see for example sources for FSI device driver:

struct iov_iter resp_iter;
struct iovec resp_iov;

...

/* Prepare iov iterator */
resp_iov.iov_base = buf;
resp_iov.iov_len = len;
iov_iter_init(&resp_iter, WRITE, &resp_iov, 1, len);

Taking this into consideration, minimal working example would look like this:

unsigned long nr_segments = 1;
size_t count = dataLen;
struct msghdr msg = {};
struct iovec iov = {}; 

iov.iov_base = data;
iov.iov_len = dataLen;

iov_iter_init(&msg.msg_iter, READ, &iov, nr_segments, count);

In my case, without this adjustment sock_sendmsg() was sending only 1 byte of data.

Here is an example:

static int rk_sendbuff(struct socket *sock, char *buffer, int length)
{
    struct msghdr msg;
    struct iovec iov;
    int len = 0;

    iov.iov_base = (char*) buffer; 
    iov.iov_len = (__kernel_size_t) length;

    msg.msg_flags = MSG_NOSIGNAL;

    #if LINUX_VERSION_CODE < KERNEL_VERSION(3,19,0)
       msg.msg_iov = &iov;
       msg.msg_iovlen = 1;
    #else
      iov_iter_init(&msg.msg_iter, WRITE, &iov, 1, length);
    #endif
    #if LINUX_VERSION_CODE < KERNEL_VERSION(4,1,0)
        len = sock_sendmsg(sock,&msg,length);
    #else
        len = sock_sendmsg(sock, &msg);
    #endif

    return len;
}
Related