From f9dd09c7f7199685601d75882447a6598be8a3e0 Mon Sep 17 00:00:00 2001 From: Jozsef Kadlecsik Date: Fri, 6 Nov 2009 00:43:42 -0800 Subject: netfilter: nf_nat: fix NAT issue in 2.6.30.4+ Vitezslav Samel discovered that since 2.6.30.4+ active FTP can not work over NAT. The "cause" of the problem was a fix of unacknowledged data detection with NAT (commit a3a9f79e361e864f0e9d75ebe2a0cb43d17c4272). However, actually, that fix uncovered a long standing bug in TCP conntrack: when NAT was enabled, we simply updated the max of the right edge of the segments we have seen (td_end), by the offset NAT produced with changing IP/port in the data. However, we did not update the other parameter (td_maxend) which is affected by the NAT offset. Thus that could drift away from the correct value and thus resulted breaking active FTP. The patch below fixes the issue by *not* updating the conntrack parameters from NAT, but instead taking into account the NAT offsets in conntrack in a consistent way. (Updating from NAT would be more harder and expensive because it'd need to re-calculate parameters we already calculated in conntrack.) Signed-off-by: Jozsef Kadlecsik Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- include/net/netfilter/nf_conntrack.h | 8 +++----- include/net/netfilter/nf_nat_helper.h | 4 ++++ 2 files changed, 7 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/net/netfilter/nf_conntrack.h b/include/net/netfilter/nf_conntrack.h index cbdd6284996..5cf7270e3ff 100644 --- a/include/net/netfilter/nf_conntrack.h +++ b/include/net/netfilter/nf_conntrack.h @@ -255,11 +255,9 @@ static inline bool nf_ct_kill(struct nf_conn *ct) } /* These are for NAT. Icky. */ -/* Update TCP window tracking data when NAT mangles the packet */ -extern void nf_conntrack_tcp_update(const struct sk_buff *skb, - unsigned int dataoff, - struct nf_conn *ct, int dir, - s16 offset); +extern s16 (*nf_ct_nat_offset)(const struct nf_conn *ct, + enum ip_conntrack_dir dir, + u32 seq); /* Fake conntrack entry for untracked connections */ extern struct nf_conn nf_conntrack_untracked; diff --git a/include/net/netfilter/nf_nat_helper.h b/include/net/netfilter/nf_nat_helper.h index 237a961f40e..4222220920a 100644 --- a/include/net/netfilter/nf_nat_helper.h +++ b/include/net/netfilter/nf_nat_helper.h @@ -32,4 +32,8 @@ extern int (*nf_nat_seq_adjust_hook)(struct sk_buff *skb, * to port ct->master->saved_proto. */ extern void nf_nat_follow_master(struct nf_conn *ct, struct nf_conntrack_expect *this); + +extern s16 nf_nat_get_offset(const struct nf_conn *ct, + enum ip_conntrack_dir dir, + u32 seq); #endif -- cgit v1.2.3 From 3806e94b0148350c72f9a3214274026b6ca03f49 Mon Sep 17 00:00:00 2001 From: Crane Cai Date: Sat, 7 Nov 2009 13:10:46 +0100 Subject: i2c-piix4: Modify code name SB900 to Hudson-2 Change SB900 to its formal code name Hudson-2. Signed-off-by: Crane Cai Signed-off-by: Jean Delvare --- Documentation/i2c/busses/i2c-piix4 | 2 +- drivers/i2c/busses/Kconfig | 2 +- drivers/i2c/busses/i2c-piix4.c | 8 ++++---- include/linux/pci_ids.h | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/Documentation/i2c/busses/i2c-piix4 b/Documentation/i2c/busses/i2c-piix4 index c5b37c57055..ac540c71c7e 100644 --- a/Documentation/i2c/busses/i2c-piix4 +++ b/Documentation/i2c/busses/i2c-piix4 @@ -8,7 +8,7 @@ Supported adapters: Datasheet: Only available via NDA from ServerWorks * ATI IXP200, IXP300, IXP400, SB600, SB700 and SB800 southbridges Datasheet: Not publicly available - * AMD SB900 + * AMD Hudson-2 Datasheet: Not publicly available * Standard Microsystems (SMSC) SLC90E66 (Victory66) southbridge Datasheet: Publicly available at the SMSC website http://www.smsc.com diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 737335ff2b2..e8fe7f169e2 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -128,7 +128,7 @@ config I2C_PIIX4 ATI SB600 ATI SB700 ATI SB800 - AMD SB900 + AMD Hudson-2 Serverworks OSB4 Serverworks CSB5 Serverworks CSB6 diff --git a/drivers/i2c/busses/i2c-piix4.c b/drivers/i2c/busses/i2c-piix4.c index d26a972aaca..1e245e9cad3 100644 --- a/drivers/i2c/busses/i2c-piix4.c +++ b/drivers/i2c/busses/i2c-piix4.c @@ -22,7 +22,7 @@ Intel PIIX4, 440MX Serverworks OSB4, CSB5, CSB6, HT-1000, HT-1100 ATI IXP200, IXP300, IXP400, SB600, SB700, SB800 - AMD SB900 + AMD Hudson-2 SMSC Victory66 Note: we assume there can only be one device, with one SMBus interface. @@ -233,9 +233,9 @@ static int __devinit piix4_setup_sb800(struct pci_dev *PIIX4_dev, unsigned short smba_idx = 0xcd6; u8 smba_en_lo, smba_en_hi, i2ccfg, i2ccfg_offset = 0x10, smb_en = 0x2c; - /* SB800 SMBus does not support forcing address */ + /* SB800 and later SMBus does not support forcing address */ if (force || force_addr) { - dev_err(&PIIX4_dev->dev, "SB800 SMBus does not support " + dev_err(&PIIX4_dev->dev, "SMBus does not support " "forcing address!\n"); return -EINVAL; } @@ -480,7 +480,7 @@ static struct pci_device_id piix4_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP300_SMBUS) }, { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP400_SMBUS) }, { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_SBX00_SMBUS) }, - { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_SB900_SMBUS) }, + { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_HUDSON2_SMBUS) }, { PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_OSB4) }, { PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index b0f0f3851cd..9ceb392cb98 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -543,7 +543,7 @@ #define PCI_DEVICE_ID_AMD_8131_BRIDGE 0x7450 #define PCI_DEVICE_ID_AMD_8131_APIC 0x7451 #define PCI_DEVICE_ID_AMD_8132_BRIDGE 0x7458 -#define PCI_DEVICE_ID_AMD_SB900_SMBUS 0x780b +#define PCI_DEVICE_ID_AMD_HUDSON2_SMBUS 0x780b #define PCI_DEVICE_ID_AMD_CS5535_IDE 0x208F #define PCI_DEVICE_ID_AMD_CS5536_ISA 0x2090 #define PCI_DEVICE_ID_AMD_CS5536_FLASH 0x2091 -- cgit v1.2.3 From afa08974fe80c198b8650f73ed8ab59135ca10d0 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Sat, 7 Nov 2009 13:10:46 +0100 Subject: i2c: Add an interface to lock/unlock an I2C bus segment Some drivers need to be able to prevent access to an I2C bus segment for a specific period of time. Add an interface for them to do so without twiddling with i2c-core internals. Signed-off-by: Jean Delvare Acked-by: Ben Hutchings --- drivers/net/sfc/sfe4001.c | 4 ++-- include/linux/i2c.h | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/net/sfc/sfe4001.c b/drivers/net/sfc/sfe4001.c index cee00ad49b5..49eb91b5f50 100644 --- a/drivers/net/sfc/sfe4001.c +++ b/drivers/net/sfc/sfe4001.c @@ -188,7 +188,7 @@ static int sfn4111t_reset(struct efx_nic *efx) efx_oword_t reg; /* GPIO 3 and the GPIO register are shared with I2C, so block that */ - mutex_lock(&efx->i2c_adap.bus_lock); + i2c_lock_adapter(&efx->i2c_adap); /* Pull RST_N (GPIO 2) low then let it up again, setting the * FLASH_CFG_1 strap (GPIO 3) appropriately. Only change the @@ -204,7 +204,7 @@ static int sfn4111t_reset(struct efx_nic *efx) falcon_write(efx, ®, GPIO_CTL_REG_KER); msleep(1); - mutex_unlock(&efx->i2c_adap.bus_lock); + i2c_unlock_adapter(&efx->i2c_adap); ssleep(1); return 0; diff --git a/include/linux/i2c.h b/include/linux/i2c.h index 57d41b0abce..7b40cda57a7 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -361,6 +361,24 @@ static inline void i2c_set_adapdata(struct i2c_adapter *dev, void *data) dev_set_drvdata(&dev->dev, data); } +/** + * i2c_lock_adapter - Prevent access to an I2C bus segment + * @adapter: Target I2C bus segment + */ +static inline void i2c_lock_adapter(struct i2c_adapter *adapter) +{ + mutex_lock(&adapter->bus_lock); +} + +/** + * i2c_unlock_adapter - Reauthorize access to an I2C bus segment + * @adapter: Target I2C bus segment + */ +static inline void i2c_unlock_adapter(struct i2c_adapter *adapter) +{ + mutex_unlock(&adapter->bus_lock); +} + /*flags for the client struct: */ #define I2C_CLIENT_PEC 0x04 /* Use Packet Error Checking */ #define I2C_CLIENT_TEN 0x10 /* we have a ten bit chip address */ -- cgit v1.2.3 From c9a9c5e02aedc1a2815877b0268f886d2640b771 Mon Sep 17 00:00:00 2001 From: Kristian Høgsberg Date: Sat, 12 Sep 2009 04:33:34 +1000 Subject: drm: Add async event synchronization for drmWaitVblank MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds a new flag to the drmWaitVblank ioctl, which asks the drm to return immediately and notify userspace when the specified vblank sequence happens by sending an event back on the drm fd. The event mechanism works with the other flags supported by the ioctls, specifically, the vblank sequence can be specified relatively or absolutely, and works for primary and seconday crtc. The signal field of the vblank request is used to provide user data, which will be sent back to user space in the vblank event. Signed-off-by: Kristian Høgsberg Reviewed-by: Jesse Barnes Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_fops.c | 98 ++++++++++++++++++++++++++++++++++++++++- drivers/gpu/drm/drm_irq.c | 95 +++++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/drm_stub.c | 2 + drivers/gpu/drm/i915/i915_drv.c | 1 + include/drm/drm.h | 33 +++++++++++++- include/drm/drmP.h | 26 +++++++++++ 6 files changed, 251 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c index 251bc0e3b5e..8ac7fbf6b2b 100644 --- a/drivers/gpu/drm/drm_fops.c +++ b/drivers/gpu/drm/drm_fops.c @@ -257,6 +257,9 @@ static int drm_open_helper(struct inode *inode, struct file *filp, INIT_LIST_HEAD(&priv->lhead); INIT_LIST_HEAD(&priv->fbs); + INIT_LIST_HEAD(&priv->event_list); + init_waitqueue_head(&priv->event_wait); + priv->event_space = 4096; /* set aside 4k for event buffer */ if (dev->driver->driver_features & DRIVER_GEM) drm_gem_open(dev, priv); @@ -413,6 +416,30 @@ static void drm_master_release(struct drm_device *dev, struct file *filp) } } +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); +} + /** * Release file. * @@ -451,6 +478,8 @@ int drm_release(struct inode *inode, struct file *filp) if (file_priv->minor->master) drm_master_release(dev, filp); + drm_events_release(file_priv); + if (dev->driver->driver_features & DRIVER_GEM) drm_gem_release(dev, file_priv); @@ -544,9 +573,74 @@ int drm_release(struct inode *inode, struct file *filp) } EXPORT_SYMBOL(drm_release); -/** No-op. */ +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); + unsigned int drm_poll(struct file *filp, struct poll_table_struct *wait) { - return 0; + 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; } EXPORT_SYMBOL(drm_poll); diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index 0a6f0b3bdc7..72754aca7ab 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c @@ -550,6 +550,62 @@ out: return ret; } +static int drm_queue_vblank_event(struct drm_device *dev, int pipe, + union drm_wait_vblank *vblwait, + struct drm_file *file_priv) +{ + struct drm_pending_vblank_event *e; + struct timeval now; + unsigned long flags; + unsigned int seq; + + e = kzalloc(sizeof *e, GFP_KERNEL); + if (e == NULL) + return -ENOMEM; + + e->pipe = pipe; + e->event.base.type = DRM_EVENT_VBLANK; + e->event.base.length = sizeof e->event; + e->event.user_data = vblwait->request.signal; + e->base.event = &e->event.base; + e->base.file_priv = file_priv; + e->base.destroy = (void (*) (struct drm_pending_event *)) kfree; + + do_gettimeofday(&now); + spin_lock_irqsave(&dev->event_lock, flags); + + if (file_priv->event_space < sizeof e->event) { + spin_unlock_irqrestore(&dev->event_lock, flags); + kfree(e); + return -ENOMEM; + } + + file_priv->event_space -= sizeof e->event; + seq = drm_vblank_count(dev, pipe); + if ((vblwait->request.type & _DRM_VBLANK_NEXTONMISS) && + (seq - vblwait->request.sequence) <= (1 << 23)) { + vblwait->request.sequence = seq + 1; + } + + DRM_DEBUG("event on vblank count %d, current %d, crtc %d\n", + vblwait->request.sequence, seq, pipe); + + e->event.sequence = vblwait->request.sequence; + if ((seq - vblwait->request.sequence) <= (1 << 23)) { + e->event.tv_sec = now.tv_sec; + e->event.tv_usec = now.tv_usec; + drm_vblank_put(dev, e->pipe); + list_add_tail(&e->base.link, &e->base.file_priv->event_list); + wake_up_interruptible(&e->base.file_priv->event_wait); + } else { + list_add_tail(&e->base.link, &dev->vblank_event_list); + } + + spin_unlock_irqrestore(&dev->event_lock, flags); + + return 0; +} + /** * Wait for VBLANK. * @@ -609,6 +665,9 @@ int drm_wait_vblank(struct drm_device *dev, void *data, goto done; } + if (flags & _DRM_VBLANK_EVENT) + return drm_queue_vblank_event(dev, crtc, vblwait, file_priv); + if ((flags & _DRM_VBLANK_NEXTONMISS) && (seq - vblwait->request.sequence) <= (1<<23)) { vblwait->request.sequence = seq + 1; @@ -641,6 +700,38 @@ done: return ret; } +void drm_handle_vblank_events(struct drm_device *dev, int crtc) +{ + struct drm_pending_vblank_event *e, *t; + struct timeval now; + unsigned long flags; + unsigned int seq; + + do_gettimeofday(&now); + seq = drm_vblank_count(dev, crtc); + + spin_lock_irqsave(&dev->event_lock, flags); + + list_for_each_entry_safe(e, t, &dev->vblank_event_list, base.link) { + if (e->pipe != crtc) + continue; + if ((seq - e->event.sequence) > (1<<23)) + continue; + + DRM_DEBUG("vblank event on %d, current %d\n", + e->event.sequence, seq); + + e->event.sequence = seq; + e->event.tv_sec = now.tv_sec; + e->event.tv_usec = now.tv_usec; + drm_vblank_put(dev, e->pipe); + list_move_tail(&e->base.link, &e->base.file_priv->event_list); + wake_up_interruptible(&e->base.file_priv->event_wait); + } + + spin_unlock_irqrestore(&dev->event_lock, flags); +} + /** * drm_handle_vblank - handle a vblank event * @dev: DRM device @@ -651,7 +742,11 @@ done: */ void drm_handle_vblank(struct drm_device *dev, int crtc) { + if (!dev->num_crtcs) + return; + atomic_inc(&dev->_vblank_count[crtc]); DRM_WAKEUP(&dev->vbl_queue[crtc]); + drm_handle_vblank_events(dev, crtc); } EXPORT_SYMBOL(drm_handle_vblank); diff --git a/drivers/gpu/drm/drm_stub.c b/drivers/gpu/drm/drm_stub.c index 55bb8a82d61..adb864dfef3 100644 --- a/drivers/gpu/drm/drm_stub.c +++ b/drivers/gpu/drm/drm_stub.c @@ -220,9 +220,11 @@ static int drm_fill_in_dev(struct drm_device * dev, struct pci_dev *pdev, INIT_LIST_HEAD(&dev->ctxlist); INIT_LIST_HEAD(&dev->vmalist); INIT_LIST_HEAD(&dev->maplist); + INIT_LIST_HEAD(&dev->vblank_event_list); spin_lock_init(&dev->count_lock); spin_lock_init(&dev->drw_lock); + spin_lock_init(&dev->event_lock); init_timer(&dev->timer); mutex_init(&dev->struct_mutex); mutex_init(&dev->ctxlist_mutex); diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 7f436ec075f..2fa21786205 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -333,6 +333,7 @@ static struct drm_driver driver = { .mmap = drm_gem_mmap, .poll = drm_poll, .fasync = drm_fasync, + .read = drm_read, #ifdef CONFIG_COMPAT .compat_ioctl = i915_compat_ioctl, #endif diff --git a/include/drm/drm.h b/include/drm/drm.h index 7cb50bdde46..fa6d9155873 100644 --- a/include/drm/drm.h +++ b/include/drm/drm.h @@ -454,6 +454,7 @@ struct drm_irq_busid { enum drm_vblank_seq_type { _DRM_VBLANK_ABSOLUTE = 0x0, /**< Wait for specific vblank sequence number */ _DRM_VBLANK_RELATIVE = 0x1, /**< Wait for given number of vblanks */ + _DRM_VBLANK_EVENT = 0x4000000, /**< Send event instead of blocking */ _DRM_VBLANK_FLIP = 0x8000000, /**< Scheduled buffer swap should flip */ _DRM_VBLANK_NEXTONMISS = 0x10000000, /**< If missed, wait for next vblank */ _DRM_VBLANK_SECONDARY = 0x20000000, /**< Secondary display controller */ @@ -461,8 +462,8 @@ enum drm_vblank_seq_type { }; #define _DRM_VBLANK_TYPES_MASK (_DRM_VBLANK_ABSOLUTE | _DRM_VBLANK_RELATIVE) -#define _DRM_VBLANK_FLAGS_MASK (_DRM_VBLANK_SIGNAL | _DRM_VBLANK_SECONDARY | \ - _DRM_VBLANK_NEXTONMISS) +#define _DRM_VBLANK_FLAGS_MASK (_DRM_VBLANK_EVENT | _DRM_VBLANK_SIGNAL | \ + _DRM_VBLANK_SECONDARY | _DRM_VBLANK_NEXTONMISS) struct drm_wait_vblank_request { enum drm_vblank_seq_type type; @@ -698,6 +699,34 @@ struct drm_gem_open { #define DRM_COMMAND_BASE 0x40 #define DRM_COMMAND_END 0xA0 +/** + * Header for events written back to userspace on the drm fd. The + * type defines the type of event, the length specifies the total + * length of the event (including the header), and user_data is + * typically a 64 bit value passed with the ioctl that triggered the + * event. A read on the drm fd will always only return complete + * events, that is, if for example the read buffer is 100 bytes, and + * there are two 64 byte events pending, only one will be returned. + * + * Event types 0 - 0x7fffffff are generic drm events, 0x80000000 and + * up are chipset specific. + */ +struct drm_event { + __u32 type; + __u32 length; +}; + +#define DRM_EVENT_VBLANK 0x01 + +struct drm_event_vblank { + struct drm_event base; + __u64 user_data; + __u32 tv_sec; + __u32 tv_usec; + __u32 sequence; + __u32 reserved; +}; + /* typedef area */ #ifndef __KERNEL__ typedef struct drm_clip_rect drm_clip_rect_t; diff --git a/include/drm/drmP.h b/include/drm/drmP.h index c8e64bbadbc..b0b36838ab1 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -426,6 +426,14 @@ struct drm_buf_entry { struct drm_freelist freelist; }; +/* Event queued up for userspace to read */ +struct drm_pending_event { + struct drm_event *event; + struct list_head link; + struct drm_file *file_priv; + void (*destroy)(struct drm_pending_event *event); +}; + /** File private data */ struct drm_file { int authenticated; @@ -449,6 +457,10 @@ struct drm_file { struct drm_master *master; /* master this node is currently associated with N.B. not always minor->master */ struct list_head fbs; + + wait_queue_head_t event_wait; + struct list_head event_list; + int event_space; }; /** Wait queue */ @@ -900,6 +912,12 @@ struct drm_minor { struct drm_mode_group mode_group; }; +struct drm_pending_vblank_event { + struct drm_pending_event base; + int pipe; + struct drm_event_vblank event; +}; + /** * DRM device structure. This structure represent a complete card that * may contain multiple heads. @@ -999,6 +1017,12 @@ struct drm_device { u32 max_vblank_count; /**< size of vblank counter register */ + /** + * List of events + */ + struct list_head vblank_event_list; + spinlock_t event_lock; + /*@} */ cycles_t ctx_start; cycles_t lck_start; @@ -1135,6 +1159,8 @@ extern int drm_lastclose(struct drm_device *dev); extern int drm_open(struct inode *inode, struct file *filp); extern int drm_stub_open(struct inode *inode, struct file *filp); extern int drm_fasync(int fd, struct file *filp, int on); +extern ssize_t drm_read(struct file *filp, char __user *buffer, + size_t count, loff_t *offset); extern int drm_release(struct inode *inode, struct file *filp); /* Mapping support (drm_vm.h) */ -- cgit v1.2.3 From 420a457088669e055e767dfb8468909cd1799cf9 Mon Sep 17 00:00:00 2001 From: Andres Salomon Date: Tue, 17 Nov 2009 14:41:23 -0800 Subject: drm: kill some unused DRM_PROC macros from drmP.h i915_gem_proc.c appears to have been the last user of the DRM_PROC_* macros, and it has gone away. The macros should die as well. Signed-off-by: Andres Salomon Signed-off-by: Andrew Morton Signed-off-by: Dave Airlie --- include/drm/drmP.h | 10 ---------- 1 file changed, 10 deletions(-) (limited to 'include') diff --git a/include/drm/drmP.h b/include/drm/drmP.h index b0b36838ab1..4977fa9038d 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -245,16 +245,6 @@ extern void drm_ut_debug_printk(unsigned int request_level, #endif -#define DRM_PROC_LIMIT (PAGE_SIZE-80) - -#define DRM_PROC_PRINT(fmt, arg...) \ - len += sprintf(&buf[len], fmt , ##arg); \ - if (len > DRM_PROC_LIMIT) { *eof = 1; return len - offset; } - -#define DRM_PROC_PRINT_RET(ret, fmt, arg...) \ - len += sprintf(&buf[len], fmt , ##arg); \ - if (len > DRM_PROC_LIMIT) { ret; *eof = 1; return len - offset; } - /*@}*/ /***********************************************************************/ -- cgit v1.2.3 From 156822f7175d9ceb9d7e808502d3c5de8841e047 Mon Sep 17 00:00:00 2001 From: Andres Salomon Date: Tue, 17 Nov 2009 14:41:23 -0800 Subject: drm: kill more unused DRM macros There are a few more macros in drmP.h that are unused; DRM_GET_PRIV_SAREA, DRM_ARRAY_SIZE, and DRM_WAITCOUNT can go away completely. Unfortunately, DRM_COPY is still used in one place, but we can at least move it to where it's used. It's an awful looking macro.. [akpm: fix overeagerness] Signed-off-by: Andres Salomon Cc: Dave Airlie Signed-off-by: Andrew Morton Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_drv.c | 12 ++++++++++++ include/drm/drmP.h | 23 ----------------------- 2 files changed, 12 insertions(+), 23 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index a75ca63deea..43297ca45f3 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -365,6 +365,18 @@ static void __exit drm_core_exit(void) module_init(drm_core_init); module_exit(drm_core_exit); +/** + * Copy and IOCTL return string to user space + */ +#define DRM_COPY(name, value) \ + len = strlen(value); \ + if (len > name##_len) len = name##_len; \ + name##_len = strlen(value); \ + if (len && name) { \ + if (copy_to_user(name, value, len)) \ + return -EFAULT; \ + } + /** * Get version information * diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 4977fa9038d..1b72a526ba6 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -255,19 +255,8 @@ extern void drm_ut_debug_printk(unsigned int request_level, #define DRM_LEFTCOUNT(x) (((x)->rp + (x)->count - (x)->wp) % ((x)->count + 1)) #define DRM_BUFCOUNT(x) ((x)->count - DRM_LEFTCOUNT(x)) -#define DRM_WAITCOUNT(dev,idx) DRM_BUFCOUNT(&dev->queuelist[idx]->waitlist) #define DRM_IF_VERSION(maj, min) (maj << 16 | min) -/** - * Get the private SAREA mapping. - * - * \param _dev DRM device. - * \param _ctx context number. - * \param _map output mapping. - */ -#define DRM_GET_PRIV_SAREA(_dev, _ctx, _map) do { \ - (_map) = (_dev)->context_sareas[_ctx]; \ -} while(0) /** * Test that the hardware lock is held by the caller, returning otherwise. @@ -286,18 +275,6 @@ do { \ } \ } while (0) -/** - * Copy and IOCTL return string to user space - */ -#define DRM_COPY( name, value ) \ - len = strlen( value ); \ - if ( len > name##_len ) len = name##_len; \ - name##_len = strlen( value ); \ - if ( len && name ) { \ - if ( copy_to_user( name, value, len ) ) \ - return -EFAULT; \ - } - /** * Ioctl function type. * -- cgit v1.2.3 From d91d8a3f88059d93e34ac70d059153ec69a9ffc7 Mon Sep 17 00:00:00 2001 From: Kristian Høgsberg Date: Tue, 17 Nov 2009 12:43:55 -0500 Subject: drm/kms: add page flipping ioctl MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds a page flipping ioctl to the KMS API. The ioctl takes an fb ID and a ctrc ID and flips the crtc to the given fb at the next vblank. The ioctl returns immediately but the flip doesn't happen until after any rendering that's currently queued up against the new framebuffer is done. After submitting a page flip, any execbuffer involving the old front buffer will block until the flip is completed. Optionally, a vblank event can be generated when the swap eventually happens. Signed-off-by: Kristian Høgsberg Reviewed-by: Chris Wilson Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_crtc.c | 69 ++++++++++++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/drm_drv.c | 1 + include/drm/drm.h | 1 + include/drm/drm_crtc.h | 16 +++++++++++ include/drm/drm_mode.h | 33 ++++++++++++++++++++++ 5 files changed, 120 insertions(+) (limited to 'include') diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 5cae0b3eee9..32756e67dd5 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -2478,3 +2478,72 @@ out: mutex_unlock(&dev->mode_config.mutex); return ret; } + +int drm_mode_page_flip_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv) +{ + struct drm_mode_crtc_page_flip *page_flip = data; + struct drm_mode_object *obj; + struct drm_crtc *crtc; + struct drm_framebuffer *fb; + struct drm_pending_vblank_event *e = NULL; + unsigned long flags; + int ret = -EINVAL; + + if (page_flip->flags & ~DRM_MODE_PAGE_FLIP_FLAGS || + page_flip->reserved != 0) + return -EINVAL; + + mutex_lock(&dev->mode_config.mutex); + obj = drm_mode_object_find(dev, page_flip->crtc_id, DRM_MODE_OBJECT_CRTC); + if (!obj) + goto out; + crtc = obj_to_crtc(obj); + + if (crtc->funcs->page_flip == NULL) + goto out; + + obj = drm_mode_object_find(dev, page_flip->fb_id, DRM_MODE_OBJECT_FB); + if (!obj) + goto out; + fb = obj_to_fb(obj); + + if (page_flip->flags & DRM_MODE_PAGE_FLIP_EVENT) { + ret = -ENOMEM; + spin_lock_irqsave(&dev->event_lock, flags); + if (file_priv->event_space < sizeof e->event) { + spin_unlock_irqrestore(&dev->event_lock, flags); + goto out; + } + file_priv->event_space -= sizeof e->event; + spin_unlock_irqrestore(&dev->event_lock, flags); + + e = kzalloc(sizeof *e, GFP_KERNEL); + if (e == NULL) { + spin_lock_irqsave(&dev->event_lock, flags); + file_priv->event_space += sizeof e->event; + spin_unlock_irqrestore(&dev->event_lock, flags); + goto out; + } + + e->event.base.type = DRM_EVENT_VBLANK; + e->event.base.length = sizeof e->event; + e->event.user_data = page_flip->user_data; + e->base.event = &e->event.base; + e->base.file_priv = file_priv; + e->base.destroy = + (void (*) (struct drm_pending_event *)) kfree; + } + + ret = crtc->funcs->page_flip(crtc, fb, e); + if (ret) { + spin_lock_irqsave(&dev->event_lock, flags); + file_priv->event_space += sizeof e->event; + spin_unlock_irqrestore(&dev->event_lock, flags); + kfree(e); + } + +out: + mutex_unlock(&dev->mode_config.mutex); + return ret; +} diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index 5bd3f9461e2..bfaf59b02bd 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -145,6 +145,7 @@ static struct drm_ioctl_desc drm_ioctls[] = { DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETFB, drm_mode_getfb, DRM_MASTER|DRM_CONTROL_ALLOW), DRM_IOCTL_DEF(DRM_IOCTL_MODE_ADDFB, drm_mode_addfb, DRM_MASTER|DRM_CONTROL_ALLOW), DRM_IOCTL_DEF(DRM_IOCTL_MODE_RMFB, drm_mode_rmfb, DRM_MASTER|DRM_CONTROL_ALLOW), + DRM_IOCTL_DEF(DRM_IOCTL_MODE_PAGE_FLIP, drm_mode_page_flip_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW), }; #define DRM_CORE_IOCTL_COUNT ARRAY_SIZE( drm_ioctls ) diff --git a/include/drm/drm.h b/include/drm/drm.h index fa6d9155873..3919a4f792a 100644 --- a/include/drm/drm.h +++ b/include/drm/drm.h @@ -687,6 +687,7 @@ struct drm_gem_open { #define DRM_IOCTL_MODE_GETFB DRM_IOWR(0xAD, struct drm_mode_fb_cmd) #define DRM_IOCTL_MODE_ADDFB DRM_IOWR(0xAE, struct drm_mode_fb_cmd) #define DRM_IOCTL_MODE_RMFB DRM_IOWR(0xAF, unsigned int) +#define DRM_IOCTL_MODE_PAGE_FLIP DRM_IOWR(0xB0, struct drm_mode_crtc_page_flip) /** * Device specific ioctls should only be in their respective headers diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index b69347b8904..4cc8a32dc4c 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -290,6 +290,7 @@ struct drm_property { struct drm_crtc; struct drm_connector; struct drm_encoder; +struct drm_pending_vblank_event; /** * drm_crtc_funcs - control CRTCs for a given device @@ -333,6 +334,19 @@ struct drm_crtc_funcs { void (*destroy)(struct drm_crtc *crtc); int (*set_config)(struct drm_mode_set *set); + + /* + * Flip to the given framebuffer. This implements the page + * flip ioctl descibed in drm_mode.h, specifically, the + * implementation must return immediately and block all + * rendering to the current fb until the flip has completed. + * If userspace set the event flag in the ioctl, the event + * argument will point to an event to send back when the flip + * completes, otherwise it will be NULL. + */ + int (*page_flip)(struct drm_crtc *crtc, + struct drm_framebuffer *fb, + struct drm_pending_vblank_event *event); }; /** @@ -756,6 +770,8 @@ extern int drm_mode_gamma_get_ioctl(struct drm_device *dev, extern int drm_mode_gamma_set_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); extern bool drm_detect_hdmi_monitor(struct edid *edid); +extern int drm_mode_page_flip_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv); extern struct drm_display_mode *drm_cvt_mode(struct drm_device *dev, int hdisplay, int vdisplay, int vrefresh, bool reduced, bool interlaced, bool margins); diff --git a/include/drm/drm_mode.h b/include/drm/drm_mode.h index 1f908416aed..68ddc6175ae 100644 --- a/include/drm/drm_mode.h +++ b/include/drm/drm_mode.h @@ -268,4 +268,37 @@ struct drm_mode_crtc_lut { __u64 blue; }; +#define DRM_MODE_PAGE_FLIP_EVENT 0x01 +#define DRM_MODE_PAGE_FLIP_FLAGS DRM_MODE_PAGE_FLIP_EVENT + +/* + * Request a page flip on the specified crtc. + * + * This ioctl will ask KMS to schedule a page flip for the specified + * crtc. Once any pending rendering targeting the specified fb (as of + * ioctl time) has completed, the crtc will be reprogrammed to display + * that fb after the next vertical refresh. The ioctl returns + * immediately, but subsequent rendering to the current fb will block + * in the execbuffer ioctl until the page flip happens. If a page + * flip is already pending as the ioctl is called, EBUSY will be + * returned. + * + * The ioctl supports one flag, DRM_MODE_PAGE_FLIP_EVENT, which will + * request that drm sends back a vblank event (see drm.h: struct + * drm_event_vblank) when the page flip is done. The user_data field + * passed in with this ioctl will be returned as the user_data field + * in the vblank event struct. + * + * The reserved field must be zero until we figure out something + * clever to use it for. + */ + +struct drm_mode_crtc_page_flip { + __u32 crtc_id; + __u32 fb_id; + __u32 flags; + __u32 reserved; + __u64 user_data; +}; + #endif -- cgit v1.2.3