Fixes for refcounting "struct linux_file" in the LinuxKPI.
- Allow "struct linux_file" to be refcounted when its "_file" member is NULL by using its "f_count" field. The reference counts are transferred to the file structure when the file descriptor is installed. - Add missing vdrop() calls for error cases during open(). - Set the "_file" member of "struct linux_file" during open. This allows use of refcounting through get_file() and fput() with LinuxKPI character devices. MFC after: 1 week Sponsored by: Mellanox Technologies
This commit is contained in:
parent
b7e7cccc2e
commit
f87686da6d
@ -2,7 +2,7 @@
|
||||
* Copyright (c) 2010 Isilon Systems, Inc.
|
||||
* Copyright (c) 2010 iX Systems, Inc.
|
||||
* Copyright (c) 2010 Panasas, Inc.
|
||||
* Copyright (c) 2013-2015 Mellanox Technologies, Ltd.
|
||||
* Copyright (c) 2013-2017 Mellanox Technologies, Ltd.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@ -60,19 +60,24 @@ linux_fget(unsigned int fd)
|
||||
return (struct linux_file *)file->f_data;
|
||||
}
|
||||
|
||||
extern void linux_file_free(struct linux_file *filp);
|
||||
|
||||
static inline void
|
||||
fput(struct linux_file *filp)
|
||||
{
|
||||
if (filp->_file == NULL) {
|
||||
kfree(filp);
|
||||
return;
|
||||
}
|
||||
if (refcount_release(&filp->_file->f_count)) {
|
||||
_fdrop(filp->_file, curthread);
|
||||
kfree(filp);
|
||||
if (refcount_release(filp->_file == NULL ?
|
||||
&filp->f_count : &filp->_file->f_count)) {
|
||||
linux_file_free(filp);
|
||||
}
|
||||
}
|
||||
|
||||
static inline unsigned int
|
||||
file_count(struct linux_file *filp)
|
||||
{
|
||||
return (filp->_file == NULL ?
|
||||
filp->f_count : filp->_file->f_count);
|
||||
}
|
||||
|
||||
static inline void
|
||||
put_unused_fd(unsigned int fd)
|
||||
{
|
||||
@ -106,6 +111,10 @@ fd_install(unsigned int fd, struct linux_file *filp)
|
||||
} else {
|
||||
filp->_file = file;
|
||||
finit(file, filp->f_mode, DTYPE_DEV, filp, &linuxfileops);
|
||||
|
||||
/* transfer reference count from "filp" to "file" */
|
||||
while (refcount_release(&filp->f_count) == 0)
|
||||
refcount_acquire(&file->f_count);
|
||||
}
|
||||
|
||||
/* drop the extra reference */
|
||||
@ -150,6 +159,8 @@ alloc_file(int mode, const struct file_operations *fops)
|
||||
filp = kzalloc(sizeof(*filp), GFP_KERNEL);
|
||||
if (filp == NULL)
|
||||
return (NULL);
|
||||
|
||||
filp->f_count = 1;
|
||||
filp->f_op = fops;
|
||||
filp->f_mode = mode;
|
||||
|
||||
@ -171,7 +182,7 @@ static inline struct fd fdget(unsigned int fd)
|
||||
return (struct fd){f};
|
||||
}
|
||||
|
||||
#define file linux_file
|
||||
#define fget linux_fget
|
||||
#define file linux_file
|
||||
#define fget(...) linux_fget(__VA_ARGS__)
|
||||
|
||||
#endif /* _LINUX_FILE_H_ */
|
||||
|
@ -79,6 +79,7 @@ struct linux_file {
|
||||
struct selinfo f_selinfo;
|
||||
struct sigio *f_sigio;
|
||||
struct vnode *f_vnode;
|
||||
volatile u_int f_count;
|
||||
};
|
||||
|
||||
#define file linux_file
|
||||
@ -220,6 +221,14 @@ iminor(struct inode *inode)
|
||||
return (minor(dev2unit(inode->v_rdev)));
|
||||
}
|
||||
|
||||
static inline struct linux_file *
|
||||
get_file(struct linux_file *f)
|
||||
{
|
||||
|
||||
refcount_acquire(f->_file == NULL ? &f->f_count : &f->_file->f_count);
|
||||
return (f);
|
||||
}
|
||||
|
||||
static inline struct inode *
|
||||
igrab(struct inode *inode)
|
||||
{
|
||||
|
@ -401,6 +401,20 @@ linux_file_dtor(void *cdp)
|
||||
kfree(filp);
|
||||
}
|
||||
|
||||
void
|
||||
linux_file_free(struct linux_file *filp)
|
||||
{
|
||||
if (filp->_file == NULL) {
|
||||
kfree(filp);
|
||||
} else {
|
||||
/*
|
||||
* The close method of the character device or file
|
||||
* will free the linux_file structure:
|
||||
*/
|
||||
_fdrop(filp->_file, curthread);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
linux_cdev_pager_populate(vm_object_t vm_obj, vm_pindex_t pidx, int fault_type,
|
||||
vm_prot_t max_prot, vm_pindex_t *first, vm_pindex_t *last)
|
||||
@ -593,9 +607,12 @@ linux_dev_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
|
||||
vhold(file->f_vnode);
|
||||
filp->f_vnode = file->f_vnode;
|
||||
linux_set_current(td);
|
||||
filp->_file = file;
|
||||
|
||||
if (filp->f_op->open) {
|
||||
error = -filp->f_op->open(file->f_vnode, filp);
|
||||
if (error) {
|
||||
vdrop(filp->f_vnode);
|
||||
kfree(filp);
|
||||
goto done;
|
||||
}
|
||||
@ -603,6 +620,7 @@ linux_dev_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
|
||||
error = devfs_set_cdevpriv(filp, linux_file_dtor);
|
||||
if (error) {
|
||||
filp->f_op->release(file->f_vnode, filp);
|
||||
vdrop(filp->f_vnode);
|
||||
kfree(filp);
|
||||
}
|
||||
done:
|
||||
@ -622,8 +640,7 @@ linux_dev_close(struct cdev *dev, int fflag, int devtype, struct thread *td)
|
||||
if ((error = devfs_get_cdevpriv((void **)&filp)) != 0)
|
||||
return (error);
|
||||
filp->f_flags = file->f_flag;
|
||||
devfs_clear_cdevpriv();
|
||||
|
||||
devfs_clear_cdevpriv();
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user