On 20/06/18(Wed) 17:16, Mark Kettenis wrote:
> > Date: Wed, 20 Jun 2018 15:17:27 +0200
> > From: Martin Pieuchot <m...@openbsd.org>
> 
> Joel, the diff below changes the semantics of the DIOCMAP ioctl a bit.
> See the bit about DIOCMAP below.  I think this approach is better.
> But maybe I'm missing someting...
> 
> Martin, I like fnew().  I can use that in the prime/DRI3 stuff as
> well.  I'm still not convinced that inserting the struct file into the
> list late is the right choice.  Yes, inserting it early (in fnew())
> means that tools like fstat may see a struct file that is in an
> inconsistent state.  But I'm not sure we can guarantee that anyway.

Let's see how we can insert the struct file early once visa@'s locking
is in.  Does it sound like a plan?

> That would allow you to atomically replace the pointer in the file
> descriptor table and be done.  Actually I think you could already
> simplify the code and skip the "Zap old file" bit.  Just do a
> 
>    fdinsert(fdp, fd, 0, fp)
> 
> which will retain the flags.

To be able to do that I have to remove the KASSERT() in fdinsert().
This doesn't make me happy because I like to assert that the given slot
is unused.

I could have used fdremove() instead of fiddling with the array directly
but then I'd have to export & call fd_used().  So for the moment I'd
prefer to keep this code "stupid", hopefully we can design a better API
to manipulate the arrays.

> Now if you really prefer to keep inserting late, could we change
> fdinsert to only insert if FIF_INSERTED isn't set already?  That way
> we can use it to insert the same open file into multiple file
> descriptor tables.  I can include such a change in my prime/DRI3 diff.

Sure, updated diff below.

Index: dev/diskmap.c
===================================================================
RCS file: /cvs/src/sys/dev/diskmap.c,v
retrieving revision 1.20
diff -u -p -r1.20 diskmap.c
--- dev/diskmap.c       9 May 2018 08:42:02 -0000       1.20
+++ dev/diskmap.c       20 Jun 2018 13:14:06 -0000
@@ -57,9 +57,9 @@ diskmapioctl(dev_t dev, u_long cmd, cadd
        struct dk_diskmap *dm;
        struct nameidata ndp;
        struct filedesc *fdp = p->p_fd;
-       struct file *fp = NULL;
-       struct vnode *vp = NULL, *ovp;
-       char *devname;
+       struct file *fp0 = NULL, *fp = NULL;
+       struct vnode *vp = NULL;
+       char *devname, flags;
        int fd, error = EINVAL;
 
        if (cmd != DIOCMAP)
@@ -80,57 +80,59 @@ diskmapioctl(dev_t dev, u_long cmd, cadd
                        goto invalid;
 
        /* Attempt to open actual device. */
-       if ((error = getvnode(p, fd, &fp)) != 0)
+       if ((error = getvnode(p, fd, &fp0)) != 0)
                goto invalid;
 
-       KASSERT(fp->f_type == DTYPE_VNODE);
-       KASSERT(fp->f_ops == &vnops);
-
-       fdplock(fdp);
-
        NDINIT(&ndp, 0, 0, UIO_SYSSPACE, devname, p);
        ndp.ni_pledge = PLEDGE_RPATH;
-       if ((error = vn_open(&ndp, fp->f_flag, 0)) != 0)
+       if ((error = vn_open(&ndp, fp0->f_flag, 0)) != 0)
                goto bad;
 
        vp = ndp.ni_vp;
+       VOP_UNLOCK(vp);
 
-       /* Close the original vnode. */
-       ovp = (struct vnode *)fp->f_data;
-       if (fp->f_flag & FWRITE)
-               ovp->v_writecount--;
-
-       if (ovp->v_writecount == 0) {
-               vn_lock(ovp, LK_EXCLUSIVE | LK_RETRY);
-               VOP_CLOSE(ovp, fp->f_flag, p->p_ucred, p);
-               vput(ovp);
+       fdplock(fdp);
+       /* We lost a race, don't go any further. */
+       if (fdp->fd_ofiles[fd] != fp0) {
+               error = EAGAIN;
+               goto bad;
        }
 
-       fp->f_data = (caddr_t)vp;
-       fp->f_offset = 0;
-       mtx_enter(&fp->f_mtx);
-       fp->f_rxfer = 0;
-       fp->f_wxfer = 0;
-       fp->f_seek = 0;
-       fp->f_rbytes = 0;
-       fp->f_wbytes = 0;
-       mtx_leave(&fp->f_mtx);
-
-       VOP_UNLOCK(vp);
+       fp = fnew(p);
+       if (fp == NULL) {
+               error = ENFILE;
+               goto bad;
+       }
 
-       FRELE(fp, p);
+       /* Zap old file. */
+       mtx_enter(&fhdlk);
+       KASSERT(fdp->fd_ofiles[fd] == fp0);
+       flags = fdp->fd_ofileflags[fd];
+       fdp->fd_ofiles[fd] = NULL;
+       fdp->fd_ofileflags[fd] = 0;
+       mtx_leave(&fhdlk);
+
+       /* Insert new file. */
+       fp->f_flag = fp0->f_flag;
+       fp->f_type = DTYPE_VNODE;
+       fp->f_ops = &vnops;
+       fp->f_data = (caddr_t)vp;
+       fdinsert(fdp, fd, flags, fp);
        fdpunlock(fdp);
+       FRELE(fp, p);
+
+       closef(fp0, p);
        free(devname, M_DEVBUF, PATH_MAX);
 
        return 0;
 
 bad:
-       if (vp)
-               vput(vp);
-       if (fp)
-               FRELE(fp, p);
-
        fdpunlock(fdp);
+
+       if (vp)
+               vrele(vp);
+       if (fp0)
+               FRELE(fp0, p);
 
 invalid:
        free(devname, M_DEVBUF, PATH_MAX);
Index: kern/kern_descrip.c
===================================================================
RCS file: /cvs/src/sys/kern/kern_descrip.c,v
retrieving revision 1.167
diff -u -p -r1.167 kern_descrip.c
--- kern/kern_descrip.c 20 Jun 2018 10:52:49 -0000      1.167
+++ kern/kern_descrip.c 21 Jun 2018 07:48:20 -0000
@@ -686,15 +686,17 @@ fdinsert(struct filedesc *fdp, int fd, i
        fdpassertlocked(fdp);
 
        mtx_enter(&fhdlk);
-       if ((fq = fdp->fd_ofiles[0]) != NULL) {
-               LIST_INSERT_AFTER(fq, fp, f_list);
-       } else {
-               LIST_INSERT_HEAD(&filehead, fp, f_list);
+       if ((fp->f_iflags & FIF_INSERTED) == 0) {
+               fp->f_iflags |= FIF_INSERTED;
+               if ((fq = fdp->fd_ofiles[0]) != NULL) {
+                       LIST_INSERT_AFTER(fq, fp, f_list);
+               } else {
+                       LIST_INSERT_HEAD(&filehead, fp, f_list);
+               }
        }
        KASSERT(fdp->fd_ofiles[fd] == NULL);
        fdp->fd_ofiles[fd] = fp;
        fdp->fd_ofileflags[fd] |= (flags & UF_EXCLOSE);
-       fp->f_iflags |= FIF_INSERTED;
        mtx_leave(&fhdlk);
 }
 
@@ -971,11 +973,29 @@ restart:
                }
                return (error);
        }
-       if (numfiles >= maxfiles) {
+
+       fp = fnew(p);
+       if (fp == NULL) {
                fd_unused(p->p_fd, i);
-               tablefull("file");
                return (ENFILE);
        }
+
+       *resultfp = fp;
+       *resultfd = i;
+
+       return (0);
+}
+
+struct file *
+fnew(struct proc *p)
+{
+       struct file *fp;
+
+       if (numfiles >= maxfiles) {
+               tablefull("file");
+               return (NULL);
+       }
+
        /*
         * Allocate a new file descriptor.
         * If the process has file descriptor zero open, add to the list
@@ -992,14 +1012,12 @@ restart:
        fp->f_count = 1;
        fp->f_cred = p->p_ucred;
        crhold(fp->f_cred);
-       *resultfp = fp;
-       *resultfd = i;
 
        mtx_enter(&fhdlk);
        fp->f_count++;
        mtx_leave(&fhdlk);
 
-       return (0);
+       return (fp);
 }
 
 /*
Index: sys/filedesc.h
===================================================================
RCS file: /cvs/src/sys/sys/filedesc.h,v
retrieving revision 1.39
diff -u -p -r1.39 filedesc.h
--- sys/filedesc.h      18 Jun 2018 09:15:05 -0000      1.39
+++ sys/filedesc.h      20 Jun 2018 11:49:07 -0000
@@ -120,6 +120,7 @@ void        filedesc_init(void);
 int    dupfdopen(struct proc *, int, int);
 int    fdalloc(struct proc *p, int want, int *result);
 void   fdexpand(struct proc *);
+struct file *fnew(struct proc *_p);
 int    falloc(struct proc *_p, struct file **_rfp, int *_rfd);
 struct filedesc *fdinit(void);
 struct filedesc *fdshare(struct process *);

Reply via email to