diff options
| -rw-r--r-- | drivers/staging/comedi/comedi_buf.c | 37 | ||||
| -rw-r--r-- | drivers/staging/comedi/comedi_fops.c | 18 | ||||
| -rw-r--r-- | drivers/staging/comedi/comedi_internal.h | 2 | 
3 files changed, 51 insertions, 6 deletions
| diff --git a/drivers/staging/comedi/comedi_buf.c b/drivers/staging/comedi/comedi_buf.c index 924fce977985..257595016161 100644 --- a/drivers/staging/comedi/comedi_buf.c +++ b/drivers/staging/comedi/comedi_buf.c @@ -61,6 +61,8 @@ static void __comedi_buf_free(struct comedi_device *dev,  			      struct comedi_subdevice *s)  {  	struct comedi_async *async = s->async; +	struct comedi_buf_map *bm; +	unsigned long flags;  	if (async->prealloc_buf) {  		vunmap(async->prealloc_buf); @@ -68,8 +70,11 @@ static void __comedi_buf_free(struct comedi_device *dev,  		async->prealloc_bufsz = 0;  	} -	comedi_buf_map_put(async->buf_map); +	spin_lock_irqsave(&s->spin_lock, flags); +	bm = async->buf_map;  	async->buf_map = NULL; +	spin_unlock_irqrestore(&s->spin_lock, flags); +	comedi_buf_map_put(bm);  }  static void __comedi_buf_alloc(struct comedi_device *dev, @@ -80,6 +85,7 @@ static void __comedi_buf_alloc(struct comedi_device *dev,  	struct page **pages = NULL;  	struct comedi_buf_map *bm;  	struct comedi_buf_page *buf; +	unsigned long flags;  	unsigned i;  	if (!IS_ENABLED(CONFIG_HAS_DMA) && s->async_dma_dir != DMA_NONE) { @@ -92,8 +98,10 @@ static void __comedi_buf_alloc(struct comedi_device *dev,  	if (!bm)  		return; -	async->buf_map = bm;  	kref_init(&bm->refcount); +	spin_lock_irqsave(&s->spin_lock, flags); +	async->buf_map = bm; +	spin_unlock_irqrestore(&s->spin_lock, flags);  	bm->dma_dir = s->async_dma_dir;  	if (bm->dma_dir != DMA_NONE)  		/* Need ref to hardware device to free buffer later. */ @@ -127,7 +135,9 @@ static void __comedi_buf_alloc(struct comedi_device *dev,  		pages[i] = virt_to_page(buf->virt_addr);  	} +	spin_lock_irqsave(&s->spin_lock, flags);  	bm->n_pages = i; +	spin_unlock_irqrestore(&s->spin_lock, flags);  	/* vmap the prealloc_buf if all the pages were allocated */  	if (i == n_pages) @@ -150,6 +160,29 @@ int comedi_buf_map_put(struct comedi_buf_map *bm)  	return 1;  } +/* returns s->async->buf_map and increments its kref refcount */ +struct comedi_buf_map * +comedi_buf_map_from_subdev_get(struct comedi_subdevice *s) +{ +	struct comedi_async *async = s->async; +	struct comedi_buf_map *bm = NULL; +	unsigned long flags; + +	if (!async) +		return NULL; + +	spin_lock_irqsave(&s->spin_lock, flags); +	bm = async->buf_map; +	/* only want it if buffer pages allocated */ +	if (bm && bm->n_pages) +		comedi_buf_map_get(bm); +	else +		bm = NULL; +	spin_unlock_irqrestore(&s->spin_lock, flags); + +	return bm; +} +  bool comedi_buf_is_mmapped(struct comedi_async *async)  {  	struct comedi_buf_map *bm = async->buf_map; diff --git a/drivers/staging/comedi/comedi_fops.c b/drivers/staging/comedi/comedi_fops.c index ea6dc36d753b..acc80197e35e 100644 --- a/drivers/staging/comedi/comedi_fops.c +++ b/drivers/staging/comedi/comedi_fops.c @@ -1926,14 +1926,21 @@ static int comedi_mmap(struct file *file, struct vm_area_struct *vma)  	struct comedi_device *dev = file->private_data;  	struct comedi_subdevice *s;  	struct comedi_async *async; -	struct comedi_buf_map *bm; +	struct comedi_buf_map *bm = NULL;  	unsigned long start = vma->vm_start;  	unsigned long size;  	int n_pages;  	int i;  	int retval; -	mutex_lock(&dev->mutex); +	/* +	 * 'trylock' avoids circular dependency with current->mm->mmap_sem +	 * and down-reading &dev->attach_lock should normally succeed without +	 * contention unless the device is in the process of being attached +	 * or detached. +	 */ +	if (!down_read_trylock(&dev->attach_lock)) +		return -EAGAIN;  	if (!dev->attached) {  		dev_dbg(dev->class_dev, "no driver attached\n"); @@ -1973,7 +1980,9 @@ static int comedi_mmap(struct file *file, struct vm_area_struct *vma)  	}  	n_pages = size >> PAGE_SHIFT; -	bm = async->buf_map; + +	/* get reference to current buf map (if any) */ +	bm = comedi_buf_map_from_subdev_get(s);  	if (!bm || n_pages > bm->n_pages) {  		retval = -EINVAL;  		goto done; @@ -1997,7 +2006,8 @@ static int comedi_mmap(struct file *file, struct vm_area_struct *vma)  	retval = 0;  done: -	mutex_unlock(&dev->mutex); +	up_read(&dev->attach_lock); +	comedi_buf_map_put(bm);	/* put reference to buf map - okay if NULL */  	return retval;  } diff --git a/drivers/staging/comedi/comedi_internal.h b/drivers/staging/comedi/comedi_internal.h index 9a746570f161..a492f2d2436e 100644 --- a/drivers/staging/comedi/comedi_internal.h +++ b/drivers/staging/comedi/comedi_internal.h @@ -19,6 +19,8 @@ void comedi_buf_reset(struct comedi_async *async);  bool comedi_buf_is_mmapped(struct comedi_async *async);  void comedi_buf_map_get(struct comedi_buf_map *bm);  int comedi_buf_map_put(struct comedi_buf_map *bm); +struct comedi_buf_map *comedi_buf_map_from_subdev_get( +		struct comedi_subdevice *s);  unsigned int comedi_buf_write_n_allocated(struct comedi_async *async);  void comedi_device_cancel_all(struct comedi_device *dev); | 
