diff options
Diffstat (limited to 'net')
-rw-r--r-- | net/bridge/br_input.c | 1 | ||||
-rw-r--r-- | net/core/dev.c | 2 | ||||
-rw-r--r-- | net/core/net-sysfs.c | 49 | ||||
-rw-r--r-- | net/dccp/proto.c | 13 | ||||
-rw-r--r-- | net/ipv4/ip_input.c | 2 | ||||
-rw-r--r-- | net/ipv4/tcp_highspeed.c | 2 | ||||
-rw-r--r-- | net/netrom/af_netrom.c | 3 | ||||
-rw-r--r-- | net/rose/af_rose.c | 3 | ||||
-rw-r--r-- | net/sctp/inqueue.c | 1 | ||||
-rw-r--r-- | net/sctp/sm_statefuns.c | 59 | ||||
-rw-r--r-- | net/sctp/sm_statetable.c | 10 | ||||
-rw-r--r-- | net/sctp/ulpqueue.c | 27 |
12 files changed, 101 insertions, 71 deletions
diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c index b0b7f55c1ed..bfa4d8c333f 100644 --- a/net/bridge/br_input.c +++ b/net/bridge/br_input.c @@ -66,6 +66,7 @@ int br_handle_frame_finish(struct sk_buff *skb) } if (is_multicast_ether_addr(dest)) { + br->statistics.multicast++; br_flood_forward(br, skb, !passedup); if (!passedup) br_pass_frame_up(br, skb); diff --git a/net/core/dev.c b/net/core/dev.c index 3bad1afc89f..9ab3cfa5846 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -3043,11 +3043,11 @@ void netdev_run_todo(void) switch(dev->reg_state) { case NETREG_REGISTERING: - dev->reg_state = NETREG_REGISTERED; err = netdev_register_sysfs(dev); if (err) printk(KERN_ERR "%s: failed sysfs registration (%d)\n", dev->name, err); + dev->reg_state = NETREG_REGISTERED; break; case NETREG_UNREGISTERING: diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c index c12990c9c60..47a6fceb677 100644 --- a/net/core/net-sysfs.c +++ b/net/core/net-sysfs.c @@ -29,7 +29,7 @@ static const char fmt_ulong[] = "%lu\n"; static inline int dev_isalive(const struct net_device *dev) { - return dev->reg_state == NETREG_REGISTERED; + return dev->reg_state <= NETREG_REGISTERED; } /* use same locking rules as GIF* ioctl's */ @@ -445,58 +445,33 @@ static struct class net_class = { void netdev_unregister_sysfs(struct net_device * net) { - struct class_device * class_dev = &(net->class_dev); - - if (net->get_stats) - sysfs_remove_group(&class_dev->kobj, &netstat_group); - -#ifdef WIRELESS_EXT - if (net->get_wireless_stats || (net->wireless_handlers && - net->wireless_handlers->get_wireless_stats)) - sysfs_remove_group(&class_dev->kobj, &wireless_group); -#endif - class_device_del(class_dev); - + class_device_del(&(net->class_dev)); } /* Create sysfs entries for network device. */ int netdev_register_sysfs(struct net_device *net) { struct class_device *class_dev = &(net->class_dev); - int ret; + struct attribute_group **groups = net->sysfs_groups; + class_device_initialize(class_dev); class_dev->class = &net_class; class_dev->class_data = net; + class_dev->groups = groups; + BUILD_BUG_ON(BUS_ID_SIZE < IFNAMSIZ); strlcpy(class_dev->class_id, net->name, BUS_ID_SIZE); - if ((ret = class_device_register(class_dev))) - goto out; - if (net->get_stats && - (ret = sysfs_create_group(&class_dev->kobj, &netstat_group))) - goto out_unreg; + if (net->get_stats) + *groups++ = &netstat_group; #ifdef WIRELESS_EXT - if (net->get_wireless_stats || (net->wireless_handlers && - net->wireless_handlers->get_wireless_stats)) { - ret = sysfs_create_group(&class_dev->kobj, &wireless_group); - if (ret) - goto out_cleanup; - } - return 0; -out_cleanup: - if (net->get_stats) - sysfs_remove_group(&class_dev->kobj, &netstat_group); -#else - return 0; + if (net->get_wireless_stats + || (net->wireless_handlers && net->wireless_handlers->get_wireless_stats)) + *groups++ = &wireless_group; #endif -out_unreg: - printk(KERN_WARNING "%s: sysfs attribute registration failed %d\n", - net->name, ret); - class_device_unregister(class_dev); -out: - return ret; + return class_device_add(class_dev); } int netdev_sysfs_init(void) diff --git a/net/dccp/proto.c b/net/dccp/proto.c index 1ff7328b0e1..2e0ee8355c4 100644 --- a/net/dccp/proto.c +++ b/net/dccp/proto.c @@ -848,6 +848,7 @@ static int dccp_close_state(struct sock *sk) void dccp_close(struct sock *sk, long timeout) { struct sk_buff *skb; + int state; lock_sock(sk); @@ -882,6 +883,11 @@ void dccp_close(struct sock *sk, long timeout) sk_stream_wait_close(sk, timeout); adjudge_to_death: + state = sk->sk_state; + sock_hold(sk); + sock_orphan(sk); + atomic_inc(sk->sk_prot->orphan_count); + /* * It is the last release_sock in its life. It will remove backlog. */ @@ -894,8 +900,9 @@ adjudge_to_death: bh_lock_sock(sk); BUG_TRAP(!sock_owned_by_user(sk)); - sock_hold(sk); - sock_orphan(sk); + /* Have we already been destroyed by a softirq or backlog? */ + if (state != DCCP_CLOSED && sk->sk_state == DCCP_CLOSED) + goto out; /* * The last release_sock may have processed the CLOSE or RESET @@ -915,12 +922,12 @@ adjudge_to_death: #endif } - atomic_inc(sk->sk_prot->orphan_count); if (sk->sk_state == DCCP_CLOSED) inet_csk_destroy_sock(sk); /* Otherwise, socket is reprieved until protocol close. */ +out: bh_unlock_sock(sk); local_bh_enable(); sock_put(sk); diff --git a/net/ipv4/ip_input.c b/net/ipv4/ip_input.c index 18d7fad474d..c9026dbf4c9 100644 --- a/net/ipv4/ip_input.c +++ b/net/ipv4/ip_input.c @@ -337,7 +337,7 @@ static inline int ip_rcv_finish(struct sk_buff *skb) * Initialise the virtual path cache for the packet. It describes * how the packet travels inside Linux networking. */ - if (likely(skb->dst == NULL)) { + if (skb->dst == NULL) { int err = ip_route_input(skb, iph->daddr, iph->saddr, iph->tos, skb->dev); if (unlikely(err)) { diff --git a/net/ipv4/tcp_highspeed.c b/net/ipv4/tcp_highspeed.c index e0e9d1383c7..b72fa55dfb8 100644 --- a/net/ipv4/tcp_highspeed.c +++ b/net/ipv4/tcp_highspeed.c @@ -137,8 +137,8 @@ static void hstcp_cong_avoid(struct sock *sk, u32 adk, u32 rtt, if (tp->snd_cwnd < tp->snd_cwnd_clamp) { tp->snd_cwnd_cnt += ca->ai; if (tp->snd_cwnd_cnt >= tp->snd_cwnd) { - tp->snd_cwnd++; tp->snd_cwnd_cnt -= tp->snd_cwnd; + tp->snd_cwnd++; } } } diff --git a/net/netrom/af_netrom.c b/net/netrom/af_netrom.c index ecd288beca7..3669cb953e6 100644 --- a/net/netrom/af_netrom.c +++ b/net/netrom/af_netrom.c @@ -1370,8 +1370,6 @@ static struct notifier_block nr_dev_notifier = { static struct net_device **dev_nr; -static char banner[] __initdata = KERN_INFO "G4KLX NET/ROM for Linux. Version 0.7 for AX25.037 Linux 2.4\n"; - static int __init nr_proto_init(void) { int i; @@ -1419,7 +1417,6 @@ static int __init nr_proto_init(void) } register_netdevice_notifier(&nr_dev_notifier); - printk(banner); ax25_protocol_register(AX25_P_NETROM, nr_route_frame); ax25_linkfail_register(nr_link_failed); diff --git a/net/rose/af_rose.c b/net/rose/af_rose.c index ef4538ac84f..55564efccf1 100644 --- a/net/rose/af_rose.c +++ b/net/rose/af_rose.c @@ -1469,8 +1469,6 @@ static struct notifier_block rose_dev_notifier = { static struct net_device **dev_rose; -static const char banner[] = KERN_INFO "F6FBB/G4KLX ROSE for Linux. Version 0.62 for AX25.037 Linux 2.4\n"; - static int __init rose_proto_init(void) { int i; @@ -1519,7 +1517,6 @@ static int __init rose_proto_init(void) sock_register(&rose_family_ops); register_netdevice_notifier(&rose_dev_notifier); - printk(banner); ax25_protocol_register(AX25_P_ROSE, rose_route_frame); ax25_linkfail_register(rose_link_failed); diff --git a/net/sctp/inqueue.c b/net/sctp/inqueue.c index 297b8951463..cf0c767d43a 100644 --- a/net/sctp/inqueue.c +++ b/net/sctp/inqueue.c @@ -149,6 +149,7 @@ struct sctp_chunk *sctp_inq_pop(struct sctp_inq *queue) /* This is the first chunk in the packet. */ chunk->singleton = 1; ch = (sctp_chunkhdr_t *) chunk->skb->data; + chunk->data_accepted = 0; } chunk->chunk_hdr = ch; diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index 2b9a832b29a..8cdba51ec07 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -636,8 +636,9 @@ sctp_disposition_t sctp_sf_do_5_1D_ce(const struct sctp_endpoint *ep, */ chunk->subh.cookie_hdr = (struct sctp_signed_cookie *)chunk->skb->data; - skb_pull(chunk->skb, - ntohs(chunk->chunk_hdr->length) - sizeof(sctp_chunkhdr_t)); + if (!pskb_pull(chunk->skb, ntohs(chunk->chunk_hdr->length) - + sizeof(sctp_chunkhdr_t))) + goto nomem; /* 5.1 D) Upon reception of the COOKIE ECHO chunk, Endpoint * "Z" will reply with a COOKIE ACK chunk after building a TCB @@ -965,7 +966,8 @@ sctp_disposition_t sctp_sf_beat_8_3(const struct sctp_endpoint *ep, */ chunk->subh.hb_hdr = (sctp_heartbeathdr_t *) chunk->skb->data; paylen = ntohs(chunk->chunk_hdr->length) - sizeof(sctp_chunkhdr_t); - skb_pull(chunk->skb, paylen); + if (!pskb_pull(chunk->skb, paylen)) + goto nomem; reply = sctp_make_heartbeat_ack(asoc, chunk, chunk->subh.hb_hdr, paylen); @@ -1860,8 +1862,9 @@ sctp_disposition_t sctp_sf_do_5_2_4_dupcook(const struct sctp_endpoint *ep, * are in good shape. */ chunk->subh.cookie_hdr = (struct sctp_signed_cookie *)chunk->skb->data; - skb_pull(chunk->skb, ntohs(chunk->chunk_hdr->length) - - sizeof(sctp_chunkhdr_t)); + if (!pskb_pull(chunk->skb, ntohs(chunk->chunk_hdr->length) - + sizeof(sctp_chunkhdr_t))) + goto nomem; /* In RFC 2960 5.2.4 3, if both Verification Tags in the State Cookie * of a duplicate COOKIE ECHO match the Verification Tags of the @@ -5151,7 +5154,9 @@ static int sctp_eat_data(const struct sctp_association *asoc, int tmp; __u32 tsn; int account_value; + struct sctp_tsnmap *map = (struct sctp_tsnmap *)&asoc->peer.tsn_map; struct sock *sk = asoc->base.sk; + int rcvbuf_over = 0; data_hdr = chunk->subh.data_hdr = (sctp_datahdr_t *)chunk->skb->data; skb_pull(chunk->skb, sizeof(sctp_datahdr_t)); @@ -5162,10 +5167,16 @@ static int sctp_eat_data(const struct sctp_association *asoc, /* ASSERT: Now skb->data is really the user data. */ /* - * if we are established, and we have used up our receive - * buffer memory, drop the frame - */ - if (asoc->state == SCTP_STATE_ESTABLISHED) { + * If we are established, and we have used up our receive buffer + * memory, think about droping the frame. + * Note that we have an opportunity to improve performance here. + * If we accept one chunk from an skbuff, we have to keep all the + * memory of that skbuff around until the chunk is read into user + * space. Therefore, once we accept 1 chunk we may as well accept all + * remaining chunks in the skbuff. The data_accepted flag helps us do + * that. + */ + if ((asoc->state == SCTP_STATE_ESTABLISHED) && (!chunk->data_accepted)) { /* * If the receive buffer policy is 1, then each * association can allocate up to sk_rcvbuf bytes @@ -5176,9 +5187,25 @@ static int sctp_eat_data(const struct sctp_association *asoc, account_value = atomic_read(&asoc->rmem_alloc); else account_value = atomic_read(&sk->sk_rmem_alloc); - - if (account_value > sk->sk_rcvbuf) - return SCTP_IERROR_IGNORE_TSN; + if (account_value > sk->sk_rcvbuf) { + /* + * We need to make forward progress, even when we are + * under memory pressure, so we always allow the + * next tsn after the ctsn ack point to be accepted. + * This lets us avoid deadlocks in which we have to + * drop frames that would otherwise let us drain the + * receive queue. + */ + if ((sctp_tsnmap_get_ctsn(map) + 1) != tsn) + return SCTP_IERROR_IGNORE_TSN; + + /* + * We're going to accept the frame but we should renege + * to make space for it. This will send us down that + * path later in this function. + */ + rcvbuf_over = 1; + } } /* Process ECN based congestion. @@ -5226,6 +5253,7 @@ static int sctp_eat_data(const struct sctp_association *asoc, datalen -= sizeof(sctp_data_chunk_t); deliver = SCTP_CMD_CHUNK_ULP; + chunk->data_accepted = 1; /* Think about partial delivery. */ if ((datalen >= asoc->rwnd) && (!asoc->ulpq.pd_mode)) { @@ -5242,7 +5270,8 @@ static int sctp_eat_data(const struct sctp_association *asoc, * large spill over. */ if (!asoc->rwnd || asoc->rwnd_over || - (datalen > asoc->rwnd + asoc->frag_point)) { + (datalen > asoc->rwnd + asoc->frag_point) || + rcvbuf_over) { /* If this is the next TSN, consider reneging to make * room. Note: Playing nice with a confused sender. A @@ -5250,8 +5279,8 @@ static int sctp_eat_data(const struct sctp_association *asoc, * space and in the future we may want to detect and * do more drastic reneging. */ - if (sctp_tsnmap_has_gap(&asoc->peer.tsn_map) && - (sctp_tsnmap_get_ctsn(&asoc->peer.tsn_map) + 1) == tsn) { + if (sctp_tsnmap_has_gap(map) && + (sctp_tsnmap_get_ctsn(map) + 1) == tsn) { SCTP_DEBUG_PRINTK("Reneging for tsn:%u\n", tsn); deliver = SCTP_CMD_RENEGE; } else { diff --git a/net/sctp/sm_statetable.c b/net/sctp/sm_statetable.c index 75ef1040876..8bcca567615 100644 --- a/net/sctp/sm_statetable.c +++ b/net/sctp/sm_statetable.c @@ -366,9 +366,9 @@ const sctp_sm_table_entry_t *sctp_sm_lookup_event(sctp_event_t event_type, /* SCTP_STATE_EMPTY */ \ {.fn = sctp_sf_ootb, .name = "sctp_sf_ootb"}, \ /* SCTP_STATE_CLOSED */ \ - {.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \ + {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \ /* SCTP_STATE_COOKIE_WAIT */ \ - {.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \ + {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \ /* SCTP_STATE_COOKIE_ECHOED */ \ {.fn = sctp_sf_do_ecne, .name = "sctp_sf_do_ecne"}, \ /* SCTP_STATE_ESTABLISHED */ \ @@ -380,7 +380,7 @@ const sctp_sm_table_entry_t *sctp_sm_lookup_event(sctp_event_t event_type, /* SCTP_STATE_SHUTDOWN_RECEIVED */ \ {.fn = sctp_sf_do_ecne, .name = "sctp_sf_do_ecne"}, \ /* SCTP_STATE_SHUTDOWN_ACK_SENT */ \ - {.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \ + {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \ } /* TYPE_SCTP_ECN_ECNE */ #define TYPE_SCTP_ECN_CWR { \ @@ -401,7 +401,7 @@ const sctp_sm_table_entry_t *sctp_sm_lookup_event(sctp_event_t event_type, /* SCTP_STATE_SHUTDOWN_RECEIVED */ \ {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \ /* SCTP_STATE_SHUTDOWN_ACK_SENT */ \ - {.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \ + {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \ } /* TYPE_SCTP_ECN_CWR */ #define TYPE_SCTP_SHUTDOWN_COMPLETE { \ @@ -647,7 +647,7 @@ chunk_event_table_unknown[SCTP_STATE_NUM_STATES] = { /* SCTP_STATE_EMPTY */ \ {.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \ /* SCTP_STATE_CLOSED */ \ - {.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \ + {.fn = sctp_sf_error_closed, .name = "sctp_sf_error_closed"}, \ /* SCTP_STATE_COOKIE_WAIT */ \ {.fn = sctp_sf_do_prm_requestheartbeat, \ .name = "sctp_sf_do_prm_requestheartbeat"}, \ diff --git a/net/sctp/ulpqueue.c b/net/sctp/ulpqueue.c index 2080b2d28c9..575e556aeb3 100644 --- a/net/sctp/ulpqueue.c +++ b/net/sctp/ulpqueue.c @@ -279,6 +279,7 @@ static inline void sctp_ulpq_store_reasm(struct sctp_ulpq *ulpq, static struct sctp_ulpevent *sctp_make_reassembled_event(struct sk_buff_head *queue, struct sk_buff *f_frag, struct sk_buff *l_frag) { struct sk_buff *pos; + struct sk_buff *new = NULL; struct sctp_ulpevent *event; struct sk_buff *pnext, *last; struct sk_buff *list = skb_shinfo(f_frag)->frag_list; @@ -297,11 +298,33 @@ static struct sctp_ulpevent *sctp_make_reassembled_event(struct sk_buff_head *qu */ if (last) last->next = pos; - else - skb_shinfo(f_frag)->frag_list = pos; + else { + if (skb_cloned(f_frag)) { + /* This is a cloned skb, we can't just modify + * the frag_list. We need a new skb to do that. + * Instead of calling skb_unshare(), we'll do it + * ourselves since we need to delay the free. + */ + new = skb_copy(f_frag, GFP_ATOMIC); + if (!new) + return NULL; /* try again later */ + + new->sk = f_frag->sk; + + skb_shinfo(new)->frag_list = pos; + } else + skb_shinfo(f_frag)->frag_list = pos; + } /* Remove the first fragment from the reassembly queue. */ __skb_unlink(f_frag, queue); + + /* if we did unshare, then free the old skb and re-assign */ + if (new) { + kfree_skb(f_frag); + f_frag = new; + } + while (pos) { pnext = pos->next; |