diff options
Diffstat (limited to 'drivers/usb')
| -rw-r--r-- | drivers/usb/gadget/function/f_fs.c | 144 | ||||
| -rw-r--r-- | drivers/usb/gadget/legacy/inode.c | 49 |
2 files changed, 109 insertions, 84 deletions
diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 47cfbe41fdff..4bf61017b42d 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -160,8 +160,6 @@ struct ffs_epfile { struct ffs_data *ffs; struct ffs_ep *ep; /* P: ffs->eps_lock */ - struct dentry *dentry; - /* * Buffer for holding data from partial reads which may happen since * we’re rounding user read requests to a multiple of a max packet size. @@ -271,11 +269,11 @@ struct ffs_desc_helper { }; static int __must_check ffs_epfiles_create(struct ffs_data *ffs); -static void ffs_epfiles_destroy(struct ffs_epfile *epfiles, unsigned count); +static void ffs_epfiles_destroy(struct super_block *sb, + struct ffs_epfile *epfiles, unsigned count); -static struct dentry * -ffs_sb_create_file(struct super_block *sb, const char *name, void *data, - const struct file_operations *fops); +static int ffs_sb_create_file(struct super_block *sb, const char *name, + void *data, const struct file_operations *fops); /* Devices management *******************************************************/ @@ -640,13 +638,22 @@ done_mutex: static int ffs_ep0_open(struct inode *inode, struct file *file) { - struct ffs_data *ffs = inode->i_private; + struct ffs_data *ffs = inode->i_sb->s_fs_info; + int ret; - if (ffs->state == FFS_CLOSING) - return -EBUSY; + /* Acquire mutex */ + ret = ffs_mutex_lock(&ffs->mutex, file->f_flags & O_NONBLOCK); + if (ret < 0) + return ret; - file->private_data = ffs; ffs_data_opened(ffs); + if (ffs->state == FFS_CLOSING) { + ffs_data_closed(ffs); + mutex_unlock(&ffs->mutex); + return -EBUSY; + } + mutex_unlock(&ffs->mutex); + file->private_data = ffs; return stream_open(inode, file); } @@ -1193,14 +1200,33 @@ error: static int ffs_epfile_open(struct inode *inode, struct file *file) { - struct ffs_epfile *epfile = inode->i_private; + struct ffs_data *ffs = inode->i_sb->s_fs_info; + struct ffs_epfile *epfile; + int ret; - if (WARN_ON(epfile->ffs->state != FFS_ACTIVE)) + /* Acquire mutex */ + ret = ffs_mutex_lock(&ffs->mutex, file->f_flags & O_NONBLOCK); + if (ret < 0) + return ret; + + if (!atomic_inc_not_zero(&ffs->opened)) { + mutex_unlock(&ffs->mutex); return -ENODEV; + } + /* + * we want the state to be FFS_ACTIVE; FFS_ACTIVE alone is + * not enough, though - we might have been through FFS_CLOSING + * and back to FFS_ACTIVE, with our file already removed. + */ + epfile = smp_load_acquire(&inode->i_private); + if (unlikely(ffs->state != FFS_ACTIVE || !epfile)) { + mutex_unlock(&ffs->mutex); + ffs_data_closed(ffs); + return -ENODEV; + } + mutex_unlock(&ffs->mutex); file->private_data = epfile; - ffs_data_opened(epfile->ffs); - return stream_open(inode, file); } @@ -1332,7 +1358,7 @@ static void ffs_dmabuf_put(struct dma_buf_attachment *attach) static int ffs_epfile_release(struct inode *inode, struct file *file) { - struct ffs_epfile *epfile = inode->i_private; + struct ffs_epfile *epfile = file->private_data; struct ffs_dmabuf_priv *priv, *tmp; struct ffs_data *ffs = epfile->ffs; @@ -1866,26 +1892,26 @@ ffs_sb_make_inode(struct super_block *sb, void *data, } /* Create "regular" file */ -static struct dentry *ffs_sb_create_file(struct super_block *sb, - const char *name, void *data, - const struct file_operations *fops) +static int ffs_sb_create_file(struct super_block *sb, const char *name, + void *data, const struct file_operations *fops) { struct ffs_data *ffs = sb->s_fs_info; struct dentry *dentry; struct inode *inode; - dentry = d_alloc_name(sb->s_root, name); - if (!dentry) - return NULL; - inode = ffs_sb_make_inode(sb, data, fops, NULL, &ffs->file_perms); - if (!inode) { - dput(dentry); - return NULL; + if (!inode) + return -ENOMEM; + dentry = simple_start_creating(sb->s_root, name); + if (IS_ERR(dentry)) { + iput(inode); + return PTR_ERR(dentry); } - d_add(dentry, inode); - return dentry; + d_make_persistent(dentry, inode); + + simple_done_creating(dentry); + return 0; } /* Super block */ @@ -1928,10 +1954,7 @@ static int ffs_sb_fill(struct super_block *sb, struct fs_context *fc) return -ENOMEM; /* EP0 file */ - if (!ffs_sb_create_file(sb, "ep0", ffs, &ffs_ep0_operations)) - return -ENOMEM; - - return 0; + return ffs_sb_create_file(sb, "ep0", ffs, &ffs_ep0_operations); } enum { @@ -2071,12 +2094,21 @@ static int ffs_fs_init_fs_context(struct fs_context *fc) return 0; } +static void ffs_data_reset(struct ffs_data *ffs); + static void ffs_fs_kill_sb(struct super_block *sb) { - kill_litter_super(sb); - if (sb->s_fs_info) - ffs_data_closed(sb->s_fs_info); + kill_anon_super(sb); + if (sb->s_fs_info) { + struct ffs_data *ffs = sb->s_fs_info; + ffs->state = FFS_CLOSING; + ffs_data_reset(ffs); + // no configfs accesses from that point on, + // so no further schedule_work() is possible + cancel_work_sync(&ffs->reset_work); + ffs_data_put(ffs); + } } static struct file_system_type ffs_fs_type = { @@ -2114,7 +2146,6 @@ static void functionfs_cleanup(void) /* ffs_data and ffs_function construction and destruction code **************/ static void ffs_data_clear(struct ffs_data *ffs); -static void ffs_data_reset(struct ffs_data *ffs); static void ffs_data_get(struct ffs_data *ffs) { @@ -2123,7 +2154,6 @@ static void ffs_data_get(struct ffs_data *ffs) static void ffs_data_opened(struct ffs_data *ffs) { - refcount_inc(&ffs->ref); if (atomic_add_return(1, &ffs->opened) == 1 && ffs->state == FFS_DEACTIVATED) { ffs->state = FFS_CLOSING; @@ -2148,11 +2178,11 @@ static void ffs_data_put(struct ffs_data *ffs) static void ffs_data_closed(struct ffs_data *ffs) { - struct ffs_epfile *epfiles; - unsigned long flags; - if (atomic_dec_and_test(&ffs->opened)) { if (ffs->no_disconnect) { + struct ffs_epfile *epfiles; + unsigned long flags; + ffs->state = FFS_DEACTIVATED; spin_lock_irqsave(&ffs->eps_lock, flags); epfiles = ffs->epfiles; @@ -2161,7 +2191,7 @@ static void ffs_data_closed(struct ffs_data *ffs) flags); if (epfiles) - ffs_epfiles_destroy(epfiles, + ffs_epfiles_destroy(ffs->sb, epfiles, ffs->eps_count); if (ffs->setup_state == FFS_SETUP_PENDING) @@ -2171,12 +2201,6 @@ static void ffs_data_closed(struct ffs_data *ffs) ffs_data_reset(ffs); } } - if (atomic_read(&ffs->opened) < 0) { - ffs->state = FFS_CLOSING; - ffs_data_reset(ffs); - } - - ffs_data_put(ffs); } static struct ffs_data *ffs_data_new(const char *dev_name) @@ -2226,7 +2250,7 @@ static void ffs_data_clear(struct ffs_data *ffs) * copy of epfile will save us from use-after-free. */ if (epfiles) { - ffs_epfiles_destroy(epfiles, ffs->eps_count); + ffs_epfiles_destroy(ffs->sb, epfiles, ffs->eps_count); ffs->epfiles = NULL; } @@ -2323,6 +2347,7 @@ static int ffs_epfiles_create(struct ffs_data *ffs) { struct ffs_epfile *epfile, *epfiles; unsigned i, count; + int err; count = ffs->eps_count; epfiles = kcalloc(count, sizeof(*epfiles), GFP_KERNEL); @@ -2339,12 +2364,11 @@ static int ffs_epfiles_create(struct ffs_data *ffs) sprintf(epfile->name, "ep%02x", ffs->eps_addrmap[i]); else sprintf(epfile->name, "ep%u", i); - epfile->dentry = ffs_sb_create_file(ffs->sb, epfile->name, - epfile, - &ffs_epfile_operations); - if (!epfile->dentry) { - ffs_epfiles_destroy(epfiles, i - 1); - return -ENOMEM; + err = ffs_sb_create_file(ffs->sb, epfile->name, + epfile, &ffs_epfile_operations); + if (err) { + ffs_epfiles_destroy(ffs->sb, epfiles, i - 1); + return err; } } @@ -2352,16 +2376,20 @@ static int ffs_epfiles_create(struct ffs_data *ffs) return 0; } -static void ffs_epfiles_destroy(struct ffs_epfile *epfiles, unsigned count) +static void clear_one(struct dentry *dentry) +{ + smp_store_release(&dentry->d_inode->i_private, NULL); +} + +static void ffs_epfiles_destroy(struct super_block *sb, + struct ffs_epfile *epfiles, unsigned count) { struct ffs_epfile *epfile = epfiles; + struct dentry *root = sb->s_root; for (; count; --count, ++epfile) { BUG_ON(mutex_is_locked(&epfile->mutex)); - if (epfile->dentry) { - simple_recursive_removal(epfile->dentry, NULL); - epfile->dentry = NULL; - } + simple_remove_by_name(root, epfile->name, clear_one); } kfree(epfiles); diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c index 13c3da49348c..62566a8e7451 100644 --- a/drivers/usb/gadget/legacy/inode.c +++ b/drivers/usb/gadget/legacy/inode.c @@ -150,7 +150,6 @@ struct dev_data { void *buf; wait_queue_head_t wait; struct super_block *sb; - struct dentry *dentry; /* except this scratch i/o buffer for ep0 */ u8 rbuf[RBUF_SIZE]; @@ -208,7 +207,6 @@ struct ep_data { struct usb_endpoint_descriptor desc, hs_desc; struct list_head epfiles; wait_queue_head_t wait; - struct dentry *dentry; }; static inline void get_ep (struct ep_data *data) @@ -1561,16 +1559,12 @@ static void destroy_ep_files (struct dev_data *dev) spin_lock_irq (&dev->lock); while (!list_empty(&dev->epfiles)) { struct ep_data *ep; - struct dentry *dentry; /* break link to FS */ ep = list_first_entry (&dev->epfiles, struct ep_data, epfiles); list_del_init (&ep->epfiles); spin_unlock_irq (&dev->lock); - dentry = ep->dentry; - ep->dentry = NULL; - /* break link to controller */ mutex_lock(&ep->lock); if (ep->state == STATE_EP_ENABLED) @@ -1581,10 +1575,11 @@ static void destroy_ep_files (struct dev_data *dev) mutex_unlock(&ep->lock); wake_up (&ep->wait); - put_ep (ep); /* break link to dcache */ - simple_recursive_removal(dentry, NULL); + simple_remove_by_name(dev->sb->s_root, ep->name, NULL); + + put_ep (ep); spin_lock_irq (&dev->lock); } @@ -1592,14 +1587,14 @@ static void destroy_ep_files (struct dev_data *dev) } -static struct dentry * -gadgetfs_create_file (struct super_block *sb, char const *name, +static int gadgetfs_create_file (struct super_block *sb, char const *name, void *data, const struct file_operations *fops); static int activate_ep_files (struct dev_data *dev) { struct usb_ep *ep; struct ep_data *data; + int err; gadget_for_each_ep (ep, dev->gadget) { @@ -1622,9 +1617,9 @@ static int activate_ep_files (struct dev_data *dev) if (!data->req) goto enomem1; - data->dentry = gadgetfs_create_file (dev->sb, data->name, + err = gadgetfs_create_file (dev->sb, data->name, data, &ep_io_operations); - if (!data->dentry) + if (err) goto enomem2; list_add_tail (&data->epfiles, &dev->epfiles); } @@ -1988,25 +1983,27 @@ gadgetfs_make_inode (struct super_block *sb, /* creates in fs root directory, so non-renamable and non-linkable. * so inode and dentry are paired, until device reconfig. */ -static struct dentry * -gadgetfs_create_file (struct super_block *sb, char const *name, +static int gadgetfs_create_file (struct super_block *sb, char const *name, void *data, const struct file_operations *fops) { struct dentry *dentry; struct inode *inode; - dentry = d_alloc_name(sb->s_root, name); - if (!dentry) - return NULL; - inode = gadgetfs_make_inode (sb, data, fops, S_IFREG | (default_perm & S_IRWXUGO)); - if (!inode) { - dput(dentry); - return NULL; + if (!inode) + return -ENOMEM; + + dentry = simple_start_creating(sb->s_root, name); + if (IS_ERR(dentry)) { + iput(inode); + return PTR_ERR(dentry); } - d_add (dentry, inode); - return dentry; + + d_make_persistent(dentry, inode); + + simple_done_creating(dentry); + return 0; } static const struct super_operations gadget_fs_operations = { @@ -2059,8 +2056,8 @@ gadgetfs_fill_super (struct super_block *sb, struct fs_context *fc) goto Enomem; dev->sb = sb; - dev->dentry = gadgetfs_create_file(sb, CHIP, dev, &ep0_operations); - if (!dev->dentry) { + rc = gadgetfs_create_file(sb, CHIP, dev, &ep0_operations); + if (rc) { put_dev(dev); goto Enomem; } @@ -2102,7 +2099,7 @@ static void gadgetfs_kill_sb (struct super_block *sb) { mutex_lock(&sb_mutex); - kill_litter_super (sb); + kill_anon_super (sb); if (the_device) { put_dev (the_device); the_device = NULL; |
