diff --git a/sys/fs/devfs/devfs_int.h b/sys/fs/devfs/devfs_int.h index 51c3625..c7a1102 100644 --- a/sys/fs/devfs/devfs_int.h +++ b/sys/fs/devfs/devfs_int.h @@ -39,6 +39,13 @@ struct devfs_dirent; +struct cdev_privdata { + struct file *cdpd_fp; + void *cdpd_data; + void (*cdpd_dtr)(void *); + LIST_ENTRY(cdev_privdata) cdpd_list; +}; + struct cdev_priv { struct cdev cdp_c; TAILQ_ENTRY(cdev_priv) cdp_list; @@ -57,17 +64,21 @@ struct cdev_priv { TAILQ_ENTRY(cdev_priv) cdp_dtr_list; void (*cdp_dtr_cb)(void *); void *cdp_dtr_cb_arg; + + LIST_HEAD(, cdev_privdata) cdp_fdpriv; }; struct cdev *devfs_alloc(void); void devfs_free(struct cdev *); void devfs_create(struct cdev *dev); void devfs_destroy(struct cdev *dev); +void devfs_destroy_cdevpiv(struct cdev_privdata *p); extern struct unrhdr *devfs_inos; extern struct mtx devmtx; extern struct mtx devfs_de_interlock; extern struct sx clone_drain_lock; +extern struct mtx cdevpriv_mtx; extern TAILQ_HEAD(cdev_priv_list, cdev_priv) cdevp_list; #endif /* _KERNEL */ diff --git a/sys/fs/devfs/devfs_vnops.c b/sys/fs/devfs/devfs_vnops.c index 598bcb9..5caadc0 100644 --- a/sys/fs/devfs/devfs_vnops.c +++ b/sys/fs/devfs/devfs_vnops.c @@ -73,10 +73,14 @@ static struct fileops devfs_ops_f; #include +static MALLOC_DEFINE(M_CDEVPDATA, "DEVFSP", "Metainfo for cdev-fp data"); + struct mtx devfs_de_interlock; MTX_SYSINIT(devfs_de_interlock, &devfs_de_interlock, "devfs interlock", MTX_DEF); struct sx clone_drain_lock; SX_SYSINIT(clone_drain_lock, &clone_drain_lock, "clone events drain lock"); +struct mtx cdevpriv_mtx; +MTX_SYSINIT(cdevpriv_mtx, &cdevpriv_mtx, "cdevpriv lock", MTX_DEF); static int devfs_fp_check(struct file *fp, struct cdev **devp, struct cdevsw **dswp) @@ -92,9 +96,97 @@ devfs_fp_check(struct file *fp, struct cdev **devp, struct cdevsw **dswp) ("devfs: un-referenced struct cdev *(%s)", devtoname(*devp))); if (*dswp == NULL) return (ENXIO); + curthread->td_fpop = fp; return (0); } +int +devfs_get_cdevpriv(void **datap) +{ + struct file *fp; + struct cdev_privdata *p; + int error; + + fp = curthread->td_fpop; + if (fp == NULL) + return (EBADF); + mtx_lock(&cdevpriv_mtx); + p = fp->f_cdevpriv; + mtx_unlock(&cdevpriv_mtx); + if (p != NULL) { + error = 0; + *datap = p->cdpd_data; + } else + error = ENOENT; + return (error); +} + +int +devfs_set_cdevpriv(void *priv, cdevpriv_dtr_t priv_dtr) +{ + struct file *fp; + struct cdev_priv *cdp; + struct cdev_privdata *p; + int error; + + fp = curthread->td_fpop; + if (fp == NULL) + return (ENOENT); + cdp = ((struct cdev *)fp->f_data)->si_priv; + p = malloc(sizeof(struct cdev_privdata), M_CDEVPDATA, M_WAITOK); + p->cdpd_data = priv; + p->cdpd_dtr = priv_dtr; + p->cdpd_fp = fp; + mtx_lock(&cdevpriv_mtx); + if (fp->f_cdevpriv == NULL) { + LIST_INSERT_HEAD(&cdp->cdp_fdpriv, p, cdpd_list); + fp->f_cdevpriv = p; + mtx_unlock(&cdevpriv_mtx); + error = 0; + } else { + mtx_unlock(&cdevpriv_mtx); + free(p, M_CDEVPDATA); + error = EBUSY; + } + return (error); +} + +void +devfs_destroy_cdevpiv(struct cdev_privdata *p) +{ + + mtx_assert(&cdevpriv_mtx, MA_OWNED); + p->cdpd_fp->f_cdevpriv = NULL; + LIST_REMOVE(p, cdpd_list); + mtx_unlock(&cdevpriv_mtx); + (p->cdpd_dtr)(p->cdpd_data); + free(p, M_CDEVPDATA); +} + +void +devfs_fpdrop(struct file *fp) +{ + struct cdev_privdata *p; + + mtx_lock(&cdevpriv_mtx); + if ((p = fp->f_cdevpriv) == NULL) { + mtx_unlock(&cdevpriv_mtx); + return; + } + devfs_destroy_cdevpiv(p); +} + +void +devfs_clear_cdevpriv(void) +{ + struct file *fp; + + fp = curthread->td_fpop; + if (fp == NULL) + return; + devfs_fpdrop(fp); +} + /* * Construct the fully qualified path name relative to the mountpoint */ @@ -374,8 +466,12 @@ devfs_close(struct vop_close_args *ap) static int devfs_close_f(struct file *fp, struct thread *td) { + int error; - return (vnops.fo_close(fp, td)); + curthread->td_fpop = fp; + error = vnops.fo_close(fp, td); + curthread->td_fpop = NULL; + return (error); } /* ARGSUSED */ @@ -472,6 +568,7 @@ devfs_ioctl_f(struct file *fp, u_long com, void *data, struct ucred *cred, struc if (com == FIODTYPE) { *(int *)data = dsw->d_flags & D_TYPEMASK; + td->td_fpop = NULL; dev_relthread(dev); return (0); } else if (com == FIODGNAME) { @@ -482,10 +579,12 @@ devfs_ioctl_f(struct file *fp, u_long com, void *data, struct ucred *cred, struc error = EINVAL; else error = copyout(p, fgn->buf, i); + td->td_fpop = NULL; dev_relthread(dev); return (error); } error = dsw->d_ioctl(dev, com, data, fp->f_flag, td); + td->td_fpop = NULL; dev_relthread(dev); if (error == ENOIOCTL) error = ENOTTY; @@ -529,6 +628,7 @@ devfs_kqfilter_f(struct file *fp, struct knote *kn) if (error) return (error); error = dsw->d_kqfilter(dev, kn); + curthread->td_fpop = NULL; dev_relthread(dev); return (error); } @@ -766,10 +866,15 @@ devfs_open(struct vop_open_args *ap) VOP_UNLOCK(vp, 0); + if (fp != NULL) { + td->td_fpop = fp; + fp->f_data = dev; + } if (dsw->d_fdopen != NULL) error = dsw->d_fdopen(dev, ap->a_mode, td, fp); else error = dsw->d_open(dev, ap->a_mode, S_IFCHR, td); + td->td_fpop = NULL; vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); @@ -825,6 +930,7 @@ devfs_poll_f(struct file *fp, int events, struct ucred *cred, struct thread *td) if (error) return (error); error = dsw->d_poll(dev, events, td); + curthread->td_fpop = NULL; dev_relthread(dev); return(error); } @@ -862,6 +968,7 @@ devfs_read_f(struct file *fp, struct uio *uio, struct ucred *cred, int flags, st error = dsw->d_read(dev, uio, ioflag); if (uio->uio_resid != resid || (error == 0 && resid != 0)) vfs_timestamp(&dev->si_atime); + curthread->td_fpop = NULL; dev_relthread(dev); if ((flags & FOF_OFFSET) == 0) @@ -1298,6 +1405,7 @@ devfs_write_f(struct file *fp, struct uio *uio, struct ucred *cred, int flags, s vfs_timestamp(&dev->si_ctime); dev->si_mtime = dev->si_ctime; } + curthread->td_fpop = NULL; dev_relthread(dev); if ((flags & FOF_OFFSET) == 0) diff --git a/sys/kern/kern_conf.c b/sys/kern/kern_conf.c index e911913..7cb6036 100644 --- a/sys/kern/kern_conf.c +++ b/sys/kern/kern_conf.c @@ -801,6 +801,7 @@ static void destroy_devl(struct cdev *dev) { struct cdevsw *csw; + struct cdev_privdata *p, *p1; mtx_assert(&devmtx, MA_OWNED); KASSERT(dev->si_flags & SI_NAMED, @@ -830,6 +831,14 @@ destroy_devl(struct cdev *dev) dev->si_refcount++; /* Avoid race with dev_rel() */ csw = dev->si_devsw; dev->si_devsw = NULL; /* already NULL for SI_ALIAS */ + dev_unlock(); + mtx_lock(&cdevpriv_mtx); + LIST_FOREACH_SAFE(p, &dev->si_priv->cdp_fdpriv, cdpd_list, p1) { + devfs_destroy_cdevpiv(p); + mtx_lock(&cdevpriv_mtx); + } + mtx_unlock(&cdevpriv_mtx); + dev_lock(); while (csw != NULL && csw->d_purge != NULL && dev->si_threadcount) { csw->d_purge(dev); msleep(csw, &devmtx, PRIBIO, "devprg", hz/10); diff --git a/sys/kern/kern_descrip.c b/sys/kern/kern_descrip.c index c440832..6a28c90 100644 --- a/sys/kern/kern_descrip.c +++ b/sys/kern/kern_descrip.c @@ -2221,6 +2221,7 @@ _fdrop(struct file *fp, struct thread *td) panic("fdrop: count %d", fp->f_count); if (fp->f_ops != &badfileops) error = fo_close(fp, td); + devfs_fpdrop(fp); atomic_subtract_int(&openfiles, 1); crfree(fp->f_cred); uma_zfree(file_zone, fp); diff --git a/sys/sys/conf.h b/sys/sys/conf.h index 22d7abc..dbd9d0e 100644 --- a/sys/sys/conf.h +++ b/sys/sys/conf.h @@ -281,6 +281,12 @@ int unit2minor(int _unit); u_int minor2unit(u_int _minor); void setconf(void); +typedef void (*cdevpriv_dtr_t)(void *data); +int devfs_get_cdevpriv(void **datap); +int devfs_set_cdevpriv(void *priv, cdevpriv_dtr_t dtr); +void devfs_clear_cdevpriv(void); +void devfs_fpdrop(struct file *fp); /* XXX This is not public KPI */ + #define UID_ROOT 0 #define UID_BIN 3 #define UID_UUCP 66 diff --git a/sys/sys/file.h b/sys/sys/file.h index 6833445..878ef80 100644 --- a/sys/sys/file.h +++ b/sys/sys/file.h @@ -104,6 +104,7 @@ struct fileops { * Below is the list of locks that protects members in struct file. * * (f) protected with mtx_lock(mtx_pool_find(fp)) + * (d) cdevpriv_mtx * none not locked */ @@ -121,6 +122,7 @@ struct file { */ int f_seqcount; /* Count of sequential accesses. */ off_t f_nextoff; /* next expected read/write offset. */ + struct cdev_privdata *f_cdevpriv; /* (d) Private data for the cdev. */ /* * DFLAG_SEEKABLE specific fields */ diff --git a/sys/sys/proc.h b/sys/sys/proc.h index 358dcbc..d83c3f7 100644 --- a/sys/sys/proc.h +++ b/sys/sys/proc.h @@ -229,6 +229,7 @@ struct thread { u_long td_profil_addr; /* (k) Temporary addr until AST. */ u_int td_profil_ticks; /* (k) Temporary ticks until AST. */ char td_name[MAXCOMLEN + 1]; /* (*) Thread name. */ + struct file *td_fpop; /* (k) file referencing cdev under op */ #define td_endzero td_base_pri /* Copied during fork1() or thread_sched_upcall(). */