From 1716a96101c49186bb0b8491922fd3e69030235f Mon Sep 17 00:00:00 2001 From: Gao feng Date: Fri, 6 Apr 2012 00:13:10 +0000 Subject: ipv6: fix problem with expired dst cache If the ipv6 dst cache which copy from the dst generated by ICMPV6 RA packet. this dst cache will not check expire because it has no RTF_EXPIRES flag. So this dst cache will always be used until the dst gc run. Change the struct dst_entry,add a union contains new pointer from and expires. When rt6_info.rt6i_flags has no RTF_EXPIRES flag,the dst.expires has no use. we can use this field to point to where the dst cache copy from. The dst.from is only used in IPV6. rt6_check_expired check if rt6_info.dst.from is expired. ip6_rt_copy only set dst.from when the ort has flag RTF_ADDRCONF and RTF_DEFAULT.then hold the ort. ip6_dst_destroy release the ort. Add some functions to operate the RTF_EXPIRES flag and expires(from) together. and change the code to use these new adding functions. Changes from v5: modify ip6_route_add and ndisc_router_discovery to use new adding functions. Only set dst.from when the ort has flag RTF_ADDRCONF and RTF_DEFAULT.then hold the ort. Signed-off-by: Gao feng Signed-off-by: David S. Miller --- include/net/dst.h | 6 +++++- include/net/ip6_fib.h | 42 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/dst.h b/include/net/dst.h index 59c5d18cc385..ff4da42fcfc6 100644 --- a/include/net/dst.h +++ b/include/net/dst.h @@ -36,7 +36,11 @@ struct dst_entry { struct net_device *dev; struct dst_ops *ops; unsigned long _metrics; - unsigned long expires; + union { + unsigned long expires; + /* point to where the dst_entry copied from */ + struct dst_entry *from; + }; struct dst_entry *path; struct neighbour __rcu *_neighbour; #ifdef CONFIG_XFRM diff --git a/include/net/ip6_fib.h b/include/net/ip6_fib.h index b26bb8101981..c64778fd5e13 100644 --- a/include/net/ip6_fib.h +++ b/include/net/ip6_fib.h @@ -123,6 +123,48 @@ static inline struct inet6_dev *ip6_dst_idev(struct dst_entry *dst) return ((struct rt6_info *)dst)->rt6i_idev; } +static inline void rt6_clean_expires(struct rt6_info *rt) +{ + if (!(rt->rt6i_flags & RTF_EXPIRES) && rt->dst.from) + dst_release(rt->dst.from); + + rt->rt6i_flags &= ~RTF_EXPIRES; + rt->dst.expires = 0; +} + +static inline void rt6_set_expires(struct rt6_info *rt, unsigned long expires) +{ + if (!(rt->rt6i_flags & RTF_EXPIRES) && rt->dst.from) + dst_release(rt->dst.from); + + rt->rt6i_flags |= RTF_EXPIRES; + rt->dst.expires = expires; +} + +static inline void rt6_update_expires(struct rt6_info *rt, int timeout) +{ + if (!(rt->rt6i_flags & RTF_EXPIRES) && rt->dst.from) + dst_release(rt->dst.from); + + dst_set_expires(&rt->dst, timeout); + rt->rt6i_flags |= RTF_EXPIRES; +} + +static inline void rt6_set_from(struct rt6_info *rt, struct rt6_info *from) +{ + struct dst_entry *new = (struct dst_entry *) from; + + if (!(rt->rt6i_flags & RTF_EXPIRES) && rt->dst.from) { + if (new == rt->dst.from) + return; + dst_release(rt->dst.from); + } + + rt->rt6i_flags &= ~RTF_EXPIRES; + rt->dst.from = new; + dst_hold(new); +} + struct fib6_walker_t { struct list_head lh; struct fib6_node *root, *node; -- cgit v1.2.3 From ca8f4fb21d08747013cce9cf1840aa5bfc31f2d8 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Mon, 9 Apr 2012 00:24:02 +0000 Subject: skbuff: struct ubuf_info callback type safety The skb struct ubuf_info callback gets passed struct ubuf_info itself, not the arg value as the field name and the function signature seem to imply. Rename the arg field to ctx to match usage, add documentation and change the callback argument type to make usage clear and to have compiler check correctness. Signed-off-by: Michael S. Tsirkin Signed-off-by: David S. Miller --- drivers/vhost/net.c | 2 +- drivers/vhost/vhost.c | 5 ++--- drivers/vhost/vhost.h | 2 +- include/linux/skbuff.h | 7 ++++--- 4 files changed, 8 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c index f0da2c32fbde..1f21d2a1e528 100644 --- a/drivers/vhost/net.c +++ b/drivers/vhost/net.c @@ -238,7 +238,7 @@ static void handle_tx(struct vhost_net *net) vq->heads[vq->upend_idx].len = len; ubuf->callback = vhost_zerocopy_callback; - ubuf->arg = vq->ubufs; + ubuf->ctx = vq->ubufs; ubuf->desc = vq->upend_idx; msg.msg_control = ubuf; msg.msg_controllen = sizeof(ubuf); diff --git a/drivers/vhost/vhost.c b/drivers/vhost/vhost.c index 947f00d8e091..51e4c1eeec4f 100644 --- a/drivers/vhost/vhost.c +++ b/drivers/vhost/vhost.c @@ -1598,10 +1598,9 @@ void vhost_ubuf_put_and_wait(struct vhost_ubuf_ref *ubufs) kfree(ubufs); } -void vhost_zerocopy_callback(void *arg) +void vhost_zerocopy_callback(struct ubuf_info *ubuf) { - struct ubuf_info *ubuf = arg; - struct vhost_ubuf_ref *ubufs = ubuf->arg; + struct vhost_ubuf_ref *ubufs = ubuf->ctx; struct vhost_virtqueue *vq = ubufs->vq; /* set len = 1 to mark this desc buffers done DMA */ diff --git a/drivers/vhost/vhost.h b/drivers/vhost/vhost.h index 8dcf4cca6bf2..8de1fd5b8efb 100644 --- a/drivers/vhost/vhost.h +++ b/drivers/vhost/vhost.h @@ -188,7 +188,7 @@ bool vhost_enable_notify(struct vhost_dev *, struct vhost_virtqueue *); int vhost_log_write(struct vhost_virtqueue *vq, struct vhost_log *log, unsigned int log_num, u64 len); -void vhost_zerocopy_callback(void *arg); +void vhost_zerocopy_callback(struct ubuf_info *); int vhost_zerocopy_signal_used(struct vhost_virtqueue *vq); #define vq_err(vq, fmt, ...) do { \ diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 70a3f8d49118..775292a66fa4 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -238,11 +238,12 @@ enum { /* * The callback notifies userspace to release buffers when skb DMA is done in * lower device, the skb last reference should be 0 when calling this. - * The desc is used to track userspace buffer index. + * The ctx field is used to track device context. + * The desc field is used to track userspace buffer index. */ struct ubuf_info { - void (*callback)(void *); - void *arg; + void (*callback)(struct ubuf_info *); + void *ctx; unsigned long desc; }; -- cgit v1.2.3 From 4362aaf6054b9760652c7047cdf6fa852acb6cf7 Mon Sep 17 00:00:00 2001 From: David Ward Date: Mon, 16 Apr 2012 03:17:22 +0000 Subject: net_sched: red: Make minor corrections to comments Signed-off-by: David Ward Signed-off-by: David S. Miller --- include/net/red.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/net/red.h b/include/net/red.h index 77d4c3745cb5..ef46058d35bf 100644 --- a/include/net/red.h +++ b/include/net/red.h @@ -245,7 +245,7 @@ static inline unsigned long red_calc_qavg_from_idle_time(const struct red_parms * * dummy packets as a burst after idle time, i.e. * - * p->qavg *= (1-W)^m + * v->qavg *= (1-W)^m * * This is an apparently overcomplicated solution (f.e. we have to * precompute a table to make this calculation in reasonable time) @@ -279,7 +279,7 @@ static inline unsigned long red_calc_qavg_no_idle_time(const struct red_parms *p unsigned int backlog) { /* - * NOTE: p->qavg is fixed point number with point at Wlog. + * NOTE: v->qavg is fixed point number with point at Wlog. * The formula below is equvalent to floating point * version: * @@ -390,7 +390,7 @@ static inline void red_adaptative_algo(struct red_parms *p, struct red_vars *v) if (red_is_idling(v)) qavg = red_calc_qavg_from_idle_time(p, v); - /* p->qavg is fixed point number with point at Wlog */ + /* v->qavg is fixed point number with point at Wlog */ qavg >>= p->Wlog; if (qavg > p->target_max && p->max_P <= MAX_P_MAX) -- cgit v1.2.3 From edfb5d4687d587c9f714799c7ee27517118e12e6 Mon Sep 17 00:00:00 2001 From: Jiri Bohac Date: Mon, 16 Apr 2012 03:34:39 +0000 Subject: ipv6: fix rt6_update_expires Commit 1716a961 (ipv6: fix problem with expired dst cache) broke PMTU discovery. rt6_update_expires() calls dst_set_expires(), which only updates dst->expires if it has not been set previously (expires == 0) or if the new expires is earlier than the current dst->expires. rt6_update_expires() needs to zero rt->dst.expires, otherwise it will contain ivalid data left over from rt->dst.from and will confuse dst_set_expires(). Signed-off-by: Jiri Bohac Signed-off-by: David S. Miller --- include/net/ip6_fib.h | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/ip6_fib.h b/include/net/ip6_fib.h index c64778fd5e13..cb8da1dac512 100644 --- a/include/net/ip6_fib.h +++ b/include/net/ip6_fib.h @@ -143,8 +143,14 @@ static inline void rt6_set_expires(struct rt6_info *rt, unsigned long expires) static inline void rt6_update_expires(struct rt6_info *rt, int timeout) { - if (!(rt->rt6i_flags & RTF_EXPIRES) && rt->dst.from) - dst_release(rt->dst.from); + if (!(rt->rt6i_flags & RTF_EXPIRES)) { + if (rt->dst.from) + dst_release(rt->dst.from); + /* dst_set_expires relies on expires == 0 + * if it has not been set previously. + */ + rt->dst.expires = 0; + } dst_set_expires(&rt->dst, timeout); rt->rt6i_flags |= RTF_EXPIRES; -- cgit v1.2.3 From cda31e10baf47a8a7d9360d9488fb76294be1ca3 Mon Sep 17 00:00:00 2001 From: Jiri Bohac Date: Mon, 16 Apr 2012 03:35:41 +0000 Subject: ipv6: clean up rt6_clean_expires Functionally, this change is a NOP. Semantically, rt6_clean_expires() wants to do rt->dst.from = NULL instead of rt->dst.expires = 0. It is clearing the RTF_EXPIRES flag, so the union is going to be treated as a pointer (dst.from) not a long (dst.expires). Signed-off-by: Jiri Bohac Signed-off-by: David S. Miller --- include/net/ip6_fib.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/ip6_fib.h b/include/net/ip6_fib.h index cb8da1dac512..0ae759a6c76e 100644 --- a/include/net/ip6_fib.h +++ b/include/net/ip6_fib.h @@ -129,7 +129,7 @@ static inline void rt6_clean_expires(struct rt6_info *rt) dst_release(rt->dst.from); rt->rt6i_flags &= ~RTF_EXPIRES; - rt->dst.expires = 0; + rt->dst.from = NULL; } static inline void rt6_set_expires(struct rt6_info *rt, unsigned long expires) -- cgit v1.2.3 From d3d4f0a025e621b82da08a76df4036d4267739dd Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Tue, 17 Apr 2012 14:03:53 +0000 Subject: net/sock.h: fix sk_peek_off kernel-doc warning Fix kernel-doc warning in net/sock.h: Warning(include/net/sock.h:377): No description found for parameter 'sk_peek_off' Signed-off-by: Randy Dunlap Signed-off-by: David S. Miller --- include/net/sock.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/net/sock.h b/include/net/sock.h index a6ba1f8871fd..188532ee88b6 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -246,6 +246,7 @@ struct cg_proto; * @sk_user_data: RPC layer private data * @sk_sndmsg_page: cached page for sendmsg * @sk_sndmsg_off: cached offset for sendmsg + * @sk_peek_off: current peek_offset value * @sk_send_head: front of stuff to transmit * @sk_security: used by security modules * @sk_mark: generic packet mark -- cgit v1.2.3 From 95b72eb0bdef6476b7e73061f0382adf46c5495a Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Fri, 20 Apr 2012 19:24:51 -0400 Subject: NFSv4: Ensure we do not reuse open owner names The NFSv4 spec is ambiguous about whether or not it is permissible to reuse open owner names, so play it safe. This patch adds a timestamp to the state_owner structure, and combines that with the IDA based uniquifier. Fixes a regression whereby the Linux server returns NFS4ERR_BAD_SEQID. Signed-off-by: Trond Myklebust --- fs/nfs/nfs4_fs.h | 1 + fs/nfs/nfs4proc.c | 6 +++--- fs/nfs/nfs4state.c | 1 + fs/nfs/nfs4xdr.c | 9 +++++---- include/linux/nfs_xdr.h | 7 ++++++- 5 files changed, 16 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index 97ecc863dd76..b6db9e33fb7b 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -59,6 +59,7 @@ struct nfs_unique_id { #define NFS_SEQID_CONFIRMED 1 struct nfs_seqid_counter { + ktime_t create_time; int owner_id; int flags; u32 counter; diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index f875cf305237..60d5f4c26dda 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -838,7 +838,8 @@ static struct nfs4_opendata *nfs4_opendata_alloc(struct dentry *dentry, p->o_arg.open_flags = flags; p->o_arg.fmode = fmode & (FMODE_READ|FMODE_WRITE); p->o_arg.clientid = server->nfs_client->cl_clientid; - p->o_arg.id = sp->so_seqid.owner_id; + p->o_arg.id.create_time = ktime_to_ns(sp->so_seqid.create_time); + p->o_arg.id.uniquifier = sp->so_seqid.owner_id; p->o_arg.name = &dentry->d_name; p->o_arg.server = server; p->o_arg.bitmask = server->attr_bitmask; @@ -1466,8 +1467,7 @@ static void nfs4_open_prepare(struct rpc_task *task, void *calldata) goto unlock_no_action; rcu_read_unlock(); } - /* Update sequence id. */ - data->o_arg.id = sp->so_seqid.owner_id; + /* Update client id. */ data->o_arg.clientid = sp->so_server->nfs_client->cl_clientid; if (data->o_arg.claim == NFS4_OPEN_CLAIM_PREVIOUS) { task->tk_msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_NOATTR]; diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index 0f43414eb25a..3b07f094f3a9 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -393,6 +393,7 @@ nfs4_remove_state_owner_locked(struct nfs4_state_owner *sp) static void nfs4_init_seqid_counter(struct nfs_seqid_counter *sc) { + sc->create_time = ktime_get(); sc->flags = 0; sc->counter = 0; spin_lock_init(&sc->lock); diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index c74fdb114b48..77fc5f959c4e 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -74,7 +74,7 @@ static int nfs4_stat_to_errno(int); /* lock,open owner id: * we currently use size 2 (u64) out of (NFS4_OPAQUE_LIMIT >> 2) */ -#define open_owner_id_maxsz (1 + 1 + 4) +#define open_owner_id_maxsz (1 + 2 + 1 + 1 + 2) #define lock_owner_id_maxsz (1 + 1 + 4) #define decode_lockowner_maxsz (1 + XDR_QUADLEN(IDMAP_NAMESZ)) #define compound_encode_hdr_maxsz (3 + (NFS4_MAXTAGLEN >> 2)) @@ -1340,12 +1340,13 @@ static inline void encode_openhdr(struct xdr_stream *xdr, const struct nfs_opena */ encode_nfs4_seqid(xdr, arg->seqid); encode_share_access(xdr, arg->fmode); - p = reserve_space(xdr, 32); + p = reserve_space(xdr, 36); p = xdr_encode_hyper(p, arg->clientid); - *p++ = cpu_to_be32(20); + *p++ = cpu_to_be32(24); p = xdr_encode_opaque_fixed(p, "open id:", 8); *p++ = cpu_to_be32(arg->server->s_dev); - xdr_encode_hyper(p, arg->id); + *p++ = cpu_to_be32(arg->id.uniquifier); + xdr_encode_hyper(p, arg->id.create_time); } static inline void encode_createmode(struct xdr_stream *xdr, const struct nfs_openargs *arg) diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index bfd0d1bf6707..7ba3551a0414 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -312,6 +312,11 @@ struct nfs4_layoutreturn { int rpc_status; }; +struct stateowner_id { + __u64 create_time; + __u32 uniquifier; +}; + /* * Arguments to the open call. */ @@ -321,7 +326,7 @@ struct nfs_openargs { int open_flags; fmode_t fmode; __u64 clientid; - __u64 id; + struct stateowner_id id; union { struct { struct iattr * attrs; /* UNCHECKED, GUARDED */ -- cgit v1.2.3 From 3fca40c704dd013797f2c0c518f37cd2cc8e19fe Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 19 Apr 2012 17:29:42 +0000 Subject: irq: Add IRQ_TYPE_DEFAULT for use by PIC drivers This is meant typically to allow a PIC driver's irq domain map() callback to establish sane defaults for the interrupt (and make sure that the HW and the irq_desc are in sync as far as the trigger is concerned). The irq core may not call the set_trigger callback if it thinks the trigger is already set to the right setting, so we need to ensure new descriptors are properly synchronized with the hardware. Signed-off-by: Benjamin Herrenschmidt --- include/linux/irq.h | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'include') diff --git a/include/linux/irq.h b/include/linux/irq.h index 7810406f3d80..b27cfcfd3a59 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h @@ -49,6 +49,12 @@ typedef void (*irq_preflow_handler_t)(struct irq_data *data); * IRQ_TYPE_LEVEL_LOW - low level triggered * IRQ_TYPE_LEVEL_MASK - Mask to filter out the level bits * IRQ_TYPE_SENSE_MASK - Mask for all the above bits + * IRQ_TYPE_DEFAULT - For use by some PICs to ask irq_set_type + * to setup the HW to a sane default (used + * by irqdomain map() callbacks to synchronize + * the HW state and SW flags for a newly + * allocated descriptor). + * * IRQ_TYPE_PROBE - Special flag for probing in progress * * Bits which can be modified via irq_set/clear/modify_status_flags() @@ -77,6 +83,7 @@ enum { IRQ_TYPE_LEVEL_LOW = 0x00000008, IRQ_TYPE_LEVEL_MASK = (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH), IRQ_TYPE_SENSE_MASK = 0x0000000f, + IRQ_TYPE_DEFAULT = IRQ_TYPE_SENSE_MASK, IRQ_TYPE_PROBE = 0x00000010, -- cgit v1.2.3 From 5a218ceba7b64f506bf4f004b04bb457c1805a62 Mon Sep 17 00:00:00 2001 From: Carlos Chinea Date: Wed, 4 Apr 2012 14:11:45 +0300 Subject: HSI: hsi: Rework hsi_controller release Use the proper release mechanism for hsi_controller and hsi_ports structures. Free the structures through their associated device release callbacks. Signed-off-by: Carlos Chinea Acked-by: Greg Kroah-Hartman Acked-by: Linus Walleij --- drivers/hsi/hsi.c | 108 +++++++++++++++++++++++++++++------------------- include/linux/hsi/hsi.h | 6 +-- 2 files changed, 69 insertions(+), 45 deletions(-) (limited to 'include') diff --git a/drivers/hsi/hsi.c b/drivers/hsi/hsi.c index 4e2d79b79334..c17d12ca8e7f 100644 --- a/drivers/hsi/hsi.c +++ b/drivers/hsi/hsi.c @@ -140,12 +140,17 @@ static int hsi_remove_port(struct device *dev, void *data __maybe_unused) return 0; } -static void hsi_controller_release(struct device *dev __maybe_unused) +static void hsi_controller_release(struct device *dev) { + struct hsi_controller *hsi = to_hsi_controller(dev); + + kfree(hsi->port); + kfree(hsi); } -static void hsi_port_release(struct device *dev __maybe_unused) +static void hsi_port_release(struct device *dev) { + kfree(to_hsi_port(dev)); } /** @@ -172,18 +177,14 @@ int hsi_register_controller(struct hsi_controller *hsi) hsi->device.type = &hsi_ctrl; hsi->device.bus = &hsi_bus_type; - hsi->device.release = hsi_controller_release; - err = device_register(&hsi->device); + err = device_add(&hsi->device); if (err < 0) return err; for (i = 0; i < hsi->num_ports; i++) { - hsi->port[i].device.parent = &hsi->device; - hsi->port[i].device.bus = &hsi_bus_type; - hsi->port[i].device.release = hsi_port_release; - hsi->port[i].device.type = &hsi_port; - INIT_LIST_HEAD(&hsi->port[i].clients); - spin_lock_init(&hsi->port[i].clock); - err = device_register(&hsi->port[i].device); + hsi->port[i]->device.parent = &hsi->device; + hsi->port[i]->device.bus = &hsi_bus_type; + hsi->port[i]->device.type = &hsi_port; + err = device_add(&hsi->port[i]->device); if (err < 0) goto out; } @@ -192,7 +193,9 @@ int hsi_register_controller(struct hsi_controller *hsi) return 0; out: - hsi_unregister_controller(hsi); + while (i-- > 0) + device_del(&hsi->port[i]->device); + device_del(&hsi->device); return err; } @@ -222,6 +225,29 @@ static inline int hsi_dummy_cl(struct hsi_client *cl __maybe_unused) return 0; } +/** + * hsi_put_controller - Free an HSI controller + * + * @hsi: Pointer to the HSI controller to freed + * + * HSI controller drivers should only use this function if they need + * to free their allocated hsi_controller structures before a successful + * call to hsi_register_controller. Other use is not allowed. + */ +void hsi_put_controller(struct hsi_controller *hsi) +{ + unsigned int i; + + if (!hsi) + return; + + for (i = 0; i < hsi->num_ports; i++) + if (hsi->port && hsi->port[i]) + put_device(&hsi->port[i]->device); + put_device(&hsi->device); +} +EXPORT_SYMBOL_GPL(hsi_put_controller); + /** * hsi_alloc_controller - Allocate an HSI controller and its ports * @n_ports: Number of ports on the HSI controller @@ -232,54 +258,52 @@ static inline int hsi_dummy_cl(struct hsi_client *cl __maybe_unused) struct hsi_controller *hsi_alloc_controller(unsigned int n_ports, gfp_t flags) { struct hsi_controller *hsi; - struct hsi_port *port; + struct hsi_port **port; unsigned int i; if (!n_ports) return NULL; - port = kzalloc(sizeof(*port)*n_ports, flags); - if (!port) - return NULL; hsi = kzalloc(sizeof(*hsi), flags); if (!hsi) - goto out; - for (i = 0; i < n_ports; i++) { - dev_set_name(&port[i].device, "port%d", i); - port[i].num = i; - port[i].async = hsi_dummy_msg; - port[i].setup = hsi_dummy_cl; - port[i].flush = hsi_dummy_cl; - port[i].start_tx = hsi_dummy_cl; - port[i].stop_tx = hsi_dummy_cl; - port[i].release = hsi_dummy_cl; - mutex_init(&port[i].lock); + return NULL; + port = kzalloc(sizeof(*port)*n_ports, flags); + if (!port) { + kfree(hsi); + return NULL; } hsi->num_ports = n_ports; hsi->port = port; + hsi->device.release = hsi_controller_release; + device_initialize(&hsi->device); + + for (i = 0; i < n_ports; i++) { + port[i] = kzalloc(sizeof(**port), flags); + if (port[i] == NULL) + goto out; + port[i]->num = i; + port[i]->async = hsi_dummy_msg; + port[i]->setup = hsi_dummy_cl; + port[i]->flush = hsi_dummy_cl; + port[i]->start_tx = hsi_dummy_cl; + port[i]->stop_tx = hsi_dummy_cl; + port[i]->release = hsi_dummy_cl; + mutex_init(&port[i]->lock); + INIT_LIST_HEAD(&hsi->port[i]->clients); + spin_lock_init(&hsi->port[i]->clock); + dev_set_name(&port[i]->device, "port%d", i); + hsi->port[i]->device.release = hsi_port_release; + device_initialize(&hsi->port[i]->device); + } return hsi; out: - kfree(port); + hsi_put_controller(hsi); return NULL; } EXPORT_SYMBOL_GPL(hsi_alloc_controller); -/** - * hsi_free_controller - Free an HSI controller - * @hsi: Pointer to HSI controller - */ -void hsi_free_controller(struct hsi_controller *hsi) -{ - if (!hsi) - return; - - kfree(hsi->port); - kfree(hsi); -} -EXPORT_SYMBOL_GPL(hsi_free_controller); - /** * hsi_free_msg - Free an HSI message * @msg: Pointer to the HSI message diff --git a/include/linux/hsi/hsi.h b/include/linux/hsi/hsi.h index 4b178067f405..7f3b7262a2b6 100644 --- a/include/linux/hsi/hsi.h +++ b/include/linux/hsi/hsi.h @@ -270,13 +270,13 @@ struct hsi_controller { struct module *owner; unsigned int id; unsigned int num_ports; - struct hsi_port *port; + struct hsi_port **port; }; #define to_hsi_controller(dev) container_of(dev, struct hsi_controller, device) struct hsi_controller *hsi_alloc_controller(unsigned int n_ports, gfp_t flags); -void hsi_free_controller(struct hsi_controller *hsi); +void hsi_put_controller(struct hsi_controller *hsi); int hsi_register_controller(struct hsi_controller *hsi); void hsi_unregister_controller(struct hsi_controller *hsi); @@ -294,7 +294,7 @@ static inline void *hsi_controller_drvdata(struct hsi_controller *hsi) static inline struct hsi_port *hsi_find_port_num(struct hsi_controller *hsi, unsigned int num) { - return (num < hsi->num_ports) ? &hsi->port[num] : NULL; + return (num < hsi->num_ports) ? hsi->port[num] : NULL; } /* -- cgit v1.2.3 From ec1c56ff813a198d656d4aa42e5de03e45751bf8 Mon Sep 17 00:00:00 2001 From: Carlos Chinea Date: Wed, 11 Apr 2012 10:55:53 +0300 Subject: HSI: hsi: Rework hsi_event interface Remove custom hack and make use of the notifier chain interfaces for delivering events from the ports to their associated clients. Clients that want to receive port events need to register their callbacks using hsi_register_port_event(). The callbacks can be called in interrupt context. Use hsi_unregestier_port_event() to undo the registration. Signed-off-by: Carlos Chinea Acked-by: Greg Kroah-Hartman Acked-by: Linus Walleij --- drivers/hsi/hsi.c | 101 ++++++++++++++++++++++++++---------------------- include/linux/hsi/hsi.h | 25 ++++++------ 2 files changed, 68 insertions(+), 58 deletions(-) (limited to 'include') diff --git a/drivers/hsi/hsi.c b/drivers/hsi/hsi.c index cec1f0c04557..2d58f939d27f 100644 --- a/drivers/hsi/hsi.c +++ b/drivers/hsi/hsi.c @@ -21,12 +21,11 @@ */ #include #include -#include #include -#include #include #include #include +#include #include "hsi_core.h" static ssize_t modalias_show(struct device *dev, @@ -67,7 +66,6 @@ static void hsi_client_release(struct device *dev) static void hsi_new_client(struct hsi_port *port, struct hsi_board_info *info) { struct hsi_client *cl; - unsigned long flags; cl = kzalloc(sizeof(*cl), GFP_KERNEL); if (!cl) @@ -79,9 +77,6 @@ static void hsi_new_client(struct hsi_port *port, struct hsi_board_info *info) cl->device.release = hsi_client_release; dev_set_name(&cl->device, info->name); cl->device.platform_data = info->platform_data; - spin_lock_irqsave(&port->clock, flags); - list_add_tail(&cl->link, &port->clients); - spin_unlock_irqrestore(&port->clock, flags); if (info->archdata) cl->device.archdata = *info->archdata; if (device_register(&cl->device) < 0) { @@ -106,13 +101,6 @@ static void hsi_scan_board_info(struct hsi_controller *hsi) static int hsi_remove_client(struct device *dev, void *data __maybe_unused) { - struct hsi_client *cl = to_hsi_client(dev); - struct hsi_port *port = to_hsi_port(dev->parent); - unsigned long flags; - - spin_lock_irqsave(&port->clock, flags); - list_del(&cl->link); - spin_unlock_irqrestore(&port->clock, flags); device_unregister(dev); return 0; @@ -271,8 +259,7 @@ struct hsi_controller *hsi_alloc_controller(unsigned int n_ports, gfp_t flags) port[i]->stop_tx = hsi_dummy_cl; port[i]->release = hsi_dummy_cl; mutex_init(&port[i]->lock); - INIT_LIST_HEAD(&hsi->port[i]->clients); - spin_lock_init(&hsi->port[i]->clock); + ATOMIC_INIT_NOTIFIER_HEAD(&port[i]->n_head); dev_set_name(&port[i]->device, "port%d", i); hsi->port[i]->device.release = hsi_port_release; device_initialize(&hsi->port[i]->device); @@ -420,37 +407,67 @@ void hsi_release_port(struct hsi_client *cl) } EXPORT_SYMBOL_GPL(hsi_release_port); -static int hsi_start_rx(struct hsi_client *cl, void *data __maybe_unused) +static int hsi_event_notifier_call(struct notifier_block *nb, + unsigned long event, void *data __maybe_unused) { - if (cl->hsi_start_rx) - (*cl->hsi_start_rx)(cl); + struct hsi_client *cl = container_of(nb, struct hsi_client, nb); + + (*cl->ehandler)(cl, event); return 0; } -static int hsi_stop_rx(struct hsi_client *cl, void *data __maybe_unused) +/** + * hsi_register_port_event - Register a client to receive port events + * @cl: HSI client that wants to receive port events + * @cb: Event handler callback + * + * Clients should register a callback to be able to receive + * events from the ports. Registration should happen after + * claiming the port. + * The handler can be called in interrupt context. + * + * Returns -errno on error, or 0 on success. + */ +int hsi_register_port_event(struct hsi_client *cl, + void (*handler)(struct hsi_client *, unsigned long)) { - if (cl->hsi_stop_rx) - (*cl->hsi_stop_rx)(cl); + struct hsi_port *port = hsi_get_port(cl); - return 0; + if (!handler || cl->ehandler) + return -EINVAL; + if (!hsi_port_claimed(cl)) + return -EACCES; + cl->ehandler = handler; + cl->nb.notifier_call = hsi_event_notifier_call; + + return atomic_notifier_chain_register(&port->n_head, &cl->nb); } +EXPORT_SYMBOL_GPL(hsi_register_port_event); -static int hsi_port_for_each_client(struct hsi_port *port, void *data, - int (*fn)(struct hsi_client *cl, void *data)) +/** + * hsi_unregister_port_event - Stop receiving port events for a client + * @cl: HSI client that wants to stop receiving port events + * + * Clients should call this function before releasing their associated + * port. + * + * Returns -errno on error, or 0 on success. + */ +int hsi_unregister_port_event(struct hsi_client *cl) { - struct hsi_client *cl; + struct hsi_port *port = hsi_get_port(cl); + int err; - spin_lock(&port->clock); - list_for_each_entry(cl, &port->clients, link) { - spin_unlock(&port->clock); - (*fn)(cl, data); - spin_lock(&port->clock); - } - spin_unlock(&port->clock); + WARN_ON(!hsi_port_claimed(cl)); - return 0; + err = atomic_notifier_chain_unregister(&port->n_head, &cl->nb); + if (!err) + cl->ehandler = NULL; + + return err; } +EXPORT_SYMBOL_GPL(hsi_unregister_port_event); /** * hsi_event -Notifies clients about port events @@ -464,22 +481,12 @@ static int hsi_port_for_each_client(struct hsi_port *port, void *data, * Events: * HSI_EVENT_START_RX - Incoming wake line high * HSI_EVENT_STOP_RX - Incoming wake line down + * + * Returns -errno on error, or 0 on success. */ -void hsi_event(struct hsi_port *port, unsigned int event) +int hsi_event(struct hsi_port *port, unsigned long event) { - int (*fn)(struct hsi_client *cl, void *data); - - switch (event) { - case HSI_EVENT_START_RX: - fn = hsi_start_rx; - break; - case HSI_EVENT_STOP_RX: - fn = hsi_stop_rx; - break; - default: - return; - } - hsi_port_for_each_client(port, NULL, fn); + return atomic_notifier_call_chain(&port->n_head, event, NULL); } EXPORT_SYMBOL_GPL(hsi_event); diff --git a/include/linux/hsi/hsi.h b/include/linux/hsi/hsi.h index 7f3b7262a2b6..56fae865e272 100644 --- a/include/linux/hsi/hsi.h +++ b/include/linux/hsi/hsi.h @@ -26,9 +26,9 @@ #include #include #include -#include #include #include +#include /* HSI message ttype */ #define HSI_MSG_READ 0 @@ -121,18 +121,18 @@ static inline int hsi_register_board_info(struct hsi_board_info const *info, * @device: Driver model representation of the device * @tx_cfg: HSI TX configuration * @rx_cfg: HSI RX configuration - * @hsi_start_rx: Called after incoming wake line goes high - * @hsi_stop_rx: Called after incoming wake line goes low + * @e_handler: Callback for handling port events (RX Wake High/Low) + * @pclaimed: Keeps tracks if the clients claimed its associated HSI port + * @nb: Notifier block for port events */ struct hsi_client { struct device device; struct hsi_config tx_cfg; struct hsi_config rx_cfg; - void (*hsi_start_rx)(struct hsi_client *cl); - void (*hsi_stop_rx)(struct hsi_client *cl); /* private: */ + void (*ehandler)(struct hsi_client *, unsigned long); unsigned int pclaimed:1; - struct list_head link; + struct notifier_block nb; }; #define to_hsi_client(dev) container_of(dev, struct hsi_client, device) @@ -147,6 +147,10 @@ static inline void *hsi_client_drvdata(struct hsi_client *cl) return dev_get_drvdata(&cl->device); } +int hsi_register_port_event(struct hsi_client *cl, + void (*handler)(struct hsi_client *, unsigned long)); +int hsi_unregister_port_event(struct hsi_client *cl); + /** * struct hsi_client_driver - Driver associated to an HSI client * @driver: Driver model representation of the driver @@ -214,8 +218,7 @@ void hsi_free_msg(struct hsi_msg *msg); * @start_tx: Callback to inform that a client wants to TX data * @stop_tx: Callback to inform that a client no longer wishes to TX data * @release: Callback to inform that a client no longer uses the port - * @clients: List of hsi_clients using the port. - * @clock: Lock to serialize access to the clients list. + * @n_head: Notifier chain for signaling port events to the clients. */ struct hsi_port { struct device device; @@ -231,14 +234,14 @@ struct hsi_port { int (*start_tx)(struct hsi_client *cl); int (*stop_tx)(struct hsi_client *cl); int (*release)(struct hsi_client *cl); - struct list_head clients; - spinlock_t clock; + /* private */ + struct atomic_notifier_head n_head; }; #define to_hsi_port(dev) container_of(dev, struct hsi_port, device) #define hsi_get_port(cl) to_hsi_port((cl)->device.parent) -void hsi_event(struct hsi_port *port, unsigned int event); +int hsi_event(struct hsi_port *port, unsigned long event); int hsi_claim_port(struct hsi_client *cl, unsigned int share); void hsi_release_port(struct hsi_client *cl); -- cgit v1.2.3 From d643bdca8ab9cd333da1b68267d0e47328e56f56 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Mon, 23 Apr 2012 16:29:18 -0700 Subject: asm-generic: Allow overriding clock_t and add attributes to siginfo_t For the particular issue of x32, which shares code with i386 in the handling of compat_siginfo_t, the use of a 64-bit clock_t bumps the sigchld structure out of alignment, which triggers a messy cascade of padding. This was already handled on the kernel compat side, but it needs handling on the user space side, which uses the generic header. To make that possible: 1. Allow __kernel_clock_t to be overridden in struct siginfo; 2. Allow there to be attributes added to struct siginfo. Reported-by: H.J. Lu Cc: Bruce J. Beare Cc: Arnd Bergmann Link: http://lkml.kernel.org/r/CAMe9rOqF6Kh6-NK7oP0Fpzkd4SBAWU%2BG53hwBbSD4iA2UzyxuA@mail.gmail.com Signed-off-by: H. Peter Anvin --- include/asm-generic/siginfo.h | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/asm-generic/siginfo.h b/include/asm-generic/siginfo.h index 0dd4e87f6fba..5e5e3865f1ed 100644 --- a/include/asm-generic/siginfo.h +++ b/include/asm-generic/siginfo.h @@ -35,6 +35,14 @@ typedef union sigval { #define __ARCH_SI_BAND_T long #endif +#ifndef __ARCH_SI_CLOCK_T +#define __ARCH_SI_CLOCK_T __kernel_clock_t +#endif + +#ifndef __ARCH_SI_ATTRIBUTES +#define __ARCH_SI_ATTRIBUTES +#endif + #ifndef HAVE_ARCH_SIGINFO_T typedef struct siginfo { @@ -72,8 +80,8 @@ typedef struct siginfo { __kernel_pid_t _pid; /* which child */ __ARCH_SI_UID_T _uid; /* sender's uid */ int _status; /* exit code */ - __kernel_clock_t _utime; - __kernel_clock_t _stime; + __ARCH_SI_CLOCK_T _utime; + __ARCH_SI_CLOCK_T _stime; } _sigchld; /* SIGILL, SIGFPE, SIGSEGV, SIGBUS */ @@ -91,7 +99,7 @@ typedef struct siginfo { int _fd; } _sigpoll; } _sifields; -} siginfo_t; +} __ARCH_SI_ATTRIBUTES siginfo_t; #endif -- cgit v1.2.3 From 151b61284776be2d6f02d48c23c3625678960b97 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 24 Apr 2012 14:07:22 -0400 Subject: USB: EHCI: fix crash during suspend on ASUS computers This patch (as1545) fixes a problem affecting several ASUS computers: The machine crashes or corrupts memory when going into suspend if the ehci-hcd driver is bound to any controllers. Users have been forced to unbind or unload ehci-hcd before putting their systems to sleep. After extensive testing, it was determined that the machines don't like going into suspend when any EHCI controllers are in the PCI D3 power state. Presumably this is a firmware bug, but there's nothing we can do about it except to avoid putting the controllers in D3 during system sleep. The patch adds a new flag to indicate whether the problem is present, and avoids changing the controller's power state if the flag is set. Runtime suspend is unaffected; this matters only for system suspend. However as a side effect, the controller will not respond to remote wakeup requests while the system is asleep. Hence USB wakeup is not functional -- but of course, this is already true in the current state of affairs. This fixes Bugzilla #42728. Signed-off-by: Alan Stern Tested-by: Steven Rostedt Tested-by: Andrey Rahmatullin Tested-by: Oleksij Rempel (fishor) Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd-pci.c | 9 +++++++++ drivers/usb/host/ehci-pci.c | 8 ++++++++ include/linux/usb/hcd.h | 2 ++ 3 files changed, 19 insertions(+) (limited to 'include') diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c index 622b4a48e732..57ed9e400c06 100644 --- a/drivers/usb/core/hcd-pci.c +++ b/drivers/usb/core/hcd-pci.c @@ -493,6 +493,15 @@ static int hcd_pci_suspend_noirq(struct device *dev) pci_save_state(pci_dev); + /* + * Some systems crash if an EHCI controller is in D3 during + * a sleep transition. We have to leave such controllers in D0. + */ + if (hcd->broken_pci_sleep) { + dev_dbg(dev, "Staying in PCI D0\n"); + return retval; + } + /* If the root hub is dead rather than suspended, disallow remote * wakeup. usb_hc_died() should ensure that both hosts are marked as * dying, so we only need to check the primary roothub. diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c index 01bb7241d6ef..fe8dc069164e 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c @@ -144,6 +144,14 @@ static int ehci_pci_setup(struct usb_hcd *hcd) hcd->has_tt = 1; tdi_reset(ehci); } + if (pdev->subsystem_vendor == PCI_VENDOR_ID_ASUSTEK) { + /* EHCI #1 or #2 on 6 Series/C200 Series chipset */ + if (pdev->device == 0x1c26 || pdev->device == 0x1c2d) { + ehci_info(ehci, "broken D3 during system sleep on ASUS\n"); + hcd->broken_pci_sleep = 1; + device_set_wakeup_capable(&pdev->dev, false); + } + } break; case PCI_VENDOR_ID_TDI: if (pdev->device == PCI_DEVICE_ID_TDI_EHCI) { diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index 5de415707c23..d28cc78a38e4 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h @@ -126,6 +126,8 @@ struct usb_hcd { unsigned wireless:1; /* Wireless USB HCD */ unsigned authorized_default:1; unsigned has_tt:1; /* Integrated TT in root hub */ + unsigned broken_pci_sleep:1; /* Don't put the + controller in PCI-D3 for system sleep */ unsigned int irq; /* irq allocated */ void __iomem *regs; /* device memory/io */ -- cgit v1.2.3 From 904249aa68010c8e223263c922fcbb840a3f42e4 Mon Sep 17 00:00:00 2001 From: Ying Han Date: Wed, 25 Apr 2012 16:01:48 -0700 Subject: mm: fix up the vmscan stat in vmstat The "pgsteal" stat is confusing because it counts both direct reclaim as well as background reclaim. However, we have "kswapd_steal" which also counts background reclaim value. This patch fixes it and also makes it match the existng "pgscan_" stats. Test: pgsteal_kswapd_dma32 447623 pgsteal_kswapd_normal 42272677 pgsteal_kswapd_movable 0 pgsteal_direct_dma32 2801 pgsteal_direct_normal 44353270 pgsteal_direct_movable 0 Signed-off-by: Ying Han Reviewed-by: Rik van Riel Acked-by: Christoph Lameter Cc: Johannes Weiner Cc: Michal Hocko Cc: Mel Gorman Acked-by: KAMEZAWA Hiroyuki Cc: Hillf Danton Cc: Hugh Dickins Cc: Dan Magenheimer Reviewed-by: Minchan Kim Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/vm_event_item.h | 5 +++-- mm/vmscan.c | 11 ++++++++--- mm/vmstat.c | 4 ++-- 3 files changed, 13 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/linux/vm_event_item.h b/include/linux/vm_event_item.h index 03b90cdc1921..06f8e3858251 100644 --- a/include/linux/vm_event_item.h +++ b/include/linux/vm_event_item.h @@ -26,13 +26,14 @@ enum vm_event_item { PGPGIN, PGPGOUT, PSWPIN, PSWPOUT, PGFREE, PGACTIVATE, PGDEACTIVATE, PGFAULT, PGMAJFAULT, FOR_ALL_ZONES(PGREFILL), - FOR_ALL_ZONES(PGSTEAL), + FOR_ALL_ZONES(PGSTEAL_KSWAPD), + FOR_ALL_ZONES(PGSTEAL_DIRECT), FOR_ALL_ZONES(PGSCAN_KSWAPD), FOR_ALL_ZONES(PGSCAN_DIRECT), #ifdef CONFIG_NUMA PGSCAN_ZONE_RECLAIM_FAILED, #endif - PGINODESTEAL, SLABS_SCANNED, KSWAPD_STEAL, KSWAPD_INODESTEAL, + PGINODESTEAL, SLABS_SCANNED, KSWAPD_INODESTEAL, KSWAPD_LOW_WMARK_HIT_QUICKLY, KSWAPD_HIGH_WMARK_HIT_QUICKLY, KSWAPD_SKIP_CONGESTION_WAIT, PAGEOUTRUN, ALLOCSTALL, PGROTATED, diff --git a/mm/vmscan.c b/mm/vmscan.c index 1a518684a32f..33dc256033b5 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -1568,9 +1568,14 @@ shrink_inactive_list(unsigned long nr_to_scan, struct mem_cgroup_zone *mz, reclaim_stat->recent_scanned[0] += nr_anon; reclaim_stat->recent_scanned[1] += nr_file; - if (current_is_kswapd()) - __count_vm_events(KSWAPD_STEAL, nr_reclaimed); - __count_zone_vm_events(PGSTEAL, zone, nr_reclaimed); + if (global_reclaim(sc)) { + if (current_is_kswapd()) + __count_zone_vm_events(PGSTEAL_KSWAPD, zone, + nr_reclaimed); + else + __count_zone_vm_events(PGSTEAL_DIRECT, zone, + nr_reclaimed); + } putback_inactive_pages(mz, &page_list); diff --git a/mm/vmstat.c b/mm/vmstat.c index f600557a7659..7db1b9bab492 100644 --- a/mm/vmstat.c +++ b/mm/vmstat.c @@ -738,7 +738,8 @@ const char * const vmstat_text[] = { "pgmajfault", TEXTS_FOR_ZONES("pgrefill") - TEXTS_FOR_ZONES("pgsteal") + TEXTS_FOR_ZONES("pgsteal_kswapd") + TEXTS_FOR_ZONES("pgsteal_direct") TEXTS_FOR_ZONES("pgscan_kswapd") TEXTS_FOR_ZONES("pgscan_direct") @@ -747,7 +748,6 @@ const char * const vmstat_text[] = { #endif "pginodesteal", "slabs_scanned", - "kswapd_steal", "kswapd_inodesteal", "kswapd_low_wmark_hit_quickly", "kswapd_high_wmark_hit_quickly", -- cgit v1.2.3 From b95ace54a23e2f8ebb032744cebb17c9f43bf651 Mon Sep 17 00:00:00 2001 From: Robert Jarzmik Date: Sun, 22 Apr 2012 13:37:24 +0200 Subject: ARM: pxa: fix gpio wakeup setting In 3.3, gpio wakeup setting was broken. The call enable_irq_wake() didn't set up the PXA gpio registers (PWER, ...) anymore. Fix it at least for pxa27x. The driver doesn't seem to be used in pxa25x (weird ...), and the fix doesn't extend to pxa3xx and pxa95x (which don't have a gpio_set_wake() available). Signed-off-by: Robert Jarzmik Signed-off-by: Haojian Zhuang --- arch/arm/mach-pxa/pxa27x.c | 6 +++++- drivers/gpio/gpio-pxa.c | 21 +++++++++++++++++++-- include/linux/gpio-pxa.h | 4 ++++ 3 files changed, 28 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/arch/arm/mach-pxa/pxa27x.c b/arch/arm/mach-pxa/pxa27x.c index 6bce78edce7a..4726c246dcdc 100644 --- a/arch/arm/mach-pxa/pxa27x.c +++ b/arch/arm/mach-pxa/pxa27x.c @@ -421,8 +421,11 @@ void __init pxa27x_set_i2c_power_info(struct i2c_pxa_platform_data *info) pxa_register_device(&pxa27x_device_i2c_power, info); } +static struct pxa_gpio_platform_data pxa27x_gpio_info __initdata = { + .gpio_set_wake = gpio_set_wake, +}; + static struct platform_device *devices[] __initdata = { - &pxa_device_gpio, &pxa27x_device_udc, &pxa_device_pmu, &pxa_device_i2s, @@ -458,6 +461,7 @@ static int __init pxa27x_init(void) register_syscore_ops(&pxa2xx_mfp_syscore_ops); register_syscore_ops(&pxa2xx_clock_syscore_ops); + pxa_register_device(&pxa_device_gpio, &pxa27x_gpio_info); ret = platform_add_devices(devices, ARRAY_SIZE(devices)); } diff --git a/drivers/gpio/gpio-pxa.c b/drivers/gpio/gpio-pxa.c index 5689ce62fd81..fc3ace3fd4cb 100644 --- a/drivers/gpio/gpio-pxa.c +++ b/drivers/gpio/gpio-pxa.c @@ -64,6 +64,7 @@ struct pxa_gpio_chip { unsigned long irq_mask; unsigned long irq_edge_rise; unsigned long irq_edge_fall; + int (*set_wake)(unsigned int gpio, unsigned int on); #ifdef CONFIG_PM unsigned long saved_gplr; @@ -269,7 +270,8 @@ static void pxa_gpio_set(struct gpio_chip *chip, unsigned offset, int value) (value ? GPSR_OFFSET : GPCR_OFFSET)); } -static int __devinit pxa_init_gpio_chip(int gpio_end) +static int __devinit pxa_init_gpio_chip(int gpio_end, + int (*set_wake)(unsigned int, unsigned int)) { int i, gpio, nbanks = gpio_to_bank(gpio_end) + 1; struct pxa_gpio_chip *chips; @@ -285,6 +287,7 @@ static int __devinit pxa_init_gpio_chip(int gpio_end) sprintf(chips[i].label, "gpio-%d", i); chips[i].regbase = gpio_reg_base + BANK_OFF(i); + chips[i].set_wake = set_wake; c->base = gpio; c->label = chips[i].label; @@ -412,6 +415,17 @@ static void pxa_mask_muxed_gpio(struct irq_data *d) writel_relaxed(gfer, c->regbase + GFER_OFFSET); } +static int pxa_gpio_set_wake(struct irq_data *d, unsigned int on) +{ + int gpio = pxa_irq_to_gpio(d->irq); + struct pxa_gpio_chip *c = gpio_to_pxachip(gpio); + + if (c->set_wake) + return c->set_wake(gpio, on); + else + return 0; +} + static void pxa_unmask_muxed_gpio(struct irq_data *d) { int gpio = pxa_irq_to_gpio(d->irq); @@ -427,6 +441,7 @@ static struct irq_chip pxa_muxed_gpio_chip = { .irq_mask = pxa_mask_muxed_gpio, .irq_unmask = pxa_unmask_muxed_gpio, .irq_set_type = pxa_gpio_irq_type, + .irq_set_wake = pxa_gpio_set_wake, }; static int pxa_gpio_nums(void) @@ -471,6 +486,7 @@ static int __devinit pxa_gpio_probe(struct platform_device *pdev) struct pxa_gpio_chip *c; struct resource *res; struct clk *clk; + struct pxa_gpio_platform_data *info; int gpio, irq, ret; int irq0 = 0, irq1 = 0, irq_mux, gpio_offset = 0; @@ -516,7 +532,8 @@ static int __devinit pxa_gpio_probe(struct platform_device *pdev) } /* Initialize GPIO chips */ - pxa_init_gpio_chip(pxa_last_gpio); + info = dev_get_platdata(&pdev->dev); + pxa_init_gpio_chip(pxa_last_gpio, info ? info->gpio_set_wake : NULL); /* clear all GPIO edge detects */ for_each_gpio_chip(gpio, c) { diff --git a/include/linux/gpio-pxa.h b/include/linux/gpio-pxa.h index 05071ee34c3f..d755b28ba635 100644 --- a/include/linux/gpio-pxa.h +++ b/include/linux/gpio-pxa.h @@ -13,4 +13,8 @@ extern int pxa_last_gpio; extern int pxa_irq_to_gpio(int irq); +struct pxa_gpio_platform_data { + int (*gpio_set_wake)(unsigned int gpio, unsigned int on); +}; + #endif /* __GPIO_PXA_H */ -- cgit v1.2.3 From dbabe0d659d3cfe42830a779909ab3cd42f7b027 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Tue, 17 Apr 2012 17:03:50 -0700 Subject: spi: fix spi.h kernel-doc warning Fix kernel-doc warning in spi.h (copy/paste): Warning(include/linux/spi/spi.h:365): No description found for parameter 'unprepare_transfer_hardware' Signed-off-by: Randy Dunlap Signed-off-by: Grant Likely --- include/linux/spi/spi.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index 98679b061b63..fa702aeb5038 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -254,7 +254,7 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv) * driver is finished with this message, it must call * spi_finalize_current_message() so the subsystem can issue the next * transfer - * @prepare_transfer_hardware: there are currently no more messages on the + * @unprepare_transfer_hardware: there are currently no more messages on the * queue so the subsystem notifies the driver that it may relax the * hardware by issuing this call * -- cgit v1.2.3 From 9883035ae7edef3ec62ad215611cb8e17d6a1a5d Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sun, 29 Apr 2012 13:12:42 -0700 Subject: pipes: add a "packetized pipe" mode for writing The actual internal pipe implementation is already really about individual packets (called "pipe buffers"), and this simply exposes that as a special packetized mode. When we are in the packetized mode (marked by O_DIRECT as suggested by Alan Cox), a write() on a pipe will not merge the new data with previous writes, so each write will get a pipe buffer of its own. The pipe buffer is then marked with the PIPE_BUF_FLAG_PACKET flag, which in turn will tell the reader side to break the read at that boundary (and throw away any partial packet contents that do not fit in the read buffer). End result: as long as you do writes less than PIPE_BUF in size (so that the pipe doesn't have to split them up), you can now treat the pipe as a packet interface, where each read() system call will read one packet at a time. You can just use a sufficiently big read buffer (PIPE_BUF is sufficient, since bigger than that doesn't guarantee atomicity anyway), and the return value of the read() will naturally give you the size of the packet. NOTE! We do not support zero-sized packets, and zero-sized reads and writes to a pipe continue to be no-ops. Also note that big packets will currently be split at write time, but that the size at which that happens is not really specified (except that it's bigger than PIPE_BUF). Currently that limit is the system page size, but we might want to explicitly support bigger packets some day. The main user for this is going to be the autofs packet interface, allowing us to stop having to care so deeply about exact packet sizes (which have had bugs with 32/64-bit compatibility modes). But user space can create packetized pipes with "pipe2(fd, O_DIRECT)", which will fail with an EINVAL on kernels that do not support this interface. Tested-by: Michael Tokarev Cc: Alan Cox Cc: David Miller Cc: Ian Kent Cc: Thomas Meyer Cc: stable@kernel.org # needed for systemd/autofs interaction fix Signed-off-by: Linus Torvalds --- fs/pipe.c | 31 +++++++++++++++++++++++++++++-- include/linux/pipe_fs_i.h | 1 + 2 files changed, 30 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/fs/pipe.c b/fs/pipe.c index 25feaa3faac0..fec5e4ad071a 100644 --- a/fs/pipe.c +++ b/fs/pipe.c @@ -346,6 +346,16 @@ static const struct pipe_buf_operations anon_pipe_buf_ops = { .get = generic_pipe_buf_get, }; +static const struct pipe_buf_operations packet_pipe_buf_ops = { + .can_merge = 0, + .map = generic_pipe_buf_map, + .unmap = generic_pipe_buf_unmap, + .confirm = generic_pipe_buf_confirm, + .release = anon_pipe_buf_release, + .steal = generic_pipe_buf_steal, + .get = generic_pipe_buf_get, +}; + static ssize_t pipe_read(struct kiocb *iocb, const struct iovec *_iov, unsigned long nr_segs, loff_t pos) @@ -407,6 +417,13 @@ redo: ret += chars; buf->offset += chars; buf->len -= chars; + + /* Was it a packet buffer? Clean up and exit */ + if (buf->flags & PIPE_BUF_FLAG_PACKET) { + total_len = chars; + buf->len = 0; + } + if (!buf->len) { buf->ops = NULL; ops->release(pipe, buf); @@ -459,6 +476,11 @@ redo: return ret; } +static inline int is_packetized(struct file *file) +{ + return (file->f_flags & O_DIRECT) != 0; +} + static ssize_t pipe_write(struct kiocb *iocb, const struct iovec *_iov, unsigned long nr_segs, loff_t ppos) @@ -593,6 +615,11 @@ redo2: buf->ops = &anon_pipe_buf_ops; buf->offset = 0; buf->len = chars; + buf->flags = 0; + if (is_packetized(filp)) { + buf->ops = &packet_pipe_buf_ops; + buf->flags = PIPE_BUF_FLAG_PACKET; + } pipe->nrbufs = ++bufs; pipe->tmp_page = NULL; @@ -1013,7 +1040,7 @@ struct file *create_write_pipe(int flags) goto err_dentry; f->f_mapping = inode->i_mapping; - f->f_flags = O_WRONLY | (flags & O_NONBLOCK); + f->f_flags = O_WRONLY | (flags & (O_NONBLOCK | O_DIRECT)); f->f_version = 0; return f; @@ -1057,7 +1084,7 @@ int do_pipe_flags(int *fd, int flags) int error; int fdw, fdr; - if (flags & ~(O_CLOEXEC | O_NONBLOCK)) + if (flags & ~(O_CLOEXEC | O_NONBLOCK | O_DIRECT)) return -EINVAL; fw = create_write_pipe(flags); diff --git a/include/linux/pipe_fs_i.h b/include/linux/pipe_fs_i.h index 6d626ff0cfd0..e1ac1ce16fb0 100644 --- a/include/linux/pipe_fs_i.h +++ b/include/linux/pipe_fs_i.h @@ -6,6 +6,7 @@ #define PIPE_BUF_FLAG_LRU 0x01 /* page is on the LRU */ #define PIPE_BUF_FLAG_ATOMIC 0x02 /* was atomically mapped */ #define PIPE_BUF_FLAG_GIFT 0x04 /* page is a gift */ +#define PIPE_BUF_FLAG_PACKET 0x08 /* read() as a packet */ /** * struct pipe_buffer - a linux kernel pipe buffer -- cgit v1.2.3