Blame view

drivers/gpu/drm/drm_fops.c 16.7 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
  /**
b5e89ed53   Dave Airlie   drm: lindent the ...
2
   * \file drm_fops.c
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
3
   * File operations for DRM
b5e89ed53   Dave Airlie   drm: lindent the ...
4
   *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
   * \author Rickard E. (Rik) Faith <faith@valinux.com>
   * \author Daryll Strauss <daryll@valinux.com>
   * \author Gareth Hughes <gareth@valinux.com>
   */
  
  /*
   * Created: Mon Jan  4 08:58:31 1999 by faith@valinux.com
   *
   * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
   * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
   * All Rights Reserved.
   *
   * Permission is hereby granted, free of charge, to any person obtaining a
   * copy of this software and associated documentation files (the "Software"),
   * to deal in the Software without restriction, including without limitation
   * the rights to use, copy, modify, merge, publish, distribute, sublicense,
   * and/or sell copies of the Software, and to permit persons to whom the
   * Software is furnished to do so, subject to the following conditions:
   *
   * The above copyright notice and this permission notice (including the next
   * paragraph) shall be included in all copies or substantial portions of the
   * Software.
   *
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
   * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
   * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
   * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
   * OTHER DEALINGS IN THE SOFTWARE.
   */
  
  #include "drmP.h"
  #include <linux/poll.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
39
  #include <linux/slab.h>
e0cd36081   Paul Gortmaker   gpu: add module.h...
40
  #include <linux/module.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
41

58374713c   Arnd Bergmann   drm: kill BKL fro...
42
43
  /* from BKL pushdown: note that nothing else serializes idr_find() */
  DEFINE_MUTEX(drm_global_mutex);
e3461a2bc   Ben Skeggs   drm: export drm_g...
44
  EXPORT_SYMBOL(drm_global_mutex);
58374713c   Arnd Bergmann   drm: kill BKL fro...
45

b5e89ed53   Dave Airlie   drm: lindent the ...
46
  static int drm_open_helper(struct inode *inode, struct file *filp,
84b1fd103   Dave Airlie   drm: remove drm_f...
47
  			   struct drm_device * dev);
c94f70298   Dave Airlie   drm: misc cleanup
48

84b1fd103   Dave Airlie   drm: remove drm_f...
49
  static int drm_setup(struct drm_device * dev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
50
51
52
  {
  	int i;
  	int ret;
22eae947b   Dave Airlie   drm: rename drive...
53
54
  	if (dev->driver->firstopen) {
  		ret = dev->driver->firstopen(dev);
b5e89ed53   Dave Airlie   drm: lindent the ...
55
  		if (ret != 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
56
57
  			return ret;
  	}
b5e89ed53   Dave Airlie   drm: lindent the ...
58
59
  	atomic_set(&dev->ioctl_count, 0);
  	atomic_set(&dev->vma_count, 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
60

f453ba046   Dave Airlie   DRM: add mode set...
61
62
63
64
  	if (drm_core_check_feature(dev, DRIVER_HAVE_DMA) &&
  	    !drm_core_check_feature(dev, DRIVER_MODESET)) {
  		dev->buf_use = 0;
  		atomic_set(&dev->buf_alloc, 0);
b5e89ed53   Dave Airlie   drm: lindent the ...
65
66
  		i = drm_dma_setup(dev);
  		if (i < 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
67
68
  			return i;
  	}
3d77461ec   Dave Airlie   drm: cleanup old ...
69
  	for (i = 0; i < ARRAY_SIZE(dev->counts); i++)
b5e89ed53   Dave Airlie   drm: lindent the ...
70
  		atomic_set(&dev->counts[i], 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
71

11d9c2fd0   Dave Airlie   drm: fix crash wi...
72
  	dev->sigdata.lock = NULL;
7c1c2871a   Dave Airlie   drm: move to kref...
73

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
74
75
76
77
  	dev->queue_count = 0;
  	dev->queue_reserved = 0;
  	dev->queue_slots = 0;
  	dev->queuelist = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
78
79
80
81
82
83
  	dev->context_flag = 0;
  	dev->interrupt_flag = 0;
  	dev->dma_flag = 0;
  	dev->last_context = 0;
  	dev->last_switch = 0;
  	dev->last_checked = 0;
b5e89ed53   Dave Airlie   drm: lindent the ...
84
  	init_waitqueue_head(&dev->context_wait);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
85
86
87
88
  	dev->if_version = 0;
  
  	dev->ctx_start = 0;
  	dev->lck_start = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
89
  	dev->buf_async = NULL;
b5e89ed53   Dave Airlie   drm: lindent the ...
90
91
  	init_waitqueue_head(&dev->buf_readers);
  	init_waitqueue_head(&dev->buf_writers);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
92

b5e89ed53   Dave Airlie   drm: lindent the ...
93
94
  	DRM_DEBUG("
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
95
96
97
  
  	/*
  	 * The kernel's context could be created here, but is now created
b5e89ed53   Dave Airlie   drm: lindent the ...
98
  	 * in drm_dma_enqueue.  This is more resource-efficient for
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
99
100
101
102
  	 * hardware that does not do DMA, but may mean that
  	 * drm_select_queue fails between the time the interrupt is
  	 * initialized and the time the queues are initialized.
  	 */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
103
104
105
106
107
108
  
  	return 0;
  }
  
  /**
   * Open file.
b5e89ed53   Dave Airlie   drm: lindent the ...
109
   *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
110
111
112
113
114
115
116
117
   * \param inode device inode
   * \param filp file pointer.
   * \return zero on success or a negative number on failure.
   *
   * Searches the DRM device with the same minor number, calls open_helper(), and
   * increments the device open count. If the open count was previous at zero,
   * i.e., it's the first that the device is open, then calls setup().
   */
b5e89ed53   Dave Airlie   drm: lindent the ...
118
  int drm_open(struct inode *inode, struct file *filp)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
119
  {
84b1fd103   Dave Airlie   drm: remove drm_f...
120
  	struct drm_device *dev = NULL;
2c14f28be   Dave Airlie   drm: reorganise m...
121
122
  	int minor_id = iminor(inode);
  	struct drm_minor *minor;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
123
  	int retcode = 0;
2c14f28be   Dave Airlie   drm: reorganise m...
124
125
  	minor = idr_find(&drm_minors_idr, minor_id);
  	if (!minor)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
126
  		return -ENODEV;
b5e89ed53   Dave Airlie   drm: lindent the ...
127

2c14f28be   Dave Airlie   drm: reorganise m...
128
  	if (!(dev = minor->dev))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
129
  		return -ENODEV;
b5e89ed53   Dave Airlie   drm: lindent the ...
130
131
132
133
  
  	retcode = drm_open_helper(inode, filp, dev);
  	if (!retcode) {
  		atomic_inc(&dev->counts[_DRM_STAT_OPENS]);
1a72d65d6   Chris Wilson   drm: Remove count...
134
  		if (!dev->open_count++)
a2c0a97b7   Jesse Barnes   drm: GEM mmap sup...
135
  			retcode = drm_setup(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
136
  	}
da5840586   Chris Wilson   drm: Return ENODE...
137
138
139
140
141
142
143
144
145
  	if (!retcode) {
  		mutex_lock(&dev->struct_mutex);
  		if (minor->type == DRM_MINOR_LEGACY) {
  			if (dev->dev_mapping == NULL)
  				dev->dev_mapping = inode->i_mapping;
  			else if (dev->dev_mapping != inode->i_mapping)
  				retcode = -ENODEV;
  		}
  		mutex_unlock(&dev->struct_mutex);
f453ba046   Dave Airlie   DRM: add mode set...
146
  	}
a2c0a97b7   Jesse Barnes   drm: GEM mmap sup...
147

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
148
149
150
151
152
  	return retcode;
  }
  EXPORT_SYMBOL(drm_open);
  
  /**
d985c1088   Dave Airlie   drm: major update...
153
154
155
156
157
158
159
160
161
162
   * File \c open operation.
   *
   * \param inode device inode.
   * \param filp file pointer.
   *
   * Puts the dev->fops corresponding to the device minor number into
   * \p filp, call the \c open method, and restore the file operations.
   */
  int drm_stub_open(struct inode *inode, struct file *filp)
  {
84b1fd103   Dave Airlie   drm: remove drm_f...
163
  	struct drm_device *dev = NULL;
2c14f28be   Dave Airlie   drm: reorganise m...
164
165
  	struct drm_minor *minor;
  	int minor_id = iminor(inode);
d985c1088   Dave Airlie   drm: major update...
166
  	int err = -ENODEV;
99ac48f54   Arjan van de Ven   [PATCH] mark f_op...
167
  	const struct file_operations *old_fops;
d985c1088   Dave Airlie   drm: major update...
168
169
170
  
  	DRM_DEBUG("
  ");
58374713c   Arnd Bergmann   drm: kill BKL fro...
171
  	mutex_lock(&drm_global_mutex);
2c14f28be   Dave Airlie   drm: reorganise m...
172
173
  	minor = idr_find(&drm_minors_idr, minor_id);
  	if (!minor)
70ffa16e6   Jonathan Corbet   drm: cdev lock_ke...
174
  		goto out;
d985c1088   Dave Airlie   drm: major update...
175

2c14f28be   Dave Airlie   drm: reorganise m...
176
  	if (!(dev = minor->dev))
70ffa16e6   Jonathan Corbet   drm: cdev lock_ke...
177
  		goto out;
d985c1088   Dave Airlie   drm: major update...
178
179
  
  	old_fops = filp->f_op;
e08e96de9   Arjan van de Ven   drm: Make the per...
180
  	filp->f_op = fops_get(dev->driver->fops);
f41ced8f1   Laurent Pinchart   Check fops_get() ...
181
182
183
184
  	if (filp->f_op == NULL) {
  		filp->f_op = old_fops;
  		goto out;
  	}
d985c1088   Dave Airlie   drm: major update...
185
186
187
188
189
  	if (filp->f_op->open && (err = filp->f_op->open(inode, filp))) {
  		fops_put(filp->f_op);
  		filp->f_op = fops_get(old_fops);
  	}
  	fops_put(old_fops);
70ffa16e6   Jonathan Corbet   drm: cdev lock_ke...
190
  out:
58374713c   Arnd Bergmann   drm: kill BKL fro...
191
  	mutex_unlock(&drm_global_mutex);
d985c1088   Dave Airlie   drm: major update...
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
  	return err;
  }
  
  /**
   * Check whether DRI will run on this CPU.
   *
   * \return non-zero if the DRI will run on this CPU, or zero otherwise.
   */
  static int drm_cpu_valid(void)
  {
  #if defined(__i386__)
  	if (boot_cpu_data.x86 == 3)
  		return 0;	/* No cmpxchg on a 386 */
  #endif
  #if defined(__sparc__) && !defined(__sparc_v9__)
  	return 0;		/* No cmpxchg before v9 sparc. */
  #endif
  	return 1;
  }
  
  /**
   * Called whenever a process opens /dev/drm.
   *
   * \param inode device inode.
   * \param filp file pointer.
   * \param dev device.
   * \return zero on success or a negative number on failure.
   *
   * Creates and initializes a drm_file structure for the file private data in \p
   * filp and add it into the double linked list in \p dev.
   */
  static int drm_open_helper(struct inode *inode, struct file *filp,
84b1fd103   Dave Airlie   drm: remove drm_f...
224
  			   struct drm_device * dev)
d985c1088   Dave Airlie   drm: major update...
225
  {
2c14f28be   Dave Airlie   drm: reorganise m...
226
  	int minor_id = iminor(inode);
84b1fd103   Dave Airlie   drm: remove drm_f...
227
  	struct drm_file *priv;
d985c1088   Dave Airlie   drm: major update...
228
229
230
231
232
233
  	int ret;
  
  	if (filp->f_flags & O_EXCL)
  		return -EBUSY;	/* No exclusive opens */
  	if (!drm_cpu_valid())
  		return -EINVAL;
5bcf719b7   Dave Airlie   drm/switcheroo: t...
234
235
  	if (dev->switch_power_state != DRM_SWITCH_POWER_ON)
  		return -EINVAL;
d985c1088   Dave Airlie   drm: major update...
236

2c14f28be   Dave Airlie   drm: reorganise m...
237
238
  	DRM_DEBUG("pid = %d, minor = %d
  ", task_pid_nr(current), minor_id);
d985c1088   Dave Airlie   drm: major update...
239

6ebc22e6d   Julia Lawall   drivers/gpu/drm: ...
240
  	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
d985c1088   Dave Airlie   drm: major update...
241
242
  	if (!priv)
  		return -ENOMEM;
d985c1088   Dave Airlie   drm: major update...
243
  	filp->private_data = priv;
6c340eac0   Eric Anholt   drm: Replace filp...
244
  	priv->filp = filp;
2df68b439   David Howells   drm/cred: wrap ta...
245
  	priv->uid = current_euid();
ba25f9dcc   Pavel Emelyanov   Use helpers to ob...
246
  	priv->pid = task_pid_nr(current);
2c14f28be   Dave Airlie   drm: reorganise m...
247
  	priv->minor = idr_find(&drm_minors_idr, minor_id);
d985c1088   Dave Airlie   drm: major update...
248
249
250
251
  	priv->ioctl_count = 0;
  	/* for compatibility root is always authenticated */
  	priv->authenticated = capable(CAP_SYS_ADMIN);
  	priv->lock_count = 0;
bd1b331fa   Dave Airlie   drm: cleanup use ...
252
  	INIT_LIST_HEAD(&priv->lhead);
f453ba046   Dave Airlie   DRM: add mode set...
253
  	INIT_LIST_HEAD(&priv->fbs);
c9a9c5e02   Kristian Høgsberg   drm: Add async ev...
254
255
256
  	INIT_LIST_HEAD(&priv->event_list);
  	init_waitqueue_head(&priv->event_wait);
  	priv->event_space = 4096; /* set aside 4k for event buffer */
bd1b331fa   Dave Airlie   drm: cleanup use ...
257

673a394b1   Eric Anholt   drm: Add GEM ("gr...
258
259
  	if (dev->driver->driver_features & DRIVER_GEM)
  		drm_gem_open(dev, priv);
d985c1088   Dave Airlie   drm: major update...
260
261
262
263
264
  	if (dev->driver->open) {
  		ret = dev->driver->open(dev, priv);
  		if (ret < 0)
  			goto out_free;
  	}
7c1c2871a   Dave Airlie   drm: move to kref...
265
266
  
  	/* if there is no current master make this fd it */
30e2fb188   Dave Airlie   sem2mutex: driver...
267
  	mutex_lock(&dev->struct_mutex);
7c1c2871a   Dave Airlie   drm: move to kref...
268
269
270
271
  	if (!priv->minor->master) {
  		/* create a new master */
  		priv->minor->master = drm_master_create(priv->minor);
  		if (!priv->minor->master) {
dba5ed0cd   Dan Carpenter   drm: drm_fops.c u...
272
  			mutex_unlock(&dev->struct_mutex);
7c1c2871a   Dave Airlie   drm: move to kref...
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
  			ret = -ENOMEM;
  			goto out_free;
  		}
  
  		priv->is_master = 1;
  		/* take another reference for the copy in the local file priv */
  		priv->master = drm_master_get(priv->minor->master);
  
  		priv->authenticated = 1;
  
  		mutex_unlock(&dev->struct_mutex);
  		if (dev->driver->master_create) {
  			ret = dev->driver->master_create(dev, priv->master);
  			if (ret) {
  				mutex_lock(&dev->struct_mutex);
  				/* drop both references if this fails */
  				drm_master_put(&priv->minor->master);
  				drm_master_put(&priv->master);
  				mutex_unlock(&dev->struct_mutex);
  				goto out_free;
  			}
  		}
862302ffe   Thomas Hellstrom   drm: Add support ...
295
296
297
298
299
300
301
302
303
304
305
306
  		mutex_lock(&dev->struct_mutex);
  		if (dev->driver->master_set) {
  			ret = dev->driver->master_set(dev, priv, true);
  			if (ret) {
  				/* drop both references if this fails */
  				drm_master_put(&priv->minor->master);
  				drm_master_put(&priv->master);
  				mutex_unlock(&dev->struct_mutex);
  				goto out_free;
  			}
  		}
  		mutex_unlock(&dev->struct_mutex);
7c1c2871a   Dave Airlie   drm: move to kref...
307
308
309
310
311
  	} else {
  		/* get a reference to the master */
  		priv->master = drm_master_get(priv->minor->master);
  		mutex_unlock(&dev->struct_mutex);
  	}
bd1b331fa   Dave Airlie   drm: cleanup use ...
312

7c1c2871a   Dave Airlie   drm: move to kref...
313
  	mutex_lock(&dev->struct_mutex);
bd1b331fa   Dave Airlie   drm: cleanup use ...
314
  	list_add(&priv->lhead, &dev->filelist);
30e2fb188   Dave Airlie   sem2mutex: driver...
315
  	mutex_unlock(&dev->struct_mutex);
d985c1088   Dave Airlie   drm: major update...
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
  
  #ifdef __alpha__
  	/*
  	 * Default the hose
  	 */
  	if (!dev->hose) {
  		struct pci_dev *pci_dev;
  		pci_dev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, NULL);
  		if (pci_dev) {
  			dev->hose = pci_dev->sysdata;
  			pci_dev_put(pci_dev);
  		}
  		if (!dev->hose) {
  			struct pci_bus *b = pci_bus_b(pci_root_buses.next);
  			if (b)
  				dev->hose = b->sysdata;
  		}
  	}
  #endif
  
  	return 0;
        out_free:
9a298b2ac   Eric Anholt   drm: Remove memor...
338
  	kfree(priv);
d985c1088   Dave Airlie   drm: major update...
339
340
341
342
343
344
345
  	filp->private_data = NULL;
  	return ret;
  }
  
  /** No-op. */
  int drm_fasync(int fd, struct file *filp, int on)
  {
84b1fd103   Dave Airlie   drm: remove drm_f...
346
  	struct drm_file *priv = filp->private_data;
2c14f28be   Dave Airlie   drm: reorganise m...
347
  	struct drm_device *dev = priv->minor->dev;
d985c1088   Dave Airlie   drm: major update...
348
349
350
  
  	DRM_DEBUG("fd = %d, device = 0x%lx
  ", fd,
2c14f28be   Dave Airlie   drm: reorganise m...
351
  		  (long)old_encode_dev(priv->minor->device));
60aa49243   Jonathan Corbet   Rationalize fasyn...
352
  	return fasync_helper(fd, filp, on, &dev->buf_async);
d985c1088   Dave Airlie   drm: major update...
353
354
  }
  EXPORT_SYMBOL(drm_fasync);
7c1c2871a   Dave Airlie   drm: move to kref...
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
  /*
   * Reclaim locked buffers; note that this may be a bad idea if the current
   * context doesn't have the hw lock...
   */
  static void drm_reclaim_locked_buffers(struct drm_device *dev, struct file *f)
  {
  	struct drm_file *file_priv = f->private_data;
  
  	if (drm_i_have_hw_lock(dev, file_priv)) {
  		dev->driver->reclaim_buffers_locked(dev, file_priv);
  	} else {
  		unsigned long _end = jiffies + 3 * DRM_HZ;
  		int locked = 0;
  
  		drm_idlelock_take(&file_priv->master->lock);
  
  		/*
  		 * Wait for a while.
  		 */
  		do {
  			spin_lock_bh(&file_priv->master->lock.spinlock);
  			locked = file_priv->master->lock.idle_has_lock;
  			spin_unlock_bh(&file_priv->master->lock.spinlock);
  			if (locked)
  				break;
  			schedule();
  		} while (!time_after_eq(jiffies, _end));
  
  		if (!locked) {
  			DRM_ERROR("reclaim_buffers_locked() deadlock. Please rework this
  "
  				  "\tdriver to use reclaim_buffers_idlelocked() instead.
  "
  				  "\tI will go on reclaiming the buffers anyway.
  ");
  		}
  
  		dev->driver->reclaim_buffers_locked(dev, file_priv);
  		drm_idlelock_release(&file_priv->master->lock);
  	}
  }
  
  static void drm_master_release(struct drm_device *dev, struct file *filp)
  {
  	struct drm_file *file_priv = filp->private_data;
  
  	if (dev->driver->reclaim_buffers_locked &&
  	    file_priv->master->lock.hw_lock)
  		drm_reclaim_locked_buffers(dev, filp);
  
  	if (dev->driver->reclaim_buffers_idlelocked &&
  	    file_priv->master->lock.hw_lock) {
  		drm_idlelock_take(&file_priv->master->lock);
  		dev->driver->reclaim_buffers_idlelocked(dev, file_priv);
  		drm_idlelock_release(&file_priv->master->lock);
  	}
  
  
  	if (drm_i_have_hw_lock(dev, file_priv)) {
  		DRM_DEBUG("File %p released, freeing lock for context %d
  ",
  			  filp, _DRM_LOCKING_CONTEXT(file_priv->master->lock.hw_lock->lock));
  		drm_lock_free(&file_priv->master->lock,
  			      _DRM_LOCKING_CONTEXT(file_priv->master->lock.hw_lock->lock));
  	}
  
  	if (drm_core_check_feature(dev, DRIVER_HAVE_DMA) &&
  	    !dev->driver->reclaim_buffers_locked) {
  		dev->driver->reclaim_buffers(dev, file_priv);
  	}
  }
c9a9c5e02   Kristian Høgsberg   drm: Add async ev...
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
  static void drm_events_release(struct drm_file *file_priv)
  {
  	struct drm_device *dev = file_priv->minor->dev;
  	struct drm_pending_event *e, *et;
  	struct drm_pending_vblank_event *v, *vt;
  	unsigned long flags;
  
  	spin_lock_irqsave(&dev->event_lock, flags);
  
  	/* Remove pending flips */
  	list_for_each_entry_safe(v, vt, &dev->vblank_event_list, base.link)
  		if (v->base.file_priv == file_priv) {
  			list_del(&v->base.link);
  			drm_vblank_put(dev, v->pipe);
  			v->base.destroy(&v->base);
  		}
  
  	/* Remove unconsumed events */
  	list_for_each_entry_safe(e, et, &file_priv->event_list, link)
  		e->destroy(e);
  
  	spin_unlock_irqrestore(&dev->event_lock, flags);
  }
d985c1088   Dave Airlie   drm: major update...
449
  /**
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
450
451
452
   * Release file.
   *
   * \param inode device inode
6c340eac0   Eric Anholt   drm: Replace filp...
453
   * \param file_priv DRM file private.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
454
455
456
457
458
   * \return zero on success or a negative number on failure.
   *
   * If the hardware lock is held then free it, and take it again for the kernel
   * context since it's necessary to reclaim buffers. Unlink the file private
   * data from its list and free it. Decreases the open count and if it reaches
22eae947b   Dave Airlie   drm: rename drive...
459
   * zero calls drm_lastclose().
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
460
   */
b5e89ed53   Dave Airlie   drm: lindent the ...
461
  int drm_release(struct inode *inode, struct file *filp)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
462
  {
6c340eac0   Eric Anholt   drm: Replace filp...
463
  	struct drm_file *file_priv = filp->private_data;
2c14f28be   Dave Airlie   drm: reorganise m...
464
  	struct drm_device *dev = file_priv->minor->dev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
465
  	int retcode = 0;
58374713c   Arnd Bergmann   drm: kill BKL fro...
466
  	mutex_lock(&drm_global_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
467

b5e89ed53   Dave Airlie   drm: lindent the ...
468
469
  	DRM_DEBUG("open_count = %d
  ", dev->open_count);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
470

22eae947b   Dave Airlie   drm: rename drive...
471
  	if (dev->driver->preclose)
6c340eac0   Eric Anholt   drm: Replace filp...
472
  		dev->driver->preclose(dev, file_priv);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
473
474
475
476
  
  	/* ========================================================
  	 * Begin inline drm_release
  	 */
b5e89ed53   Dave Airlie   drm: lindent the ...
477
478
  	DRM_DEBUG("pid = %d, device = 0x%lx, open_count = %d
  ",
ba25f9dcc   Pavel Emelyanov   Use helpers to ob...
479
  		  task_pid_nr(current),
2c14f28be   Dave Airlie   drm: reorganise m...
480
  		  (long)old_encode_dev(file_priv->minor->device),
b5e89ed53   Dave Airlie   drm: lindent the ...
481
  		  dev->open_count);
7c1c2871a   Dave Airlie   drm: move to kref...
482
483
484
  	/* if the master has gone away we can't do anything with the lock */
  	if (file_priv->minor->master)
  		drm_master_release(dev, filp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
485

c9a9c5e02   Kristian Høgsberg   drm: Add async ev...
486
  	drm_events_release(file_priv);
673a394b1   Eric Anholt   drm: Add GEM ("gr...
487
488
  	if (dev->driver->driver_features & DRIVER_GEM)
  		drm_gem_release(dev, file_priv);
ea39f8351   Kristian Høgsberg   drm: Release user...
489
490
  	if (dev->driver->driver_features & DRIVER_MODESET)
  		drm_fb_release(file_priv);
30e2fb188   Dave Airlie   sem2mutex: driver...
491
  	mutex_lock(&dev->ctxlist_mutex);
bd1b331fa   Dave Airlie   drm: cleanup use ...
492
  	if (!list_empty(&dev->ctxlist)) {
55910517a   Dave Airlie   drm: detypedeffin...
493
  		struct drm_ctx_list *pos, *n;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
494

bd1b331fa   Dave Airlie   drm: cleanup use ...
495
  		list_for_each_entry_safe(pos, n, &dev->ctxlist, head) {
6c340eac0   Eric Anholt   drm: Replace filp...
496
  			if (pos->tag == file_priv &&
b5e89ed53   Dave Airlie   drm: lindent the ...
497
  			    pos->handle != DRM_KERNEL_CONTEXT) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
498
  				if (dev->driver->context_dtor)
b5e89ed53   Dave Airlie   drm: lindent the ...
499
500
  					dev->driver->context_dtor(dev,
  								  pos->handle);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
501

b5e89ed53   Dave Airlie   drm: lindent the ...
502
  				drm_ctxbitmap_free(dev, pos->handle);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
503

b5e89ed53   Dave Airlie   drm: lindent the ...
504
  				list_del(&pos->head);
9a298b2ac   Eric Anholt   drm: Remove memor...
505
  				kfree(pos);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
506
507
508
509
  				--dev->ctx_count;
  			}
  		}
  	}
30e2fb188   Dave Airlie   sem2mutex: driver...
510
  	mutex_unlock(&dev->ctxlist_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
511

30e2fb188   Dave Airlie   sem2mutex: driver...
512
  	mutex_lock(&dev->struct_mutex);
7c1c2871a   Dave Airlie   drm: move to kref...
513
514
  
  	if (file_priv->is_master) {
fda714c29   Thomas Hellstrom   drm: Avoid client...
515
  		struct drm_master *master = file_priv->master;
84b1fd103   Dave Airlie   drm: remove drm_f...
516
  		struct drm_file *temp;
7c1c2871a   Dave Airlie   drm: move to kref...
517
518
519
520
521
  		list_for_each_entry(temp, &dev->filelist, lhead) {
  			if ((temp->master == file_priv->master) &&
  			    (temp != file_priv))
  				temp->authenticated = 0;
  		}
bd1b331fa   Dave Airlie   drm: cleanup use ...
522

fda714c29   Thomas Hellstrom   drm: Avoid client...
523
524
525
526
527
528
529
530
531
532
533
534
  		/**
  		 * Since the master is disappearing, so is the
  		 * possibility to lock.
  		 */
  
  		if (master->lock.hw_lock) {
  			if (dev->sigdata.lock == master->lock.hw_lock)
  				dev->sigdata.lock = NULL;
  			master->lock.hw_lock = NULL;
  			master->lock.file_priv = NULL;
  			wake_up_interruptible_all(&master->lock.lock_queue);
  		}
7c1c2871a   Dave Airlie   drm: move to kref...
535
536
  		if (file_priv->minor->master == file_priv->master) {
  			/* drop the reference held my the minor */
862302ffe   Thomas Hellstrom   drm: Add support ...
537
538
  			if (dev->driver->master_drop)
  				dev->driver->master_drop(dev, file_priv, true);
7c1c2871a   Dave Airlie   drm: move to kref...
539
540
  			drm_master_put(&file_priv->minor->master);
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
541
  	}
7c1c2871a   Dave Airlie   drm: move to kref...
542
543
544
545
  
  	/* drop the reference held my the file priv */
  	drm_master_put(&file_priv->master);
  	file_priv->is_master = 0;
6c340eac0   Eric Anholt   drm: Replace filp...
546
  	list_del(&file_priv->lhead);
30e2fb188   Dave Airlie   sem2mutex: driver...
547
  	mutex_unlock(&dev->struct_mutex);
b5e89ed53   Dave Airlie   drm: lindent the ...
548

22eae947b   Dave Airlie   drm: rename drive...
549
  	if (dev->driver->postclose)
6c340eac0   Eric Anholt   drm: Replace filp...
550
  		dev->driver->postclose(dev, file_priv);
9a298b2ac   Eric Anholt   drm: Remove memor...
551
  	kfree(file_priv);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
552
553
554
555
  
  	/* ========================================================
  	 * End inline drm_release
  	 */
b5e89ed53   Dave Airlie   drm: lindent the ...
556
  	atomic_inc(&dev->counts[_DRM_STAT_CLOSES]);
b5e89ed53   Dave Airlie   drm: lindent the ...
557
  	if (!--dev->open_count) {
7c1c2871a   Dave Airlie   drm: move to kref...
558
559
560
561
  		if (atomic_read(&dev->ioctl_count)) {
  			DRM_ERROR("Device busy: %d
  ",
  				  atomic_read(&dev->ioctl_count));
58374713c   Arnd Bergmann   drm: kill BKL fro...
562
  			retcode = -EBUSY;
1a72d65d6   Chris Wilson   drm: Remove count...
563
564
  		} else
  			retcode = drm_lastclose(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
565
  	}
58374713c   Arnd Bergmann   drm: kill BKL fro...
566
  	mutex_unlock(&drm_global_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
567
568
569
570
  
  	return retcode;
  }
  EXPORT_SYMBOL(drm_release);
c9a9c5e02   Kristian Høgsberg   drm: Add async ev...
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
  static bool
  drm_dequeue_event(struct drm_file *file_priv,
  		  size_t total, size_t max, struct drm_pending_event **out)
  {
  	struct drm_device *dev = file_priv->minor->dev;
  	struct drm_pending_event *e;
  	unsigned long flags;
  	bool ret = false;
  
  	spin_lock_irqsave(&dev->event_lock, flags);
  
  	*out = NULL;
  	if (list_empty(&file_priv->event_list))
  		goto out;
  	e = list_first_entry(&file_priv->event_list,
  			     struct drm_pending_event, link);
  	if (e->event->length + total > max)
  		goto out;
  
  	file_priv->event_space += e->event->length;
  	list_del(&e->link);
  	*out = e;
  	ret = true;
  
  out:
  	spin_unlock_irqrestore(&dev->event_lock, flags);
  	return ret;
  }
  
  ssize_t drm_read(struct file *filp, char __user *buffer,
  		 size_t count, loff_t *offset)
  {
  	struct drm_file *file_priv = filp->private_data;
  	struct drm_pending_event *e;
  	size_t total;
  	ssize_t ret;
  
  	ret = wait_event_interruptible(file_priv->event_wait,
  				       !list_empty(&file_priv->event_list));
  	if (ret < 0)
  		return ret;
  
  	total = 0;
  	while (drm_dequeue_event(file_priv, total, count, &e)) {
  		if (copy_to_user(buffer + total,
  				 e->event, e->event->length)) {
  			total = -EFAULT;
  			break;
  		}
  
  		total += e->event->length;
  		e->destroy(e);
  	}
  
  	return total;
  }
  EXPORT_SYMBOL(drm_read);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
628
629
  unsigned int drm_poll(struct file *filp, struct poll_table_struct *wait)
  {
c9a9c5e02   Kristian Høgsberg   drm: Add async ev...
630
631
632
633
634
635
636
637
638
  	struct drm_file *file_priv = filp->private_data;
  	unsigned int mask = 0;
  
  	poll_wait(filp, &file_priv->event_wait, wait);
  
  	if (!list_empty(&file_priv->event_list))
  		mask |= POLLIN | POLLRDNORM;
  
  	return mask;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
639
  }
b5e89ed53   Dave Airlie   drm: lindent the ...
640
  EXPORT_SYMBOL(drm_poll);