Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 4 Dec 2015 11:32:08 -0500
From:      "Kenneth D. Merry" <ken@FreeBSD.ORG>
To:        Ravi Pokala <rpokala@mac.com>
Cc:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   Re: svn commit: r291716 - in head: share/man/man4 sys/cam sys/cam/ata sys/cam/scsi sys/dev/md sys/geom sys/kern sys/pc98/include sys/sys usr.sbin usr.sbin/camdd
Message-ID:  <20151204163208.GA93141@mithlond.kdm.org>
In-Reply-To: <75635FDB-E85F-4F0A-8EDC-8A29F8A095BE@panasas.com>
References:  <201512032054.tB3KsuUw037541@repo.freebsd.org> <75635FDB-E85F-4F0A-8EDC-8A29F8A095BE@panasas.com>

next in thread | previous in thread | raw e-mail | index | archive | help
On Thu, Dec 03, 2015 at 23:55:14 -0800, Ravi Pokala wrote:
> Hi Ken,
> 
> A few questions:
> 
> >  	Although these ioctls do not have a declared argument, they
> > 	both take a union ccb pointer.  If we declare a size here,
> > 	the ioctl code in sys/kern/sys_generic.c will malloc and free
> > 	a buffer for either the CCB or the CCB pointer (depending on
> > 	how it is declared).  Since we have to keep a copy of the
> > 	CCB (which is fairly large) anyway, having the ioctl malloc
> > 	and free a CCB for each call is wasteful.
> 
> 
> (a) How does that work? That is, how does the argument get to the ioctl handler in the kernel?
> 

In sys_ioctl(), in sys/kern/sys_generic.c, the pointer argument ("data") to
the ioctl syscall is passed through into kern_ioctl() and then on down
until it gets into the passioctl() call.  It is passed through even when
the declared size of the ioctl is 0, as it is for the two new ioctls:

/*
 * These two ioctls take a union ccb *, but that is not explicitly declared
 * to avoid having the ioctl handling code malloc and free their own copy
 * of the CCB or the CCB pointer.
 */
#define CAMIOQUEUE      _IO(CAM_VERSION, 4)
#define CAMIOGET        _IO(CAM_VERSION, 5)

Here's the code in question:

        if (size > 0) {
                if (com & IOC_VOID) {
                        /* Integer argument. */
                        arg = (intptr_t)uap->data;
                        data = (void *)&arg;
                        size = 0;
                } else {
                        if (size > SYS_IOCTL_SMALL_SIZE)
                                data = malloc((u_long)size, M_IOCTLOPS, M_WAITOK
);      
                        else
                                data = smalldata;
                }
        } else
                data = (void *)&uap->data;

So in the size == 0 case, data is just passed through as is.

Prior to r274017, if the ioctl were declared as _IOWR, there would be a
malloc and copyin of however much data is declared in the ioctl, no matter
what the size.  So, in this case, sizeof(union ccb *) or sizeof(union ccb).

The problem is, upon exit from the ioctl, that data is freed.  With a
queueing interface, we need to keep a copy of the CCB around after the
ioctl exits.  You have the same problem even after r274017, because that
just provides a small buffer on the stack.  (And would only help in the
pointer case.  And we don't need to copyin the pointer.)

So, to avoid that, we don't declare an argument, but we do pass in a
pointer and do the copy the user's CCB into a CCB that is allocated inside
the pass(4) driver.

> (b) The CCB is large, but the CCB pointer is just a pointer; shouldn't that be passed in as the arg?
> 

It is.  Here's what camdd(8) does:

>From camdd_pass_run():

        union ccb *ccb;
...
        /*
         * Queue the CCB to the pass(4) driver.
         */
        if (ioctl(pass_dev->dev->fd, CAMIOQUEUE, ccb) == -1) {

And from camdd_pass_fetch():

        union ccb ccb;
...
        while ((retval = ioctl(pass_dev->dev->fd, CAMIOGET, &ccb)) != -1) {

Ken
-- 
Kenneth Merry
ken@FreeBSD.ORG



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20151204163208.GA93141>