On 20/06/18(Wed) 17:16, Mark Kettenis wrote:
> > Date: Wed, 20 Jun 2018 15:17:27 +0200
> > From: Martin Pieuchot <[email protected]>
>
> 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 *);