# Question about uiomove



## topcat (Mar 9, 2017)

Hi, I have been reading Joseph Kong's book: "FreeBSD Device Drivers - A Guide for the Intrepid". I have a confusion about the way uiomove() is being used in the code examples, such as the xxx_read() and xxx_write() routines.

As I understand, from reading /usr/src/sys/kern/subr_uio.c around line 248, uiomove() does not offset the kernel buffer automatically. The programmer has to add the offset from the uio structure. In the xxx_read() code in the book, this offset is applied correctly.

However, in all the xxx_write() examples, the amount to be written is calculated assuming the write is going to happen at a certain offset in the buffer, but in the actual call to uiomove() the offset is not applied. Is this a typo or am I missing something here?

Thanks for reading; hopefully somewhere here knows the answer.


----------



## SirDice (Mar 10, 2017)

For the people that don't have access to the book (and therefor cannot review the examples) but may know the answer, can you post a small example?


----------



## topcat (Mar 10, 2017)

Yes, sorry should have done this already:


```
xxx_write(...)
{
...
amount = MIN(uio->uio_resid,  // 0. This is the no of bytes in the uio left to write
             (BUFFER_SIZE - 1 - uio->uio_offset > 0) ?
             BUFFER_SIZE - 1 - uio->uio_offset : 0); // 1. amount to write is computed
//assuming write will happen at uio_offset in sc_buffer
if (amount == 0)
  return (error);
error = uiomove(sc->sc_buffer, amount, uio);  // 2. sc_buffer is not offset
...
}

xxx_read(...)
{
...
amount = MIN(uio->uio_resid,
             (sc->sc_length - uio->uio_offset > 0) ?
             sc->sc_length - uio->uio_offset : 0); // 3. Same as comment 1 above
error = uiomove(sc->sc_buffer + uio->uio_offset, amount, uio); // 4. Here buffer is offset
...
}
```
Hope this makes it clear.


----------



## Pegasus711 (Apr 20, 2017)

I don't see that part in the book. To be specific, I am looking at the echo module in chapter 1 and echo_write is as follows:


```
static int
echo_write(struct cdev *dev, struct uio *uio, int ioflag)
{
        int error = 0;

        error = copyin(uio->uio_iov->iov_base, echo_message->buffer,
            MIN(uio->uio_iov->iov_len, BUFFER_SIZE - 1));
        if (error != 0) {
                uprintf("Write failed.\n");
                return (error);
        }

        *(echo_message->buffer +
            MIN(uio->uio_iov->iov_len, BUFFER_SIZE - 1)) = 0;

        echo_message->length = MIN(uio->uio_iov->iov_len, BUFFER_SIZE - 1);

        return (error);
}
```

Here it purposely uses the easier to understand copyin function. However I re-wrote that function using uiomove like so (purposely having it copy in small chunks just for fun):

```
static int
echo_write(struct cdev *dev, struct uio *uio, int ioflag)
{
        int error = 0;
    //unsigned int CHUNK_SIZE = 10;

    unsigned int CharstoCopy = 0;
    char *bufferPtr = echo_message->buffer;

    while (uio->uio_resid > 0) {
        if ( echo_message->length < (BUFFER_SIZE - 1)) {
            // how many can we move?
            CharstoCopy = MIN(uio->uio_resid, BUFFER_SIZE - 1 - echo_message->length);
            error = uiomove(bufferPtr, CharstoCopy, uio);
            if (error)
                break;
            echo_message->length += CharstoCopy;
            uprintf("chars still available in the uio %lu\n", uio->uio_resid);
            bufferPtr += CharstoCopy;
        }
    }
        if (error != 0) {
                uprintf("Write failed.\n");
                return (error);
        }
}
```

So yes we have to make sure it points to the correct place in the kernel buffer. However the echo_read function goes like:

```
static int
echo_read(struct cdev *dev, struct uio *uio, int ioflag)
{
        int error = 0;
        int amount;

        amount = MIN(uio->uio_resid,
            (echo_message->length - uio->uio_offset > 0) ?
             echo_message->length - uio->uio_offset : 0);

        error = uiomove(echo_message->buffer + uio->uio_offset, amount, uio);
        if (error != 0)
                uprintf("Read failed.\n");

        return (error);
}
```
 So even here, we are apparently adding the offset to the kernel buffer while copying it so I don't see where the inconsistency is. The offset is always adjusted after every move.

Or perhaps we are looking at different files? Which chapter/code listing by the way? I have the book so I can look it up.


----------



## topcat (May 9, 2017)

Thanks for the very detailed reply! Sorry, I missed your response for some reason. Your code looks correct to me, and yes the offset is indeed correctly applied. I'll post the exact chapter/example when I get back to my copy. It's one of the later examples.


----------



## Pegasus711 (May 9, 2017)

Hay topcat You should "watch" any thread that you are interested in so that you get an email notification whenever someone replies. Keen to hear from you. I am also becoming more interested in getting to learn about the Drivers as a first step to understanding the kernel. Perhaps we could co-ordinate our efforts in learning in case you are interested that way we both might benefit from each other's perspectives.

Just a thought


----------



## topcat (May 9, 2017)

Thanks for the suggestion. I usually do watch but missed this one 

Look at the implementation of echo_read and echo_write on page 31. This works okay if only one write to the buffer happens. Otherwise, uio_offset should be added to the buffer pointer and the length variable incremented like in your example. Note that the amount variable is computed correctly. Coordinating sounds good 

Incidentally I got a reply from John Baldwin on freebsd-drivers and he agrees.


----------



## Pegasus711 (May 10, 2017)

topcat said:


> Look at the implementation of echo_read and echo_write on page 31. This works okay if only one write to the buffer happens. Otherwise, uio_offset should be added to the buffer pointer and the length variable incremented like in your example.


May be perhaps the entire buffer is drained at once from the user space? Nonetheless for very large writes the devfs subsystem will anyhow break down the write request into smaller chunks before it writes to the said device so the pointer must be nonetheless incremented after every write and perhaps behave like a circular buffer in this very simple example (since we aren't dealing with anything real here anyways ). By the way, I can give a heads up to Joseph regarding this (on Twitter) and perhaps understand his rationale as to why he did the way he did here. Do you want to do the honors or should I?


topcat said:


> Coordinating sounds good
> Incidentally I got a reply from John Baldwin on freebsd-drivers and he agrees.


Would love to, if you are really interested. Please message me so we could hopefully agree on a method which suits us both. Do you have a blog or something? What's your timezone? Keen to hear


----------



## topcat (May 10, 2017)

Pegasus711 said:


> Do you want to do the honors or should I?


Please, go ahead 



Pegasus711 said:


> Would love to, if you are really interested. Please message me so we could hopefully agree on a method which suits us both. Do you have a blog or something? What's your timezone? Keen to hear


Let's chat more about this! I'm in PDT (Vancouver, BC).


----------



## Pegasus711 (May 11, 2017)

Oh so we are a good 12+ hours apart. I'm from India (GMT +5:30). Nonetheless we live in a small world so co-ordinating shouldn't be much of a problem since both our wall clocks read the same (apart from the am/pm stuff ).

Where do I message you? What's your chat handle? gmail?


----------



## topcat (May 15, 2017)

I'm from India originally as well . You can reach me here or at anindya49 at hotmail.com


----------



## Pegasus711 (May 17, 2017)

cool! Will do bro!


----------

