diff options
author | Lachlan McIlroy <lachlan@redback.melbourne.sgi.com> | 2009-01-08 13:22:55 +1100 |
---|---|---|
committer | Lachlan McIlroy <lachlan@redback.melbourne.sgi.com> | 2009-01-08 13:22:55 +1100 |
commit | 6206aa8b2b9a45b4cf3ee31b7209b014be349fd9 (patch) | |
tree | 72c4223a2cc21bf055948eadb3b314ed0568ae9d /net | |
parent | 95f8e302c04c0b0c6de35ab399a5551605eeb006 (diff) | |
parent | 9e42d0cf5020aaf217433cad1a224745241d212a (diff) |
Merge git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
Diffstat (limited to 'net')
92 files changed, 3611 insertions, 881 deletions
diff --git a/net/Kconfig b/net/Kconfig index 6ec2cce7c16..bf2776018f7 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -254,6 +254,8 @@ source "net/mac80211/Kconfig" endif # WIRELESS +source "net/wimax/Kconfig" + source "net/rfkill/Kconfig" source "net/9p/Kconfig" diff --git a/net/Makefile b/net/Makefile index ba4460432b7..0fcce89d716 100644 --- a/net/Makefile +++ b/net/Makefile @@ -63,3 +63,4 @@ endif ifeq ($(CONFIG_NET),y) obj-$(CONFIG_SYSCTL) += sysctl_net.o endif +obj-$(CONFIG_WIMAX) += wimax/ diff --git a/net/can/bcm.c b/net/can/bcm.c index da0d426c0ce..6248ae2502c 100644 --- a/net/can/bcm.c +++ b/net/can/bcm.c @@ -70,7 +70,7 @@ #define CAN_BCM_VERSION CAN_VERSION static __initdata const char banner[] = KERN_INFO - "can: broadcast manager protocol (rev " CAN_BCM_VERSION ")\n"; + "can: broadcast manager protocol (rev " CAN_BCM_VERSION " t)\n"; MODULE_DESCRIPTION("PF_CAN broadcast manager protocol"); MODULE_LICENSE("Dual BSD/GPL"); @@ -90,6 +90,7 @@ struct bcm_op { unsigned long frames_abs, frames_filtered; struct timeval ival1, ival2; struct hrtimer timer, thrtimer; + struct tasklet_struct tsklet, thrtsklet; ktime_t rx_stamp, kt_ival1, kt_ival2, kt_lastmsg; int rx_ifindex; int count; @@ -341,6 +342,23 @@ static void bcm_send_to_user(struct bcm_op *op, struct bcm_msg_head *head, } } +static void bcm_tx_timeout_tsklet(unsigned long data) +{ + struct bcm_op *op = (struct bcm_op *)data; + struct bcm_msg_head msg_head; + + /* create notification to user */ + msg_head.opcode = TX_EXPIRED; + msg_head.flags = op->flags; + msg_head.count = op->count; + msg_head.ival1 = op->ival1; + msg_head.ival2 = op->ival2; + msg_head.can_id = op->can_id; + msg_head.nframes = 0; + + bcm_send_to_user(op, &msg_head, NULL, 0); +} + /* * bcm_tx_timeout_handler - performes cyclic CAN frame transmissions */ @@ -352,20 +370,8 @@ static enum hrtimer_restart bcm_tx_timeout_handler(struct hrtimer *hrtimer) if (op->kt_ival1.tv64 && (op->count > 0)) { op->count--; - if (!op->count && (op->flags & TX_COUNTEVT)) { - struct bcm_msg_head msg_head; - - /* create notification to user */ - msg_head.opcode = TX_EXPIRED; - msg_head.flags = op->flags; - msg_head.count = op->count; - msg_head.ival1 = op->ival1; - msg_head.ival2 = op->ival2; - msg_head.can_id = op->can_id; - msg_head.nframes = 0; - - bcm_send_to_user(op, &msg_head, NULL, 0); - } + if (!op->count && (op->flags & TX_COUNTEVT)) + tasklet_schedule(&op->tsklet); } if (op->kt_ival1.tv64 && (op->count > 0)) { @@ -402,6 +408,9 @@ static void bcm_rx_changed(struct bcm_op *op, struct can_frame *data) if (op->frames_filtered > ULONG_MAX/100) op->frames_filtered = op->frames_abs = 0; + /* this element is not throttled anymore */ + data->can_dlc &= (BCM_CAN_DLC_MASK|RX_RECV); + head.opcode = RX_CHANGED; head.flags = op->flags; head.count = op->count; @@ -420,37 +429,32 @@ static void bcm_rx_changed(struct bcm_op *op, struct can_frame *data) */ static void bcm_rx_update_and_send(struct bcm_op *op, struct can_frame *lastdata, - struct can_frame *rxdata) + const struct can_frame *rxdata) { memcpy(lastdata, rxdata, CFSIZ); - /* mark as used */ - lastdata->can_dlc |= RX_RECV; + /* mark as used and throttled by default */ + lastdata->can_dlc |= (RX_RECV|RX_THR); - /* throtteling mode inactive OR data update already on the run ? */ - if (!op->kt_ival2.tv64 || hrtimer_callback_running(&op->thrtimer)) { + /* throtteling mode inactive ? */ + if (!op->kt_ival2.tv64) { /* send RX_CHANGED to the user immediately */ - bcm_rx_changed(op, rxdata); + bcm_rx_changed(op, lastdata); return; } - if (hrtimer_active(&op->thrtimer)) { - /* mark as 'throttled' */ - lastdata->can_dlc |= RX_THR; + /* with active throttling timer we are just done here */ + if (hrtimer_active(&op->thrtimer)) return; - } - if (!op->kt_lastmsg.tv64) { - /* send first RX_CHANGED to the user immediately */ - bcm_rx_changed(op, rxdata); - op->kt_lastmsg = ktime_get(); - return; - } + /* first receiption with enabled throttling mode */ + if (!op->kt_lastmsg.tv64) + goto rx_changed_settime; + /* got a second frame inside a potential throttle period? */ if (ktime_us_delta(ktime_get(), op->kt_lastmsg) < ktime_to_us(op->kt_ival2)) { - /* mark as 'throttled' and start timer */ - lastdata->can_dlc |= RX_THR; + /* do not send the saved data - only start throttle timer */ hrtimer_start(&op->thrtimer, ktime_add(op->kt_lastmsg, op->kt_ival2), HRTIMER_MODE_ABS); @@ -458,7 +462,8 @@ static void bcm_rx_update_and_send(struct bcm_op *op, } /* the gap was that big, that throttling was not needed here */ - bcm_rx_changed(op, rxdata); +rx_changed_settime: + bcm_rx_changed(op, lastdata); op->kt_lastmsg = ktime_get(); } @@ -467,7 +472,7 @@ static void bcm_rx_update_and_send(struct bcm_op *op, * received data stored in op->last_frames[] */ static void bcm_rx_cmp_to_index(struct bcm_op *op, int index, - struct can_frame *rxdata) + const struct can_frame *rxdata) { /* * no one uses the MSBs of can_dlc for comparation, @@ -511,14 +516,12 @@ static void bcm_rx_starttimer(struct bcm_op *op) hrtimer_start(&op->timer, op->kt_ival1, HRTIMER_MODE_REL); } -/* - * bcm_rx_timeout_handler - when the (cyclic) CAN frame receiption timed out - */ -static enum hrtimer_restart bcm_rx_timeout_handler(struct hrtimer *hrtimer) +static void bcm_rx_timeout_tsklet(unsigned long data) { - struct bcm_op *op = container_of(hrtimer, struct bcm_op, timer); + struct bcm_op *op = (struct bcm_op *)data; struct bcm_msg_head msg_head; + /* create notification to user */ msg_head.opcode = RX_TIMEOUT; msg_head.flags = op->flags; msg_head.count = op->count; @@ -528,6 +531,17 @@ static enum hrtimer_restart bcm_rx_timeout_handler(struct hrtimer *hrtimer) msg_head.nframes = 0; bcm_send_to_user(op, &msg_head, NULL, 0); +} + +/* + * bcm_rx_timeout_handler - when the (cyclic) CAN frame receiption timed out + */ +static enum hrtimer_restart bcm_rx_timeout_handler(struct hrtimer *hrtimer) +{ + struct bcm_op *op = container_of(hrtimer, struct bcm_op, timer); + + /* schedule before NET_RX_SOFTIRQ */ + tasklet_hi_schedule(&op->tsklet); /* no restart of the timer is done here! */ @@ -541,9 +555,25 @@ static enum hrtimer_restart bcm_rx_timeout_handler(struct hrtimer *hrtimer) } /* + * bcm_rx_do_flush - helper for bcm_rx_thr_flush + */ +static inline int bcm_rx_do_flush(struct bcm_op *op, int update, int index) +{ + if ((op->last_frames) && (op->last_frames[index].can_dlc & RX_THR)) { + if (update) + bcm_rx_changed(op, &op->last_frames[index]); + return 1; + } + return 0; +} + +/* * bcm_rx_thr_flush - Check for throttled data and send it to the userspace + * + * update == 0 : just check if throttled data is available (any irq context) + * update == 1 : check and send throttled data to userspace (soft_irq context) */ -static int bcm_rx_thr_flush(struct bcm_op *op) +static int bcm_rx_thr_flush(struct bcm_op *op, int update) { int updated = 0; @@ -551,27 +581,25 @@ static int bcm_rx_thr_flush(struct bcm_op *op) int i; /* for MUX filter we start at index 1 */ - for (i = 1; i < op->nframes; i++) { - if ((op->last_frames) && - (op->last_frames[i].can_dlc & RX_THR)) { - op->last_frames[i].can_dlc &= ~RX_THR; - bcm_rx_changed(op, &op->last_frames[i]); - updated++; - } - } + for (i = 1; i < op->nframes; i++) + updated += bcm_rx_do_flush(op, update, i); } else { /* for RX_FILTER_ID and simple filter */ - if (op->last_frames && (op->last_frames[0].can_dlc & RX_THR)) { - op->last_frames[0].can_dlc &= ~RX_THR; - bcm_rx_changed(op, &op->last_frames[0]); - updated++; - } + updated += bcm_rx_do_flush(op, update, 0); } return updated; } +static void bcm_rx_thr_tsklet(unsigned long data) +{ + struct bcm_op *op = (struct bcm_op *)data; + + /* push the changed data to the userspace */ + bcm_rx_thr_flush(op, 1); +} + /* * bcm_rx_thr_handler - the time for blocked content updates is over now: * Check for throttled data and send it to the userspace @@ -580,7 +608,9 @@ static enum hrtimer_restart bcm_rx_thr_handler(struct hrtimer *hrtimer) { struct bcm_op *op = container_of(hrtimer, struct bcm_op, thrtimer); - if (bcm_rx_thr_flush(op)) { + tasklet_schedule(&op->thrtsklet); + + if (bcm_rx_thr_flush(op, 0)) { hrtimer_forward(hrtimer, ktime_get(), op->kt_ival2); return HRTIMER_RESTART; } else { @@ -596,48 +626,38 @@ static enum hrtimer_restart bcm_rx_thr_handler(struct hrtimer *hrtimer) static void bcm_rx_handler(struct sk_buff *skb, void *data) { struct bcm_op *op = (struct bcm_op *)data; - struct can_frame rxframe; + const struct can_frame *rxframe = (struct can_frame *)skb->data; int i; /* disable timeout */ hrtimer_cancel(&op->timer); - if (skb->len == sizeof(rxframe)) { - memcpy(&rxframe, skb->data, sizeof(rxframe)); - /* save rx timestamp */ - op->rx_stamp = skb->tstamp; - /* save originator for recvfrom() */ - op->rx_ifindex = skb->dev->ifindex; - /* update statistics */ - op->frames_abs++; - kfree_skb(skb); + if (op->can_id != rxframe->can_id) + goto rx_freeskb; - } else { - kfree_skb(skb); - return; - } - - if (op->can_id != rxframe.can_id) - return; + /* save rx timestamp */ + op->rx_stamp = skb->tstamp; + /* save originator for recvfrom() */ + op->rx_ifindex = skb->dev->ifindex; + /* update statistics */ + op->frames_abs++; if (op->flags & RX_RTR_FRAME) { /* send reply for RTR-request (placed in op->frames[0]) */ bcm_can_tx(op); - return; + goto rx_freeskb; } if (op->flags & RX_FILTER_ID) { /* the easiest case */ - bcm_rx_update_and_send(op, &op->last_frames[0], &rxframe); - bcm_rx_starttimer(op); - return; + bcm_rx_update_and_send(op, &op->last_frames[0], rxframe); + goto rx_freeskb_starttimer; } if (op->nframes == 1) { /* simple compare with index 0 */ - bcm_rx_cmp_to_index(op, 0, &rxframe); - bcm_rx_starttimer(op); - return; + bcm_rx_cmp_to_index(op, 0, rxframe); + goto rx_freeskb_starttimer; } if (op->nframes > 1) { @@ -649,15 +669,19 @@ static void bcm_rx_handler(struct sk_buff *skb, void *data) */ for (i = 1; i < op->nframes; i++) { - if ((GET_U64(&op->frames[0]) & GET_U64(&rxframe)) == + if ((GET_U64(&op->frames[0]) & GET_U64(rxframe)) == (GET_U64(&op->frames[0]) & GET_U64(&op->frames[i]))) { - bcm_rx_cmp_to_index(op, i, &rxframe); + bcm_rx_cmp_to_index(op, i, rxframe); break; } } - bcm_rx_starttimer(op); } + +rx_freeskb_starttimer: + bcm_rx_starttimer(op); +rx_freeskb: + kfree_skb(skb); } /* @@ -681,6 +705,12 @@ static void bcm_remove_op(struct bcm_op *op) hrtimer_cancel(&op->timer); hrtimer_cancel(&op->thrtimer); + if (op->tsklet.func) + tasklet_kill(&op->tsklet); + + if (op->thrtsklet.func) + tasklet_kill(&op->thrtsklet); + if ((op->frames) && (op->frames != &op->sframe)) kfree(op->frames); @@ -891,6 +921,10 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, hrtimer_init(&op->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); op->timer.function = bcm_tx_timeout_handler; + /* initialize tasklet for tx countevent notification */ + tasklet_init(&op->tsklet, bcm_tx_timeout_tsklet, + (unsigned long) op); + /* currently unused in tx_ops */ hrtimer_init(&op->thrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); @@ -1054,9 +1088,17 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, hrtimer_init(&op->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); op->timer.function = bcm_rx_timeout_handler; + /* initialize tasklet for rx timeout notification */ + tasklet_init(&op->tsklet, bcm_rx_timeout_tsklet, + (unsigned long) op); + hrtimer_init(&op->thrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); op->thrtimer.function = bcm_rx_thr_handler; + /* initialize tasklet for rx throttle handling */ + tasklet_init(&op->thrtsklet, bcm_rx_thr_tsklet, + (unsigned long) op); + /* add this bcm_op to the list of the rx_ops */ list_add(&op->list, &bo->rx_ops); @@ -1102,7 +1144,7 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, */ op->kt_lastmsg = ktime_set(0, 0); hrtimer_cancel(&op->thrtimer); - bcm_rx_thr_flush(op); + bcm_rx_thr_flush(op, 1); } if ((op->flags & STARTTIMER) && op->kt_ival1.tv64) diff --git a/net/core/dev.c b/net/core/dev.c index 446424027d2..382df6c09ee 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -132,6 +132,9 @@ /* Instead of increasing this, you should create a hash table. */ #define MAX_GRO_SKBS 8 +/* This should be increased if a protocol with a bigger head is added. */ +#define GRO_MAX_HEAD (MAX_HEADER + 128) + /* * The list of packet types we will receive (as opposed to discard) * and the routines to invoke. @@ -2345,7 +2348,7 @@ static int napi_gro_complete(struct sk_buff *skb) struct list_head *head = &ptype_base[ntohs(type) & PTYPE_HASH_MASK]; int err = -ENOENT; - if (!skb_shinfo(skb)->frag_list) + if (NAPI_GRO_CB(skb)->count == 1) goto out; rcu_read_lock(); @@ -2365,6 +2368,7 @@ static int napi_gro_complete(struct sk_buff *skb) } out: + skb_shinfo(skb)->gso_size = 0; __skb_push(skb, -skb_network_offset(skb)); return netif_receive_skb(skb); } @@ -2383,7 +2387,7 @@ void napi_gro_flush(struct napi_struct *napi) } EXPORT_SYMBOL(napi_gro_flush); -int napi_gro_receive(struct napi_struct *napi, struct sk_buff *skb) +static int __napi_gro_receive(struct napi_struct *napi, struct sk_buff *skb) { struct sk_buff **pp = NULL; struct packet_type *ptype; @@ -2392,6 +2396,7 @@ int napi_gro_receive(struct napi_struct *napi, struct sk_buff *skb) int count = 0; int same_flow; int mac_len; + int free; if (!(skb->dev->features & NETIF_F_GRO)) goto normal; @@ -2408,6 +2413,7 @@ int napi_gro_receive(struct napi_struct *napi, struct sk_buff *skb) skb->mac_len = mac_len; NAPI_GRO_CB(skb)->same_flow = 0; NAPI_GRO_CB(skb)->flush = 0; + NAPI_GRO_CB(skb)->free = 0; for (p = napi->gro_list; p; p = p->next) { count++; @@ -2427,6 +2433,7 @@ int napi_gro_receive(struct napi_struct *napi, struct sk_buff *skb) goto normal; same_flow = NAPI_GRO_CB(skb)->same_flow; + free = NAPI_GRO_CB(skb)->free; if (pp) { struct sk_buff *nskb = *pp; @@ -2446,17 +2453,91 @@ int napi_gro_receive(struct napi_struct *napi, struct sk_buff *skb) } NAPI_GRO_CB(skb)->count = 1; + skb_shinfo(skb)->gso_size = skb->len; skb->next = napi->gro_list; napi->gro_list = skb; ok: - return NET_RX_SUCCESS; + return free; normal: - return netif_receive_skb(skb); + return -1; +} + +int napi_gro_receive(struct napi_struct *napi, struct sk_buff *skb) +{ + switch (__napi_gro_receive(napi, skb)) { + case -1: + return netif_receive_skb(skb); + + case 1: + kfree_skb(skb); + break; + } + + return NET_RX_SUCCESS; } EXPORT_SYMBOL(napi_gro_receive); +int napi_gro_frags(struct napi_struct *napi, struct napi_gro_fraginfo *info) +{ + struct net_device *dev = napi->dev; + struct sk_buff *skb = napi->skb; + int err = NET_RX_DROP; + + napi->skb = NULL; + + if (!skb) { + skb = netdev_alloc_skb(dev, GRO_MAX_HEAD + NET_IP_ALIGN); + if (!skb) + goto out; + + skb_reserve(skb, NET_IP_ALIGN); + } + + BUG_ON(info->nr_frags > MAX_SKB_FRAGS); + skb_shinfo(skb)->nr_frags = info->nr_frags; + memcpy(skb_shinfo(skb)->frags, info->frags, sizeof(info->frags)); + + skb->data_len = info->len; + skb->len += info->len; + skb->truesize += info->len; + + if (!pskb_may_pull(skb, ETH_HLEN)) + goto reuse; + + err = NET_RX_SUCCESS; + + skb->protocol = eth_type_trans(skb, dev); + + skb->ip_summed = info->ip_summed; + skb->csum = info->csum; + + switch (__napi_gro_receive(napi, skb)) { + case -1: + return netif_receive_skb(skb); + + case 0: + goto out; + } + +reuse: + skb_shinfo(skb)->nr_frags = 0; + + skb->len -= skb->data_len; + skb->truesize -= skb->data_len; + skb->data_len = 0; + + __skb_pull(skb, skb_headlen(skb)); + skb_reserve(skb, NET_IP_ALIGN - skb_headroom(skb)); + + napi->skb = skb; + +out: + return err; +} +EXPORT_SYMBOL(napi_gro_frags); + static int process_backlog(struct napi_struct *napi, int quota) { int work = 0; @@ -2535,11 +2616,12 @@ void netif_napi_add(struct net_device *dev, struct napi_struct *napi, { INIT_LIST_HEAD(&napi->poll_list); napi->gro_list = NULL; + napi->skb = NULL; napi->poll = poll; napi->weight = weight; list_add(&napi->dev_list, &dev->napi_list); -#ifdef CONFIG_NETPOLL napi->dev = dev; +#ifdef CONFIG_NETPOLL spin_lock_init(&napi->poll_lock); napi->poll_owner = -1; #endif @@ -2552,6 +2634,7 @@ void netif_napi_del(struct napi_struct *napi) struct sk_buff *skb, *next; list_del_init(&napi->dev_list); + kfree(napi->skb); for (skb = napi->gro_list; skb; skb = next) { next = skb->next; @@ -5066,13 +5149,14 @@ static struct pernet_operations __net_initdata netdev_net_ops = { static void __net_exit default_device_exit(struct net *net) { - struct net_device *dev, *next; + struct net_device *dev; /* * Push all migratable of the network devices back to the * initial network namespace */ rtnl_lock(); - for_each_netdev_safe(net, dev, next) { +restart: + for_each_netdev(net, dev) { int err; char fb_name[IFNAMSIZ]; @@ -5083,7 +5167,7 @@ static void __net_exit default_device_exit(struct net *net) /* Delete virtual devices */ if (dev->rtnl_link_ops && dev->rtnl_link_ops->dellink) { dev->rtnl_link_ops->dellink(dev); - continue; + goto restart; } /* Push remaing network devices to init_net */ @@ -5094,6 +5178,7 @@ static void __net_exit default_device_exit(struct net *net) __func__, dev->name, err); BUG(); } + goto restart; } rtnl_unlock(); } diff --git a/net/core/neighbour.c b/net/core/neighbour.c index 9c3717a23cf..f66c58df895 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -2414,7 +2414,7 @@ static void *neigh_stat_seq_start(struct seq_file *seq, loff_t *pos) if (*pos == 0) return SEQ_START_TOKEN; - for (cpu = *pos-1; cpu < NR_CPUS; ++cpu) { + for (cpu = *pos-1; cpu < nr_cpu_ids; ++cpu) { if (!cpu_possible(cpu)) continue; *pos = cpu+1; @@ -2429,7 +2429,7 @@ static void *neigh_stat_seq_next(struct seq_file *seq, void *v, loff_t *pos) struct neigh_table *tbl = pde->data; int cpu; - for (cpu = *pos; cpu < NR_CPUS; ++cpu) { + for (cpu = *pos; cpu < nr_cpu_ids; ++cpu) { if (!cpu_possible(cpu)) continue; *pos = cpu+1; diff --git a/net/core/skbuff.c b/net/core/skbuff.c index b8d0abb2643..5110b359c75 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -2594,6 +2594,17 @@ int skb_gro_receive(struct sk_buff **head, struct sk_buff *skb) if (skb_shinfo(p)->frag_list) goto merge; + else if (!skb_headlen(p) && !skb_headlen(skb) && + skb_shinfo(p)->nr_frags + skb_shinfo(skb)->nr_frags < + MAX_SKB_FRAGS) { + memcpy(skb_shinfo(p)->frags + skb_shinfo(p)->nr_frags, + skb_shinfo(skb)->frags, + skb_shinfo(skb)->nr_frags * sizeof(skb_frag_t)); + + skb_shinfo(p)->nr_frags += skb_shinfo(skb)->nr_frags; + NAPI_GRO_CB(skb)->free = 1; + goto done; + } headroom = skb_headroom(p); nskb = netdev_alloc_skb(p->dev, headroom); @@ -2613,6 +2624,7 @@ int skb_gro_receive(struct sk_buff **head, struct sk_buff *skb) *NAPI_GRO_CB(nskb) = *NAPI_GRO_CB(p); skb_shinfo(nskb)->frag_list = p; + skb_shinfo(nskb)->gso_size = skb_shinfo(p)->gso_size; skb_header_release(p); nskb->prev = p; @@ -2627,11 +2639,12 @@ int skb_gro_receive(struct sk_buff **head, struct sk_buff *skb) p = nskb; merge: - NAPI_GRO_CB(p)->count++; p->prev->next = skb; p->prev = skb; skb_header_release(skb); +done: + NAPI_GRO_CB(p)->count++; p->data_len += skb->len; p->truesize += skb->len; p->len += skb->len; diff --git a/net/dcb/dcbnl.c b/net/dcb/dcbnl.c index 5dbfe5fdc0d..8379496de82 100644 --- a/net/dcb/dcbnl.c +++ b/net/dcb/dcbnl.c @@ -191,7 +191,7 @@ static int dcbnl_reply(u8 value, u8 event, u8 cmd, u8 attr, u32 pid, return 0; nlmsg_failure: err: - kfree(dcbnl_skb); + kfree_skb(dcbnl_skb); return ret; } @@ -272,7 +272,7 @@ static int dcbnl_getpfccfg(struct net_device *netdev, struct nlattr **tb, return 0; nlmsg_failure: err: - kfree(dcbnl_skb); + kfree_skb(dcbnl_skb); err_out: return -EINVAL; } @@ -314,7 +314,7 @@ static int dcbnl_getperm_hwaddr(struct net_device *netdev, struct nlattr **tb, nlmsg_failure: err: - kfree(dcbnl_skb); + kfree_skb(dcbnl_skb); err_out: return -EINVAL; } @@ -380,7 +380,7 @@ static int dcbnl_getcap(struct net_device *netdev, struct nlattr **tb, return 0; nlmsg_failure: err: - kfree(dcbnl_skb); + kfree_skb(dcbnl_skb); err_out: return -EINVAL; } @@ -458,7 +458,7 @@ static int dcbnl_getnumtcs(struct net_device *netdev, struct nlattr **tb, return 0; nlmsg_failure: err: - kfree(dcbnl_skb); + kfree_skb(dcbnl_skb); err_out: return ret; } @@ -687,7 +687,7 @@ err_pg: nla_nest_cancel(dcbnl_skb, pg_nest); nlmsg_failure: err: - kfree(dcbnl_skb); + kfree_skb(dcbnl_skb); err_out: ret = -EINVAL; return ret; @@ -949,7 +949,7 @@ err_bcn: nla_nest_cancel(dcbnl_skb, bcn_nest); nlmsg_failure: err: - kfree(dcbnl_skb); + kfree_skb(dcbnl_skb); err_out: ret = -EINVAL; return ret; diff --git a/net/dccp/Kconfig b/net/dccp/Kconfig index 7aa2a7acc7e..ad6dffd9070 100644 --- a/net/dccp/Kconfig +++ b/net/dccp/Kconfig @@ -1,7 +1,6 @@ menuconfig IP_DCCP tristate "The DCCP Protocol (EXPERIMENTAL)" depends on INET && EXPERIMENTAL - select IP_DCCP_CCID2 ---help--- Datagram Congestion Control Protocol (RFC 4340) @@ -25,9 +24,6 @@ config INET_DCCP_DIAG def_tristate y if (IP_DCCP = y && INET_DIAG = y) def_tristate m -config IP_DCCP_ACKVEC - bool - source "net/dccp/ccids/Kconfig" menu "DCCP Kernel Hacking" diff --git a/net/dccp/Makefile b/net/dccp/Makefile index f4f8793aaff..2991efcc8de 100644 --- a/net/dccp/Makefile +++ b/net/dccp/Makefile @@ -2,14 +2,23 @@ obj-$(CONFIG_IP_DCCP) += dccp.o dccp_ipv4.o dccp-y := ccid.o feat.o input.o minisocks.o options.o output.o proto.o timer.o +# +# CCID algorithms to be used by dccp.ko +# +# CCID-2 is default (RFC 4340, p. 77) and has Ack Vectors as dependency +dccp-y += ccids/ccid2.o ackvec.o +dccp-$(CONFIG_IP_DCCP_CCID3) += ccids/ccid3.o +dccp-$(CONFIG_IP_DCCP_TFRC_LIB) += ccids/lib/tfrc.o \ + ccids/lib/tfrc_equation.o \ + ccids/lib/packet_history.o \ + ccids/lib/loss_interval.o + dccp_ipv4-y := ipv4.o # build dccp_ipv6 as module whenever either IPv6 or DCCP is a module obj-$(subst y,$(CONFIG_IP_DCCP),$(CONFIG_IPV6)) += dccp_ipv6.o dccp_ipv6-y := ipv6.o -dccp-$(CONFIG_IP_DCCP_ACKVEC) += ackvec.o - obj-$(CONFIG_INET_DCCP_DIAG) += dccp_diag.o obj-$(CONFIG_NET_DCCPPROBE) += dccp_probe.o @@ -17,5 +26,3 @@ dccp-$(CONFIG_SYSCTL) += sysctl.o dccp_diag-y := diag.o dccp_probe-y := probe.o - -obj-y += ccids/ diff --git a/net/dccp/ackvec.h b/net/dccp/ackvec.h index 4ccee030524..45f95e55f87 100644 --- a/net/dccp/ackvec.h +++ b/net/dccp/ackvec.h @@ -84,7 +84,6 @@ struct dccp_ackvec_record { struct sock; struct sk_buff; -#ifdef CONFIG_IP_DCCP_ACKVEC extern int dccp_ackvec_init(void); extern void dccp_ackvec_exit(void); @@ -106,52 +105,4 @@ static inline int dccp_ackvec_pending(const struct dccp_ackvec *av) { return av->av_vec_len; } -#else /* CONFIG_IP_DCCP_ACKVEC */ -static inline int dccp_ackvec_init(void) -{ - return 0; -} - -static inline void dccp_ackvec_exit(void) -{ -} - -static inline struct dccp_ackvec *dccp_ackvec_alloc(const gfp_t priority) -{ - return NULL; -} - -static inline void dccp_ackvec_free(struct dccp_ackvec *av) -{ -} - -static inline int dccp_ackvec_add(struct dccp_ackvec *av, const struct sock *sk, - const u64 ackno, const u8 state) -{ - return -1; -} - -static inline void dccp_ackvec_check_rcv_ackno(struct dccp_ackvec *av, - struct sock *sk, const u64 ackno) -{ -} - -static inline int dccp_ackvec_parse(struct sock *sk, const struct sk_buff *skb, - const u64 *ackno, const u8 opt, - const u8 *value, const u8 len) -{ - return -1; -} - -static inline int dccp_insert_option_ackvec(const struct sock *sk, - const struct sk_buff *skb) -{ - return -1; -} - -static inline int dccp_ackvec_pending(const struct dccp_ackvec *av) -{ - return 0; -} -#endif /* CONFIG_IP_DCCP_ACKVEC */ #endif /* _ACKVEC_H */ diff --git a/net/dccp/ccid.c b/net/dccp/ccid.c index bcc643f992a..f3e9ba1cfd0 100644 --- a/net/dccp/ccid.c +++ b/net/dccp/ccid.c @@ -12,56 +12,70 @@ */ #include "ccid.h" +#include "ccids/lib/tfrc.h" -static u8 builtin_ccids[] = { - DCCPC_CCID2, /* CCID2 is supported by default */ -#if defined(CONFIG_IP_DCCP_CCID3) || defined(CONFIG_IP_DCCP_CCID3_MODULE) - DCCPC_CCID3, +static struct ccid_operations *ccids[] = { + &ccid2_ops, +#ifdef CONFIG_IP_DCCP_CCID3 + &ccid3_ops, #endif }; -static struct ccid_operations *ccids[CCID_MAX]; -#if defined(CONFIG_SMP) || defined(CONFIG_PREEMPT) -static atomic_t ccids_lockct = ATOMIC_INIT(0); -static DEFINE_SPINLOCK(ccids_lock); - -/* - * The strategy is: modifications ccids vector are short, do not sleep and - * veeery rare, but read access should be free of any exclusive locks. - */ -static void ccids_write_lock(void) +static struct ccid_operations *ccid_by_number(const u8 id) { - spin_lock(&ccids_lock); - while (atomic_read(&ccids_lockct) != 0) { - spin_unlock(&ccids_lock); - yield(); - spin_lock(&ccids_lock); - } + int i; + + for (i = 0; i < ARRAY_SIZE(ccids); i++) + if (ccids[i]->ccid_id == id) + return ccids[i]; + return NULL; } -static inline void ccids_write_unlock(void) +/* check that up to @array_len members in @ccid_array are supported */ +bool ccid_support_check(u8 const *ccid_array, u8 array_len) { - spin_unlock(&ccids_lock); + while (array_len > 0) + if (ccid_by_number(ccid_array[--array_len]) == NULL) + return false; + return true; } -static inline void ccids_read_lock(void) +/** + * ccid_get_builtin_ccids - Populate a list of built-in CCIDs + * @ccid_array: pointer to copy into + * @array_len: value to return length into + * This function allocates memory - caller must see that it is freed after use. + */ +int ccid_get_builtin_ccids(u8 **ccid_array, u8 *array_len) { - atomic_inc(&ccids_lockct); - smp_mb__after_atomic_inc(); - spin_unlock_wait(&ccids_lock); + *ccid_array = kmalloc(ARRAY_SIZE(ccids), gfp_any()); + if (*ccid_array == NULL) + return -ENOBUFS; + + for (*array_len = 0; *array_len < ARRAY_SIZE(ccids); *array_len += 1) + (*ccid_array)[*array_len] = ccids[*array_len]->ccid_id; + return 0; } -static inline void ccids_read_unlock(void) +int ccid_getsockopt_builtin_ccids(struct sock *sk, int len, + char __user *optval, int __user *optlen) { - atomic_dec(&ccids_lockct); -} + u8 *ccid_array, array_len; + int err = 0; -#else -#define ccids_write_lock() do { } while(0) -#define ccids_write_unlock() do { } while(0) -#define ccids_read_lock() do { } while(0) -#define ccids_read_unlock() do { } while(0) -#endif + if (len < ARRAY_SIZE(ccids)) + return -EINVAL; + + if (ccid_get_builtin_ccids(&ccid_array, &array_len)) + return -ENOBUFS; + + if (put_user(array_len, optlen) || + copy_to_user(optval, ccid_array, array_len)) + err = -EFAULT; + + kfree(ccid_array); + return err; +} static struct kmem_cache *ccid_kmem_cache_create(int obj_size, const char *fmt,...) { @@ -93,48 +107,7 @@ static void ccid_kmem_cache_destroy(struct kmem_cache *slab) } } -/* check that up to @array_len members in @ccid_array are supported */ -bool ccid_support_check(u8 const *ccid_array, u8 array_len) -{ - u8 i, j, found; - - for (i = 0, found = 0; i < array_len; i++, found = 0) { - for (j = 0; !found && j < ARRAY_SIZE(builtin_ccids); j++) - found = (ccid_array[i] == builtin_ccids[j]); - if (!found) - return false; - } - return true; -} - -/** - * ccid_get_builtin_ccids - Provide copy of `builtin' CCID array - * @ccid_array: pointer to copy into - * @array_len: value to return length into - * This function allocates memory - caller must see that it is freed after use. - */ -int ccid_get_builtin_ccids(u8 **ccid_array, u8 *array_len) -{ - *ccid_array = kmemdup(builtin_ccids, sizeof(builtin_ccids), gfp_any()); - if (*ccid_array == NULL) - return -ENOBUFS; - *array_len = ARRAY_SIZE(builtin_ccids); - return 0; -} - -int ccid_getsockopt_builtin_ccids(struct sock *sk, int len, - char __user *optval, int __user *optlen) -{ - if (len < sizeof(builtin_ccids)) - return -EINVAL; - - if (put_user(sizeof(builtin_ccids), optlen) || - copy_to_user(optval, builtin_ccids, sizeof(builtin_ccids))) - return -EFAULT; - return 0; -} - -int ccid_register(struct ccid_operations *ccid_ops) +static int ccid_activate(struct ccid_operations *ccid_ops) { int err = -ENOBUFS; @@ -152,79 +125,40 @@ int ccid_register(struct ccid_operations *ccid_ops) if (ccid_ops->ccid_hc_tx_slab == NULL) goto out_free_rx_slab; - ccids_write_lock(); - err = -EEXIST; - if (ccids[ccid_ops->ccid_id] == NULL) { - ccids[ccid_ops->ccid_id] = ccid_ops; - err = 0; - } - ccids_write_unlock(); - if (err != 0) - goto out_free_tx_slab; - - pr_info("CCID: Registered CCID %d (%s)\n", + pr_info("CCID: Activated CCID %d (%s)\n", ccid_ops->ccid_id, ccid_ops->ccid_name); + err = 0; out: return err; -out_free_tx_slab: - ccid_kmem_cache_destroy(ccid_ops->ccid_hc_tx_slab); - ccid_ops->ccid_hc_tx_slab = NULL; - goto out; out_free_rx_slab: ccid_kmem_cache_destroy(ccid_ops->ccid_hc_rx_slab); ccid_ops->ccid_hc_rx_slab = NULL; goto out; } -EXPORT_SYMBOL_GPL(ccid_register); - -int ccid_unregister(struct ccid_operations *ccid_ops) +static void ccid_deactivate(struct ccid_operations *ccid_ops) { - ccids_write_lock(); - ccids[ccid_ops->ccid_id] = NULL; - ccids_write_unlock(); - ccid_kmem_cache_destroy(ccid_ops->ccid_hc_tx_slab); ccid_ops->ccid_hc_tx_slab = NULL; ccid_kmem_cache_destroy(ccid_ops->ccid_hc_rx_slab); ccid_ops->ccid_hc_rx_slab = NULL; - pr_info("CCID: Unregistered CCID %d (%s)\n", + pr_info("CCID: Deactivated CCID %d (%s)\n", ccid_ops->ccid_id, ccid_ops->ccid_name); - return 0; } -EXPORT_SYMBOL_GPL(ccid_unregister); - -struct ccid *ccid_new(unsigned char id, struct sock *sk, int rx, gfp_t gfp) +struct ccid *ccid_new(const u8 id, struct sock *sk, bool rx) { - struct ccid_operations *ccid_ops; + struct ccid_operations *ccid_ops = ccid_by_number(id); struct ccid *ccid = NULL; - ccids_read_lock(); -#ifdef CONFIG_MODULES - if (ccids[id] == NULL) { - /* We only try to load if in process context */ - ccids_read_unlock(); - if (gfp & GFP_ATOMIC) - goto out; - request_module("net-dccp-ccid-%d", id); - ccids_read_lock(); - } -#endif - ccid_ops = ccids[id]; if (ccid_ops == NULL) - goto out_unlock; - - if (!try_module_get(ccid_ops->ccid_owner)) - goto out_unlock; - - ccids_read_unlock(); + goto out; ccid = kmem_cache_alloc(rx ? ccid_ops->ccid_hc_rx_slab : - ccid_ops->ccid_hc_tx_slab, gfp); + ccid_ops->ccid_hc_tx_slab, gfp_any()); if (ccid == NULL) - goto out_module_put; + goto out; ccid->ccid_ops = ccid_ops; if (rx) { memset(ccid + 1, 0, ccid_ops->ccid_hc_rx_obj_size); @@ -239,53 +173,57 @@ struct ccid *ccid_new(unsigned char id, struct sock *sk, int rx, gfp_t gfp) } out: return ccid; -out_unlock: - ccids_read_unlock(); - goto out; out_free_ccid: kmem_cache_free(rx ? ccid_ops->ccid_hc_rx_slab : ccid_ops->ccid_hc_tx_slab, ccid); ccid = NULL; -out_module_put: - module_put(ccid_ops->ccid_owner); goto out; } -EXPORT_SYMBOL_GPL(ccid_new); - -static void ccid_delete(struct ccid *ccid, struct sock *sk, int rx) +void ccid_hc_rx_delete(struct ccid *ccid, struct sock *sk) { - struct ccid_operations *ccid_ops; - - if (ccid == NULL) - return; - - ccid_ops = ccid->ccid_ops; - if (rx) { - if (ccid_ops->ccid_hc_rx_exit != NULL) - ccid_ops->ccid_hc_rx_exit(sk); - kmem_cache_free(ccid_ops->ccid_hc_rx_slab, ccid); - } else { - if (ccid_ops->ccid_hc_tx_exit != NULL) - ccid_ops->ccid_hc_tx_exit(sk); - kmem_cache_free(ccid_ops->ccid_hc_tx_slab, ccid); + if (ccid != NULL) { + if (ccid->ccid_ops->ccid_hc_rx_exit != NULL) + ccid->ccid_ops->ccid_hc_rx_exit(sk); + kmem_cache_free(ccid->ccid_ops->ccid_hc_rx_slab, ccid); } - ccids_read_lock(); - if (ccids[ccid_ops->ccid_id] != NULL) - module_put(ccid_ops->ccid_owner); - ccids_read_unlock(); } -void ccid_hc_rx_delete(struct ccid *ccid, struct sock *sk) +void ccid_hc_tx_delete(struct ccid *ccid, struct sock *sk) { - ccid_delete(ccid, sk, 1); + if (ccid != NULL) { + if (ccid->ccid_ops->ccid_hc_tx_exit != NULL) + ccid->ccid_ops->ccid_hc_tx_exit(sk); + kmem_cache_free(ccid->ccid_ops->ccid_hc_tx_slab, ccid); + } } -EXPORT_SYMBOL_GPL(ccid_hc_rx_delete); - -void ccid_hc_tx_delete(struct ccid *ccid, struct sock *sk) +int __init ccid_initialize_builtins(void) { - ccid_delete(ccid, sk, 0); + int i, err = tfrc_lib_init(); + + if (err) + return err; + + for (i = 0; i < ARRAY_SIZE(ccids); i++) { + err = ccid_activate(ccids[i]); + if (err) + goto unwind_registrations; + } + return 0; + +unwind_registrations: + while(--i >= 0) + ccid_deactivate(ccids[i]); + tfrc_lib_exit(); + return err; } -EXPORT_SYMBOL_GPL(ccid_hc_tx_delete); +void ccid_cleanup_builtins(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ccids); i++) + ccid_deactivate(ccids[i]); + tfrc_lib_exit(); +} diff --git a/net/dccp/ccid.h b/net/dccp/ccid.h index 18f69423a70..facedd20b53 100644 --- a/net/dccp/ccid.h +++ b/net/dccp/ccid.h @@ -29,7 +29,6 @@ struct tcp_info; * @ccid_id: numerical CCID ID (up to %CCID_MAX, cf. table 5 in RFC 4340, 10.) * @ccid_ccmps: the CCMPS including network/transport headers (0 when disabled) * @ccid_name: alphabetical identifier string for @ccid_id - * @ccid_owner: module which implements/owns this CCID * @ccid_hc_{r,t}x_slab: memory pool for the receiver/sender half-connection * @ccid_hc_{r,t}x_obj_size: size of the receiver/sender half-connection socket * @@ -48,7 +47,6 @@ struct ccid_operations { unsigned char ccid_id; __u32 ccid_ccmps; const char *ccid_name; - struct module *ccid_owner; struct kmem_cache *ccid_hc_rx_slab, *ccid_hc_tx_slab; __u32 ccid_hc_rx_obj_size, @@ -90,8 +88,13 @@ struct ccid_operations { int __user *optlen); }; -extern int ccid_register(struct ccid_operations *ccid_ops); -extern int ccid_unregister(struct ccid_operations *ccid_ops); +extern struct ccid_operations ccid2_ops; +#ifdef CONFIG_IP_DCCP_CCID3 +extern struct ccid_operations ccid3_ops; +#endif + +extern int ccid_initialize_builtins(void); +extern void ccid_cleanup_builtins(void); struct ccid { struct ccid_operations *ccid_ops; @@ -108,8 +111,7 @@ extern int ccid_get_builtin_ccids(u8 **ccid_array, u8 *array_len); extern int ccid_getsockopt_builtin_ccids(struct sock *sk, int len, char __user *, int __user *); -extern struct ccid *ccid_new(unsigned char id, struct sock *sk, int rx, - gfp_t gfp); +extern struct ccid *ccid_new(const u8 id, struct sock *sk, bool rx); static inline int ccid_get_current_rx_ccid(struct dccp_sock *dp) { diff --git a/net/dccp/ccids/Kconfig b/net/dccp/ccids/Kconfig index 12275943eab..b28bf962edc 100644 --- a/net/dccp/ccids/Kconfig +++ b/net/dccp/ccids/Kconfig @@ -1,80 +1,51 @@ menu "DCCP CCIDs Configuration (EXPERIMENTAL)" depends on EXPERIMENTAL -config IP_DCCP_CCID2 - tristate "CCID2 (TCP-Like) (EXPERIMENTAL)" - def_tristate IP_DCCP - select IP_DCCP_ACKVEC - ---help--- - CCID 2, TCP-like Congestion Control, denotes Additive Increase, - Multiplicative Decrease (AIMD) congestion control with behavior - modelled directly on TCP, including congestion window, slow start, - timeouts, and so forth [RFC 2581]. CCID 2 achieves maximum - bandwidth over the long term, consistent with the use of end-to-end - congestion control, but halves its congestion window in response to - each congestion event. This leads to the abrupt rate changes - typical of TCP. Applications should use CCID 2 if they prefer - maximum bandwidth utilization to steadiness of rate. This is often - the case for applications that are not playing their data directly - to the user. For example, a hypothetical application that - transferred files over DCCP, using application-level retransmissions - for lost packets, would prefer CCID 2 to CCID 3. On-line games may - also prefer CCID 2. See RFC 4341 for further details. - - CCID2 is the default CCID used by DCCP. - config IP_DCCP_CCID2_DEBUG - bool "CCID2 debugging messages" - depends on IP_DCCP_CCID2 - ---help--- - Enable CCID2-specific debugging messages. + bool "CCID-2 debugging messages" + ---help--- + Enable CCID-2 specific debugging messages. - When compiling CCID2 as a module, this debugging output can - additionally be toggled by setting the ccid2_debug module - parameter to 0 or 1. + The debugging output can additionally be toggled by setting the + ccid2_debug parameter to 0 or 1. - If in doubt, say N. + If in doubt, say N. config IP_DCCP_CCID3 - tristate "CCID3 (TCP-Friendly) (EXPERIMENTAL)" - def_tristate IP_DCCP - select IP_DCCP_TFRC_LIB + bool "CCID-3 (TCP-Friendly) (EXPERIMENTAL)" + def_bool y if (IP_DCCP = y || IP_DCCP = m) ---help--- - CCID 3 denotes TCP-Friendly Rate Control (TFRC), an equation-based + CCID-3 denotes TCP-Friendly Rate Control (TFRC), an equation-based rate-controlled congestion control mechanism. TFRC is designed to be reasonably fair when competing for bandwidth with TCP-like flows, where a flow is "reasonably fair" if its sending rate is generally within a factor of two of the sending rate of a TCP flow under the same conditions. However, TFRC has a much lower variation of - throughput over time compared with TCP, which makes CCID 3 more - suitable than CCID 2 for applications such streaming media where a + throughput over time compared with TCP, which makes CCID-3 more + suitable than CCID-2 for applications such streaming media where a relatively smooth sending rate is of importance. - CCID 3 is further described in RFC 4342, + CCID-3 is further described in RFC 4342, http://www.ietf.org/rfc/rfc4342.txt The TFRC congestion control algorithms were initially described in - RFC 3448. + RFC 5448. This text was extracted from RFC 4340 (sec. 10.2), http://www.ietf.org/rfc/rfc4340.txt - - To compile this CCID as a module, choose M here: the module will be - called dccp_ccid3. - If in doubt, say M. + If in doubt, say N. config IP_DCCP_CCID3_DEBUG - bool "CCID3 debugging messages" - depends on IP_DCCP_CCID3 - ---help--- - Enable CCID3-specific debugging messages. + bool "CCID-3 debugging messages" + depends on IP_DCCP_CCID3 + ---help--- + Enable CCID-3 specific debugging messages. - When compiling CCID3 as a module, this debugging output can - additionally be toggled by setting the ccid3_debug module - parameter to 0 or 1. + The debugging output can additionally be toggled by setting the + ccid3_debug parameter to 0 or 1. - If in doubt, say N. + If in doubt, say N. config IP_DCCP_CCID3_RTO int "Use higher bound for nofeedback timer" @@ -108,12 +79,8 @@ config IP_DCCP_CCID3_RTO therefore not be performed on WANs. config IP_DCCP_TFRC_LIB - tristate - default n + def_bool y if IP_DCCP_CCID3 config IP_DCCP_TFRC_DEBUG - bool - depends on IP_DCCP_TFRC_LIB - default y if IP_DCCP_CCID3_DEBUG - + def_bool y if IP_DCCP_CCID3_DEBUG endmenu diff --git a/net/dccp/ccids/Makefile b/net/dccp/ccids/Makefile deleted file mode 100644 index 438f20bccff..00000000000 --- a/net/dccp/ccids/Makefile +++ /dev/null @@ -1,9 +0,0 @@ -obj-$(CONFIG_IP_DCCP_CCID3) += dccp_ccid3.o - -dccp_ccid3-y := ccid3.o - -obj-$(CONFIG_IP_DCCP_CCID2) += dccp_ccid2.o - -dccp_ccid2-y := ccid2.o - -obj-y += lib/ diff --git a/net/dccp/ccids/ccid2.c b/net/dccp/ccids/ccid2.c index c9ea19a4d85..d235294ace2 100644 --- a/net/dccp/ccids/ccid2.c +++ b/net/dccp/ccids/ccid2.c @@ -768,10 +768,9 @@ static void ccid2_hc_rx_packet_recv(struct sock *sk, struct sk_buff *skb) } } -static struct ccid_operations ccid2 = { +struct ccid_operations ccid2_ops = { .ccid_id = DCCPC_CCID2, .ccid_name = "TCP-like", - .ccid_owner = THIS_MODULE, .ccid_hc_tx_obj_size = sizeof(struct ccid2_hc_tx_sock), .ccid_hc_tx_init = ccid2_hc_tx_init, .ccid_hc_tx_exit = ccid2_hc_tx_exit, @@ -784,22 +783,5 @@ static struct ccid_operations ccid2 = { #ifdef CONFIG_IP_DCCP_CCID2_DEBUG module_param(ccid2_debug, bool, 0644); -MODULE_PARM_DESC(ccid2_debug, "Enable debug messages"); +MODULE_PARM_DESC(ccid2_debug, "Enable CCID-2 debug messages"); #endif - -static __init int ccid2_module_init(void) -{ - return ccid_register(&ccid2); -} -module_init(ccid2_module_init); - -static __exit void ccid2_module_exit(void) -{ - ccid_unregister(&ccid2); -} -module_exit(ccid2_module_exit); - -MODULE_AUTHOR("Andrea Bittau <a.bittau@cs.ucl.ac.uk>"); -MODULE_DESCRIPTION("DCCP TCP-Like (CCID2) CCID"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("net-dccp-ccid-2"); diff --git a/net/dccp/ccids/ccid3.c b/net/dccp/ccids/ccid3.c index 3b8bd7ca676..a27b7f4c19c 100644 --- a/net/dccp/ccids/ccid3.c +++ b/net/dccp/ccids/ccid3.c @@ -940,10 +940,9 @@ static int ccid3_hc_rx_getsockopt(struct sock *sk, const int optname, int len, return 0; } -static struct ccid_operations ccid3 = { +struct ccid_operations ccid3_ops = { .ccid_id = DCCPC_CCID3, .ccid_name = "TCP-Friendly Rate Control", - .ccid_owner = THIS_MODULE, .ccid_hc_tx_obj_size = sizeof(struct ccid3_hc_tx_sock), .ccid_hc_tx_init = ccid3_hc_tx_init, .ccid_hc_tx_exit = ccid3_hc_tx_exit, @@ -964,23 +963,5 @@ static struct ccid_operations ccid3 = { #ifdef CONFIG_IP_DCCP_CCID3_DEBUG module_param(ccid3_debug, bool, 0644); -MODULE_PARM_DESC(ccid3_debug, "Enable debug messages"); +MODULE_PARM_DESC(ccid3_debug, "Enable CCID-3 debug messages"); #endif - -static __init int ccid3_module_init(void) -{ - return ccid_register(&ccid3); -} -module_init(ccid3_module_init); - -static __exit void ccid3_module_exit(void) -{ - ccid_unregister(&ccid3); -} -module_exit(ccid3_module_exit); - -MODULE_AUTHOR("Ian McDonald <ian.mcdonald@jandi.co.nz>, " - "Arnaldo Carvalho de Melo <acme@ghostprotocols.net>"); -MODULE_DESCRIPTION("DCCP TFRC CCID3 CCID"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("net-dccp-ccid-3"); diff --git a/net/dccp/ccids/lib/Makefile b/net/dccp/ccids/lib/Makefile deleted file mode 100644 index 68c93e3d89d..00000000000 --- a/net/dccp/ccids/lib/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -obj-$(CONFIG_IP_DCCP_TFRC_LIB) += dccp_tfrc_lib.o - -dccp_tfrc_lib-y := tfrc.o tfrc_equation.o packet_history.o loss_interval.o diff --git a/net/dccp/ccids/lib/loss_interval.c b/net/dccp/ccids/lib/loss_interval.c index 5b3ce0688c5..4d1e4012726 100644 --- a/net/dccp/ccids/lib/loss_interval.c +++ b/net/dccp/ccids/lib/loss_interval.c @@ -60,7 +60,6 @@ void tfrc_lh_cleanup(struct tfrc_loss_hist *lh) lh->ring[LIH_INDEX(lh->counter)] = NULL; } } -EXPORT_SYMBOL_GPL(tfrc_lh_cleanup); static void tfrc_lh_calc_i_mean(struct tfrc_loss_hist *lh) { @@ -121,7 +120,6 @@ u8 tfrc_lh_update_i_mean(struct tfrc_loss_hist *lh, struct sk_buff *skb) return (lh->i_mean < old_i_mean); } -EXPORT_SYMBOL_GPL(tfrc_lh_update_i_mean); /* Determine if `new_loss' does begin a new loss interval [RFC 4342, 10.2] */ static inline u8 tfrc_lh_is_new_loss(struct tfrc_loss_interval *cur, @@ -169,7 +167,6 @@ int tfrc_lh_interval_add(struct tfrc_loss_hist *lh, struct tfrc_rx_hist *rh, } return 1; } -EXPORT_SYMBOL_GPL(tfrc_lh_interval_add); int __init tfrc_li_init(void) { diff --git a/net/dccp/ccids/lib/packet_history.c b/net/dccp/ccids/lib/packet_history.c index 6cc108afdc3..b7785b3581e 100644 --- a/net/dccp/ccids/lib/packet_history.c +++ b/net/dccp/ccids/lib/packet_history.c @@ -94,7 +94,6 @@ int tfrc_tx_hist_add(struct tfrc_tx_hist_entry **headp, u64 seqno) *headp = entry; return 0; } -EXPORT_SYMBOL_GPL(tfrc_tx_hist_add); void tfrc_tx_hist_purge(struct tfrc_tx_hist_entry **headp) { @@ -109,7 +108,6 @@ void tfrc_tx_hist_purge(struct tfrc_tx_hist_entry **headp) *headp = NULL; } -EXPORT_SYMBOL_GPL(tfrc_tx_hist_purge); u32 tfrc_tx_hist_rtt(struct tfrc_tx_hist_entry *head, const u64 seqno, const ktime_t now) @@ -127,7 +125,6 @@ u32 tfrc_tx_hist_rtt(struct tfrc_tx_hist_entry *head, const u64 seqno, return rtt; } -EXPORT_SYMBOL_GPL(tfrc_tx_hist_rtt); /* @@ -172,7 +169,6 @@ void tfrc_rx_hist_add_packet(struct tfrc_rx_hist *h, tfrc_rx_hist_entry_from_skb(entry, skb, ndp); } -EXPORT_SYMBOL_GPL(tfrc_rx_hist_add_packet); /* has the packet contained in skb been seen before? */ int tfrc_rx_hist_duplicate(struct tfrc_rx_hist *h, struct sk_buff *skb) @@ -189,7 +185,6 @@ int tfrc_rx_hist_duplicate(struct tfrc_rx_hist *h, struct sk_buff *skb) return 0; } -EXPORT_SYMBOL_GPL(tfrc_rx_hist_duplicate); static void tfrc_rx_hist_swap(struct tfrc_rx_hist *h, const u8 a, const u8 b) { @@ -390,7 +385,6 @@ int tfrc_rx_handle_loss(struct tfrc_rx_hist *h, } return is_new_loss; } -EXPORT_SYMBOL_GPL(tfrc_rx_handle_loss); int tfrc_rx_hist_alloc(struct tfrc_rx_hist *h) { @@ -412,7 +406,6 @@ out_free: } return -ENOBUFS; } -EXPORT_SYMBOL_GPL(tfrc_rx_hist_alloc); void tfrc_rx_hist_purge(struct tfrc_rx_hist *h) { @@ -424,7 +417,6 @@ void tfrc_rx_hist_purge(struct tfrc_rx_hist *h) h->ring[i] = NULL; } } -EXPORT_SYMBOL_GPL(tfrc_rx_hist_purge); /** * tfrc_rx_hist_rtt_last_s - reference entry to compute RTT samples against @@ -495,4 +487,3 @@ keep_ref_for_next_time: return sample; } -EXPORT_SYMBOL_GPL(tfrc_rx_hist_sample_rtt); diff --git a/net/dccp/ccids/lib/tfrc.c b/net/dccp/ccids/lib/tfrc.c index 185916218e0..60c412ccfee 100644 --- a/net/dccp/ccids/lib/tfrc.c +++ b/net/dccp/ccids/lib/tfrc.c @@ -1,20 +1,18 @@ /* - * TFRC: main module holding the pieces of the TFRC library together + * TFRC library initialisation * * Copyright (c) 2007 The University of Aberdeen, Scotland, UK * Copyright (c) 2007 Arnaldo Carvalho de Melo <acme@redhat.com> */ -#include <linux/module.h> -#include <linux/moduleparam.h> #include "tfrc.h" #ifdef CONFIG_IP_DCCP_TFRC_DEBUG int tfrc_debug; module_param(tfrc_debug, bool, 0644); -MODULE_PARM_DESC(tfrc_debug, "Enable debug messages"); +MODULE_PARM_DESC(tfrc_debug, "Enable TFRC debug messages"); #endif -static int __init tfrc_module_init(void) +int __init tfrc_lib_init(void) { int rc = tfrc_li_init(); @@ -38,18 +36,9 @@ out: return rc; } -static void __exit tfrc_module_exit(void) +void __exit tfrc_lib_exit(void) { tfrc_rx_packet_history_exit(); tfrc_tx_packet_history_exit(); tfrc_li_exit(); } - -module_init(tfrc_module_init); -module_exit(tfrc_module_exit); - -MODULE_AUTHOR("Gerrit Renker <gerrit@erg.abdn.ac.uk>, " - "Ian McDonald <ian.mcdonald@jandi.co.nz>, " - "Arnaldo Carvalho de Melo <acme@redhat.com>"); -MODULE_DESCRIPTION("DCCP TFRC library"); -MODULE_LICENSE("GPL"); diff --git a/net/dccp/ccids/lib/tfrc.h b/net/dccp/ccids/lib/tfrc.h index ed9857527ac..e9720b14327 100644 --- a/net/dccp/ccids/lib/tfrc.h +++ b/net/dccp/ccids/lib/tfrc.h @@ -17,7 +17,8 @@ #include <linux/types.h> #include <linux/math64.h> #include "../../dccp.h" -/* internal includes that this module exports: */ + +/* internal includes that this library exports: */ #include "loss_interval.h" #include "packet_history.h" @@ -66,4 +67,12 @@ extern void tfrc_rx_packet_history_exit(void); extern int tfrc_li_init(void); extern void tfrc_li_exit(void); + +#ifdef CONFIG_IP_DCCP_TFRC_LIB +extern int tfrc_lib_init(void); +extern void tfrc_lib_exit(void); +#else +#define tfrc_lib_init() (0) +#define tfrc_lib_exit() +#endif #endif /* _TFRC_H_ */ diff --git a/net/dccp/ccids/lib/tfrc_equation.c b/net/dccp/ccids/lib/tfrc_equation.c index 2f20a29cffe..c5d3a9e5a5a 100644 --- a/net/dccp/ccids/lib/tfrc_equation.c +++ b/net/dccp/ccids/lib/tfrc_equation.c @@ -659,8 +659,6 @@ u32 tfrc_calc_x(u16 s, u32 R, u32 p) return scaled_div32(result, f); } -EXPORT_SYMBOL_GPL(tfrc_calc_x); - /** * tfrc_calc_x_reverse_lookup - try to find p given f(p) * @@ -693,5 +691,3 @@ u32 tfrc_calc_x_reverse_lookup(u32 fvalue) index = tfrc_binsearch(fvalue, 0); return (index + 1) * 1000000 / TFRC_CALC_X_ARRSIZE; } - -EXPORT_SYMBOL_GPL(tfrc_calc_x_reverse_lookup); diff --git a/net/dccp/dccp.h b/net/dccp/dccp.h index 0bc4c9a02e1..f2230fc168e 100644 --- a/net/dccp/dccp.h +++ b/net/dccp/dccp.h @@ -432,10 +432,8 @@ static inline int dccp_ack_pending(const struct sock *sk) { const struct dccp_sock *dp = dccp_sk(sk); return dp->dccps_timestamp_echo != 0 || -#ifdef CONFIG_IP_DCCP_ACKVEC (dp->dccps_hc_rx_ackvec != NULL && dccp_ackvec_pending(dp->dccps_hc_rx_ackvec)) || -#endif inet_csk_ack_scheduled(sk); } diff --git a/net/dccp/feat.c b/net/dccp/feat.c index 30f9fb76b92..4152308958a 100644 --- a/net/dccp/feat.c +++ b/net/dccp/feat.c @@ -34,7 +34,7 @@ static int dccp_hdlr_ccid(struct sock *sk, u64 ccid, bool rx) { struct dccp_sock *dp = dccp_sk(sk); - struct ccid *new_ccid = ccid_new(ccid, sk, rx, gfp_any()); + struct ccid *new_ccid = ccid_new(ccid, sk, rx); if (new_ccid == NULL) return -ENOMEM; @@ -1214,8 +1214,6 @@ const char *dccp_feat_typename(const u8 type) return NULL; } -EXPORT_SYMBOL_GPL(dccp_feat_typename); - const char *dccp_feat_name(const u8 feat) { static const char *feature_names[] = { @@ -1240,6 +1238,4 @@ const char *dccp_feat_name(const u8 feat) return feature_names[feat]; } - -EXPORT_SYMBOL_GPL(dccp_feat_name); #endif /* CONFIG_IP_DCCP_DEBUG */ diff --git a/net/dccp/input.c b/net/dccp/input.c index 5eb443f656c..7648f316310 100644 --- a/net/dccp/input.c +++ b/net/dccp/input.c @@ -741,5 +741,3 @@ u32 dccp_sample_rtt(struct sock *sk, long delta) return delta; } - -EXPORT_SYMBOL_GPL(dccp_sample_rtt); diff --git a/net/dccp/proto.c b/net/dccp/proto.c index d5c2bacb713..945b4d5d23b 100644 --- a/net/dccp/proto.c +++ b/net/dccp/proto.c @@ -964,7 +964,6 @@ adjudge_to_death: state = sk->sk_state; sock_hold(sk); sock_orphan(sk); - percpu_counter_inc(sk->sk_prot->orphan_count); /* * It is the last release_sock in its life. It will remove backlog. @@ -978,6 +977,8 @@ adjudge_to_death: bh_lock_sock(sk); WARN_ON(sock_owned_by_user(sk)); + percpu_counter_inc(sk->sk_prot->orphan_count); + /* Have we already been destroyed by a softirq or backlog? */ if (state != DCCP_CLOSED && sk->sk_state == DCCP_CLOSED) goto out; @@ -1117,9 +1118,15 @@ static int __init dccp_init(void) if (rc) goto out_ackvec_exit; + rc = ccid_initialize_builtins(); + if (rc) + goto out_sysctl_exit; + dccp_timestamping_init(); out: return rc; +out_sysctl_exit: + dccp_sysctl_exit(); out_ackvec_exit: dccp_ackvec_exit(); out_free_dccp_mib: @@ -1142,6 +1149,7 @@ out_free_percpu: static void __exit dccp_fini(void) { + ccid_cleanup_builtins(); dccp_mib_exit(); free_pages((unsigned long)dccp_hashinfo.bhash, get_order(dccp_hashinfo.bhash_size * diff --git a/net/ipv4/cipso_ipv4.c b/net/ipv4/cipso_ipv4.c index e52799047a5..6bb2635b5de 100644 --- a/net/ipv4/cipso_ipv4.c +++ b/net/ipv4/cipso_ipv4.c @@ -38,6 +38,7 @@ #include <linux/spinlock.h> #include <linux/string.h> #include <linux/jhash.h> +#include <linux/audit.h> #include <net/ip.h> #include <net/icmp.h> #include <net/tcp.h> @@ -449,6 +450,7 @@ static struct cipso_v4_doi *cipso_v4_doi_search(u32 doi) /** * cipso_v4_doi_add - Add a new DOI to the CIPSO protocol engine * @doi_def: the DOI structure + * @audit_info: NetLabel audit information * * Description: * The caller defines a new DOI for use by the CIPSO engine and calls this @@ -458,50 +460,78 @@ static struct cipso_v4_doi *cipso_v4_doi_search(u32 doi) * zero on success and non-zero on failure. * */ -int cipso_v4_doi_add(struct cipso_v4_doi *doi_def) +int cipso_v4_doi_add(struct cipso_v4_doi *doi_def, + struct netlbl_audit *audit_info) { + int ret_val = -EINVAL; u32 iter; + u32 doi; + u32 doi_type; + struct audit_buffer *audit_buf; + + doi = doi_def->doi; + doi_type = doi_def->type; if (doi_def == NULL || doi_def->doi == CIPSO_V4_DOI_UNKNOWN) - return -EINVAL; + goto doi_add_return; for (iter = 0; iter < CIPSO_V4_TAG_MAXCNT; iter++) { switch (doi_def->tags[iter]) { case CIPSO_V4_TAG_RBITMAP: break; case CIPSO_V4_TAG_RANGE: - if (doi_def->type != CIPSO_V4_MAP_PASS) - return -EINVAL; - break; - case CIPSO_V4_TAG_INVALID: - if (iter == 0) - return -EINVAL; - break; case CIPSO_V4_TAG_ENUM: if (doi_def->type != CIPSO_V4_MAP_PASS) - return -EINVAL; + goto doi_add_return; break; case CIPSO_V4_TAG_LOCAL: if (doi_def->type != CIPSO_V4_MAP_LOCAL) - return -EINVAL; + goto doi_add_return; + break; + case CIPSO_V4_TAG_INVALID: + if (iter == 0) + goto doi_add_return; break; default: - return -EINVAL; + goto doi_add_return; } } atomic_set(&doi_def->refcount, 1); spin_lock(&cipso_v4_doi_list_lock); - if (cipso_v4_doi_search(doi_def->doi) != NULL) - goto doi_add_failure; + if (cipso_v4_doi_search(doi_def->doi) != NULL) { + spin_unlock(&cipso_v4_doi_list_lock); + ret_val = -EEXIST; + goto doi_add_return; + } list_add_tail_rcu(&doi_def->list, &cipso_v4_doi_list); spin_unlock(&cipso_v4_doi_list_lock); + ret_val = 0; - return 0; +doi_add_return: + audit_buf = netlbl_audit_start(AUDIT_MAC_CIPSOV4_ADD, audit_info); + if (audit_buf != NULL) { + const char *type_str; + switch (doi_type) { + case CIPSO_V4_MAP_TRANS: + type_str = "trans"; + break; + case CIPSO_V4_MAP_PASS: + type_str = "pass"; + break; + case CIPSO_V4_MAP_LOCAL: + type_str = "local"; + break; + default: + type_str = "(unknown)"; + } + audit_log_format(audit_buf, + " cipso_doi=%u cipso_type=%s res=%u", + doi, type_str, ret_val == 0 ? 1 : 0); + audit_log_end(audit_buf); + } -doi_add_failure: - spin_unlock(&cipso_v4_doi_list_lock); - return -EEXIST; + return ret_val; } /** @@ -559,25 +589,39 @@ static void cipso_v4_doi_free_rcu(struct rcu_head *entry) */ int cipso_v4_doi_remove(u32 doi, struct netlbl_audit *audit_info) { + int ret_val; struct cipso_v4_doi *doi_def; + struct audit_buffer *audit_buf; spin_lock(&cipso_v4_doi_list_lock); doi_def = cipso_v4_doi_search(doi); if (doi_def == NULL) { spin_unlock(&cipso_v4_doi_list_lock); - return -ENOENT; + ret_val = -ENOENT; + goto doi_remove_return; } if (!atomic_dec_and_test(&doi_def->refcount)) { spin_unlock(&cipso_v4_doi_list_lock); - return -EBUSY; + ret_val = -EBUSY; + goto doi_remove_return; } list_del_rcu(&doi_def->list); spin_unlock(&cipso_v4_doi_list_lock); cipso_v4_cache_invalidate(); call_rcu(&doi_def->rcu, cipso_v4_doi_free_rcu); + ret_val = 0; + +doi_remove_return: + audit_buf = netlbl_audit_start(AUDIT_MAC_CIPSOV4_DEL, audit_info); + if (audit_buf != NULL) { + audit_log_format(audit_buf, + " cipso_doi=%u res=%u", + doi, ret_val == 0 ? 1 : 0); + audit_log_end(audit_buf); + } - return 0; + return ret_val; } /** diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index c7cda1ca8e6..f26ab38680d 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -633,8 +633,6 @@ void inet_csk_listen_stop(struct sock *sk) acc_req = req->dl_next; - percpu_counter_inc(sk->sk_prot->orphan_count); - local_bh_disable(); bh_lock_sock(child); WARN_ON(sock_owned_by_user(child)); @@ -644,6 +642,8 @@ void inet_csk_listen_stop(struct sock *sk) sock_orphan(child); + percpu_counter_inc(sk->sk_prot->orphan_count); + inet_csk_destroy_sock(child); bh_unlock_sock(child); diff --git a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4_compat.c b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4_compat.c index 313ebf00ee3..6ba5c557690 100644 --- a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4_compat.c +++ b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4_compat.c @@ -291,7 +291,7 @@ static void *ct_cpu_seq_start(struct seq_file *seq, loff_t *pos) if (*pos == 0) return SEQ_START_TOKEN; - for (cpu = *pos-1; cpu < NR_CPUS; ++cpu) { + for (cpu = *pos-1; cpu < nr_cpu_ids; ++cpu) { if (!cpu_possible(cpu)) continue; *pos = cpu+1; @@ -306,7 +306,7 @@ static void *ct_cpu_seq_next(struct seq_file *seq, void *v, loff_t *pos) struct net *net = seq_file_net(seq); int cpu; - for (cpu = *pos; cpu < NR_CPUS; ++cpu) { + for (cpu = *pos; cpu < nr_cpu_ids; ++cpu) { if (!cpu_possible(cpu)) continue; *pos = cpu+1; diff --git a/net/ipv4/proc.c b/net/ipv4/proc.c index 614958b7c27..eb62e58bff7 100644 --- a/net/ipv4/proc.c +++ b/net/ipv4/proc.c @@ -38,6 +38,7 @@ #include <net/tcp.h> #include <net/udp.h> #include <net/udplite.h> +#include <linux/bottom_half.h> #include <linux/inetdevice.h> #include <linux/proc_fs.h> #include <linux/seq_file.h> @@ -50,13 +51,17 @@ static int sockstat_seq_show(struct seq_file *seq, void *v) { struct net *net = seq->private; + int orphans, sockets; + + local_bh_disable(); + orphans = percpu_counter_sum_positive(&tcp_orphan_count), + sockets = percpu_counter_sum_positive(&tcp_sockets_allocated), + local_bh_enable(); socket_seq_show(seq); seq_printf(seq, "TCP: inuse %d orphan %d tw %d alloc %d mem %d\n", - sock_prot_inuse_get(net, &tcp_prot), - (int)percpu_counter_sum_positive(&tcp_orphan_count), - tcp_death_row.tw_count, - (int)percpu_counter_sum_positive(&tcp_sockets_allocated), + sock_prot_inuse_get(net, &tcp_prot), orphans, + tcp_death_row.tw_count, sockets, atomic_read(&tcp_memory_allocated)); seq_printf(seq, "UDP: inuse %d mem %d\n", sock_prot_inuse_get(net, &udp_prot), diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 77bfba97595..97f71153584 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -429,7 +429,7 @@ static void *rt_cpu_seq_start(struct seq_file *seq, loff_t *pos) if (*pos == 0) return SEQ_START_TOKEN; - for (cpu = *pos-1; cpu < NR_CPUS; ++cpu) { + for (cpu = *pos-1; cpu < nr_cpu_ids; ++cpu) { if (!cpu_possible(cpu)) continue; *pos = cpu+1; @@ -442,7 +442,7 @@ static void *rt_cpu_seq_next(struct seq_file *seq, void *v, loff_t *pos) { int cpu; - for (cpu = *pos; cpu < NR_CPUS; ++cpu) { + for (cpu = *pos; cpu < nr_cpu_ids; ++cpu) { if (!cpu_possible(cpu)) continue; *pos = cpu+1; diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 1f3d52946b3..35bcddf8a93 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -580,10 +580,6 @@ ssize_t tcp_splice_read(struct socket *sock, loff_t *ppos, else if (!ret) { if (spliced) break; - if (flags & SPLICE_F_NONBLOCK) { - ret = -EAGAIN; - break; - } if (sock_flag(sk, SOCK_DONE)) break; if (sk->sk_err) { @@ -1836,7 +1832,6 @@ adjudge_to_death: state = sk->sk_state; sock_hold(sk); sock_orphan(sk); - percpu_counter_inc(sk->sk_prot->orphan_count); /* It is the last release_sock in its life. It will remove backlog. */ release_sock(sk); @@ -1849,6 +1844,8 @@ adjudge_to_death: bh_lock_sock(sk); WARN_ON(sock_owned_by_user(sk)); + percpu_counter_inc(sk->sk_prot->orphan_count); + /* Have we already been destroyed by a softirq or backlog? */ if (state != TCP_CLOSE && sk->sk_state == TCP_CLOSE) goto out; @@ -2518,9 +2515,7 @@ found: flush |= memcmp(th + 1, th2 + 1, thlen - sizeof(*th)); total = p->len; - mss = total; - if (skb_shinfo(p)->frag_list) - mss = skb_shinfo(p)->frag_list->len; + mss = skb_shinfo(p)->gso_size; flush |= skb->len > mss || skb->len <= 0; flush |= ntohl(th2->seq) + total != ntohl(th->seq); @@ -2556,7 +2551,6 @@ int tcp_gro_complete(struct sk_buff *skb) skb->csum_offset = offsetof(struct tcphdr, check); skb->ip_summed = CHECKSUM_PARTIAL; - skb_shinfo(skb)->gso_size = skb_shinfo(skb)->frag_list->len; skb_shinfo(skb)->gso_segs = NAPI_GRO_CB(skb)->count; if (th->cwr) diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 10172487921..9d839fa9331 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -51,6 +51,7 @@ */ +#include <linux/bottom_half.h> #include <linux/types.h> #include <linux/fcntl.h> #include <linux/module.h> @@ -1797,7 +1798,9 @@ static int tcp_v4_init_sock(struct sock *sk) sk->sk_sndbuf = sysctl_tcp_wmem[1]; sk->sk_rcvbuf = sysctl_tcp_rmem[1]; + local_bh_disable(); percpu_counter_inc(&tcp_sockets_allocated); + local_bh_enable(); return 0; } diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c index eeeaad2e8b5..40f324655e2 100644 --- a/net/ipv6/ipv6_sockglue.c +++ b/net/ipv6/ipv6_sockglue.c @@ -404,7 +404,7 @@ sticky_done: else if (optlen < sizeof(struct in6_pktinfo) || optval == NULL) goto e_inval; - if (copy_from_user(&pkt, optval, optlen)) { + if (copy_from_user(&pkt, optval, sizeof(struct in6_pktinfo))) { retv = -EFAULT; break; } diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 18c486cf498..76f06b94ab9 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -627,6 +627,9 @@ static struct rt6_info *rt6_alloc_cow(struct rt6_info *ort, struct in6_addr *dad rt = ip6_rt_copy(ort); if (rt) { + struct neighbour *neigh; + int attempts = !in_softirq(); + if (!(rt->rt6i_flags&RTF_GATEWAY)) { if (rt->rt6i_dst.plen != 128 && ipv6_addr_equal(&rt->rt6i_dst.addr, daddr)) @@ -646,7 +649,35 @@ static struct rt6_info *rt6_alloc_cow(struct rt6_info *ort, struct in6_addr *dad } #endif - rt->rt6i_nexthop = ndisc_get_neigh(rt->rt6i_dev, &rt->rt6i_gateway); + retry: + neigh = ndisc_get_neigh(rt->rt6i_dev, &rt->rt6i_gateway); + if (IS_ERR(neigh)) { + struct net *net = dev_net(rt->rt6i_dev); + int saved_rt_min_interval = + net->ipv6.sysctl.ip6_rt_gc_min_interval; + int saved_rt_elasticity = + net->ipv6.sysctl.ip6_rt_gc_elasticity; + + if (attempts-- > 0) { + net->ipv6.sysctl.ip6_rt_gc_elasticity = 1; + net->ipv6.sysctl.ip6_rt_gc_min_interval = 0; + + ip6_dst_gc(net->ipv6.ip6_dst_ops); + + net->ipv6.sysctl.ip6_rt_gc_elasticity = + saved_rt_elasticity; + net->ipv6.sysctl.ip6_rt_gc_min_interval = + saved_rt_min_interval; + goto retry; + } + + if (net_ratelimit()) + printk(KERN_WARNING + "Neighbour table overflow.\n"); + dst_free(&rt->u.dst); + return NULL; + } + rt->rt6i_nexthop = neigh; } @@ -945,8 +976,11 @@ struct dst_entry *icmp6_dst_alloc(struct net_device *dev, dev_hold(dev); if (neigh) neigh_hold(neigh); - else + else { neigh = ndisc_get_neigh(dev, addr); + if (IS_ERR(neigh)) + neigh = NULL; + } rt->rt6i_dev = dev; rt->rt6i_idev = idev; @@ -1887,6 +1921,7 @@ struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev, { struct net *net = dev_net(idev->dev); struct rt6_info *rt = ip6_dst_alloc(net->ipv6.ip6_dst_ops); + struct neighbour *neigh; if (rt == NULL) return ERR_PTR(-ENOMEM); @@ -1909,11 +1944,18 @@ struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev, rt->rt6i_flags |= RTF_ANYCAST; else rt->rt6i_flags |= RTF_LOCAL; - rt->rt6i_nexthop = ndisc_get_neigh(rt->rt6i_dev, &rt->rt6i_gateway); - if (rt->rt6i_nexthop == NULL) { + neigh = ndisc_get_neigh(rt->rt6i_dev, &rt->rt6i_gateway); + if (IS_ERR(neigh)) { dst_free(&rt->u.dst); - return ERR_PTR(-ENOMEM); + + /* We are casting this because that is the return + * value type. But an errno encoded pointer is the + * same regardless of the underlying pointer type, + * and that's what we are returning. So this is OK. + */ + return (struct rt6_info *) neigh; } + rt->rt6i_nexthop = neigh; ipv6_addr_copy(&rt->rt6i_dst.addr, addr); rt->rt6i_dst.plen = 128; diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 8702b06cb60..e8b8337a831 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -23,6 +23,7 @@ * 2 of the License, or (at your option) any later version. */ +#include <linux/bottom_half.h> #include <linux/module.h> #include <linux/errno.h> #include <linux/types.h> @@ -1830,7 +1831,9 @@ static int tcp_v6_init_sock(struct sock *sk) sk->sk_sndbuf = sysctl_tcp_wmem[1]; sk->sk_rcvbuf = sysctl_tcp_rmem[1]; + local_bh_disable(); percpu_counter_inc(&tcp_sockets_allocated); + local_bh_enable(); return 0; } diff --git a/net/irda/ircomm/ircomm_tty.c b/net/irda/ircomm/ircomm_tty.c index e4e2caeb9d8..086d5ef098f 100644 --- a/net/irda/ircomm/ircomm_tty.c +++ b/net/irda/ircomm/ircomm_tty.c @@ -371,9 +371,8 @@ static int ircomm_tty_open(struct tty_struct *tty, struct file *filp) IRDA_DEBUG(2, "%s()\n", __func__ ); line = tty->index; - if ((line < 0) || (line >= IRCOMM_TTY_PORTS)) { + if (line >= IRCOMM_TTY_PORTS) return -ENODEV; - } /* Check if instance already exists */ self = hashbin_lock_find(ircomm_tty, line, NULL); @@ -405,6 +404,8 @@ static int ircomm_tty_open(struct tty_struct *tty, struct file *filp) * Force TTY into raw mode by default which is usually what * we want for IrCOMM and IrLPT. This way applications will * not have to twiddle with printcap etc. + * + * Note this is completely usafe and doesn't work properly */ tty->termios->c_iflag = 0; tty->termios->c_oflag = 0; diff --git a/net/iucv/af_iucv.c b/net/iucv/af_iucv.c index af3192d2a5a..eb8a2a0b6eb 100644 --- a/net/iucv/af_iucv.c +++ b/net/iucv/af_iucv.c @@ -494,7 +494,21 @@ static int iucv_sock_connect(struct socket *sock, struct sockaddr *addr, if (err) { iucv_path_free(iucv->path); iucv->path = NULL; - err = -ECONNREFUSED; + switch (err) { + case 0x0b: /* Target communicator is not logged on */ + err = -ENETUNREACH; + break; + case 0x0d: /* Max connections for this guest exceeded */ + case 0x0e: /* Max connections for target guest exceeded */ + err = -EAGAIN; + break; + case 0x0f: /* Missing IUCV authorization */ + err = -EACCES; + break; + default: + err = -ECONNREFUSED; + break; + } goto done; } @@ -507,6 +521,13 @@ static int iucv_sock_connect(struct socket *sock, struct sockaddr *addr, release_sock(sk); return -ECONNREFUSED; } + + if (err) { + iucv_path_sever(iucv->path, NULL); + iucv_path_free(iucv->path); + iucv->path = NULL; + } + done: release_sock(sk); return err; @@ -1021,12 +1042,14 @@ static int iucv_callback_connreq(struct iucv_path *path, ASCEBC(user_data, sizeof(user_data)); if (sk->sk_state != IUCV_LISTEN) { err = iucv_path_sever(path, user_data); + iucv_path_free(path); goto fail; } /* Check for backlog size */ if (sk_acceptq_is_full(sk)) { err = iucv_path_sever(path, user_data); + iucv_path_free(path); goto fail; } @@ -1034,6 +1057,7 @@ static int iucv_callback_connreq(struct iucv_path *path, nsk = iucv_sock_alloc(NULL, SOCK_STREAM, GFP_ATOMIC); if (!nsk) { err = iucv_path_sever(path, user_data); + iucv_path_free(path); goto fail; } @@ -1057,6 +1081,8 @@ static int iucv_callback_connreq(struct iucv_path *path, err = iucv_path_accept(path, &af_iucv_handler, nuser_data, nsk); if (err) { err = iucv_path_sever(path, user_data); + iucv_path_free(path); + iucv_sock_kill(nsk); goto fail; } diff --git a/net/iucv/iucv.c b/net/iucv/iucv.c index 8f57d4f4328..a35240f61ec 100644 --- a/net/iucv/iucv.c +++ b/net/iucv/iucv.c @@ -50,7 +50,6 @@ #include <asm/ebcdic.h> #include <asm/io.h> #include <asm/s390_ext.h> -#include <asm/s390_rdev.h> #include <asm/smp.h> /* @@ -517,6 +516,7 @@ static int iucv_enable(void) size_t alloc_size; int cpu, rc; + get_online_cpus(); rc = -ENOMEM; alloc_size = iucv_max_pathid * sizeof(struct iucv_path); iucv_path_table = kzalloc(alloc_size, GFP_KERNEL); @@ -524,19 +524,17 @@ static int iucv_enable(void) goto out; /* Declare per cpu buffers. */ rc = -EIO; - get_online_cpus(); for_each_online_cpu(cpu) smp_call_function_single(cpu, iucv_declare_cpu, NULL, 1); if (cpus_empty(iucv_buffer_cpumask)) /* No cpu could declare an iucv buffer. */ - goto out_path; + goto out; put_online_cpus(); return 0; - -out_path: - put_online_cpus(); - kfree(iucv_path_table); out: + kfree(iucv_path_table); + iucv_path_table = NULL; + put_online_cpus(); return rc; } @@ -551,8 +549,9 @@ static void iucv_disable(void) { get_online_cpus(); on_each_cpu(iucv_retrieve_cpu, NULL, 1); - put_online_cpus(); kfree(iucv_path_table); + iucv_path_table = NULL; + put_online_cpus(); } static int __cpuinit iucv_cpu_notify(struct notifier_block *self, @@ -589,10 +588,14 @@ static int __cpuinit iucv_cpu_notify(struct notifier_block *self, case CPU_ONLINE_FROZEN: case CPU_DOWN_FAILED: case CPU_DOWN_FAILED_FROZEN: + if (!iucv_path_table) + break; smp_call_function_single(cpu, iucv_declare_cpu, NULL, 1); break; case CPU_DOWN_PREPARE: case CPU_DOWN_PREPARE_FROZEN: + if (!iucv_path_table) + break; cpumask = iucv_buffer_cpumask; cpu_clear(cpu, cpumask); if (cpus_empty(cpumask)) @@ -1692,7 +1695,7 @@ static int __init iucv_init(void) rc = register_external_interrupt(0x4000, iucv_external_interrupt); if (rc) goto out; - iucv_root = s390_root_dev_register("iucv"); + iucv_root = root_device_register("iucv"); if (IS_ERR(iucv_root)) { rc = PTR_ERR(iucv_root); goto out_int; @@ -1736,7 +1739,7 @@ out_free: kfree(iucv_irq_data[cpu]); iucv_irq_data[cpu] = NULL; } - s390_root_dev_unregister(iucv_root); + root_device_unregister(iucv_root); out_int: unregister_external_interrupt(0x4000, iucv_external_interrupt); out: @@ -1766,7 +1769,7 @@ static void __exit iucv_exit(void) kfree(iucv_irq_data[cpu]); iucv_irq_data[cpu] = NULL; } - s390_root_dev_unregister(iucv_root); + root_device_unregister(iucv_root); bus_unregister(&iucv_bus); unregister_external_interrupt(0x4000, iucv_external_interrupt); } diff --git a/net/netfilter/ipvs/ip_vs_lblc.c b/net/netfilter/ipvs/ip_vs_lblc.c index 9394f539966..3eb5e2660c4 100644 --- a/net/netfilter/ipvs/ip_vs_lblc.c +++ b/net/netfilter/ipvs/ip_vs_lblc.c @@ -507,7 +507,7 @@ ip_vs_lblc_schedule(struct ip_vs_service *svc, const struct sk_buff *skb) /* No cache entry or it is invalid, time to schedule */ dest = __ip_vs_lblc_schedule(svc); if (!dest) { - IP_VS_DBG(1, "no destination available\n"); + IP_VS_ERR_RL("LBLC: no destination available\n"); return NULL; } diff --git a/net/netfilter/ipvs/ip_vs_lblcr.c b/net/netfilter/ipvs/ip_vs_lblcr.c index 92dc76a6842..c04ce56c7f0 100644 --- a/net/netfilter/ipvs/ip_vs_lblcr.c +++ b/net/netfilter/ipvs/ip_vs_lblcr.c @@ -690,7 +690,7 @@ ip_vs_lblcr_schedule(struct ip_vs_service *svc, const struct sk_buff *skb) /* The cache entry is invalid, time to schedule */ dest = __ip_vs_lblcr_schedule(svc); if (!dest) { - IP_VS_DBG(1, "no destination available\n"); + IP_VS_ERR_RL("LBLCR: no destination available\n"); read_unlock(&svc->sched_lock); return NULL; } diff --git a/net/netfilter/ipvs/ip_vs_lc.c b/net/netfilter/ipvs/ip_vs_lc.c index 51912cab777..d0dadc8a65f 100644 --- a/net/netfilter/ipvs/ip_vs_lc.c +++ b/net/netfilter/ipvs/ip_vs_lc.c @@ -66,11 +66,15 @@ ip_vs_lc_schedule(struct ip_vs_service *svc, const struct sk_buff *skb) } } - if (least) - IP_VS_DBG_BUF(6, "LC: server %s:%u activeconns %d inactconns %d\n", - IP_VS_DBG_ADDR(svc->af, &least->addr), ntohs(least->port), - atomic_read(&least->activeconns), - atomic_read(&least->inactconns)); + if (!least) + IP_VS_ERR_RL("LC: no destination available\n"); + else + IP_VS_DBG_BUF(6, "LC: server %s:%u activeconns %d " + "inactconns %d\n", + IP_VS_DBG_ADDR(svc->af, &least->addr), + ntohs(least->port), + atomic_read(&least->activeconns), + atomic_read(&least->inactconns)); return least; } diff --git a/net/netfilter/ipvs/ip_vs_nq.c b/net/netfilter/ipvs/ip_vs_nq.c index 6758ad2ceaa..694952db502 100644 --- a/net/netfilter/ipvs/ip_vs_nq.c +++ b/net/netfilter/ipvs/ip_vs_nq.c @@ -95,8 +95,10 @@ ip_vs_nq_schedule(struct ip_vs_service *svc, const struct sk_buff *skb) } } - if (!least) + if (!least) { + IP_VS_ERR_RL("NQ: no destination available\n"); return NULL; + } out: IP_VS_DBG_BUF(6, "NQ: server %s:%u " diff --git a/net/netfilter/ipvs/ip_vs_rr.c b/net/netfilter/ipvs/ip_vs_rr.c index 8fb51c169eb..2d16ab7f8c1 100644 --- a/net/netfilter/ipvs/ip_vs_rr.c +++ b/net/netfilter/ipvs/ip_vs_rr.c @@ -69,6 +69,7 @@ ip_vs_rr_schedule(struct ip_vs_service *svc, const struct sk_buff *skb) q = q->next; } while (q != p); write_unlock(&svc->sched_lock); + IP_VS_ERR_RL("RR: no destination available\n"); return NULL; out: diff --git a/net/netfilter/ipvs/ip_vs_sed.c b/net/netfilter/ipvs/ip_vs_sed.c index 691a6a0086e..20e4657d2f3 100644 --- a/net/netfilter/ipvs/ip_vs_sed.c +++ b/net/netfilter/ipvs/ip_vs_sed.c @@ -84,6 +84,7 @@ ip_vs_sed_schedule(struct ip_vs_service *svc, const struct sk_buff *skb) goto nextstage; } } + IP_VS_ERR_RL("SED: no destination available\n"); return NULL; /* diff --git a/net/netfilter/ipvs/ip_vs_sh.c b/net/netfilter/ipvs/ip_vs_sh.c index 0e53955ef13..75709ebeb63 100644 --- a/net/netfilter/ipvs/ip_vs_sh.c +++ b/net/netfilter/ipvs/ip_vs_sh.c @@ -219,6 +219,7 @@ ip_vs_sh_schedule(struct ip_vs_service *svc, const struct sk_buff *skb) || !(dest->flags & IP_VS_DEST_F_AVAILABLE) || atomic_read(&dest->weight) <= 0 || is_overloaded(dest)) { + IP_VS_ERR_RL("SH: no destination available\n"); return NULL; } diff --git a/net/netfilter/ipvs/ip_vs_wlc.c b/net/netfilter/ipvs/ip_vs_wlc.c index 57b452bbb4e..8e942565b47 100644 --- a/net/netfilter/ipvs/ip_vs_wlc.c +++ b/net/netfilter/ipvs/ip_vs_wlc.c @@ -72,6 +72,7 @@ ip_vs_wlc_schedule(struct ip_vs_service *svc, const struct sk_buff *skb) goto nextstage; } } + IP_VS_ERR_RL("WLC: no destination available\n"); return NULL; /* diff --git a/net/netfilter/ipvs/ip_vs_wrr.c b/net/netfilter/ipvs/ip_vs_wrr.c index 2f618dc29c5..f7d74ef1ecf 100644 --- a/net/netfilter/ipvs/ip_vs_wrr.c +++ b/net/netfilter/ipvs/ip_vs_wrr.c @@ -155,6 +155,8 @@ ip_vs_wrr_schedule(struct ip_vs_service *svc, const struct sk_buff *skb) if (mark->cl == mark->cl->next) { /* no dest entry */ + IP_VS_ERR_RL("WRR: no destination available: " + "no destinations present\n"); dest = NULL; goto out; } @@ -168,8 +170,8 @@ ip_vs_wrr_schedule(struct ip_vs_service *svc, const struct sk_buff *skb) */ if (mark->cw == 0) { mark->cl = &svc->destinations; - IP_VS_ERR_RL("ip_vs_wrr_schedule(): " - "no available servers\n"); + IP_VS_ERR_RL("WRR: no destination " + "available\n"); dest = NULL; goto out; } @@ -191,6 +193,8 @@ ip_vs_wrr_schedule(struct ip_vs_service *svc, const struct sk_buff *skb) /* back to the start, and no dest is found. It is only possible when all dests are OVERLOADED */ dest = NULL; + IP_VS_ERR_RL("WRR: no destination available: " + "all destinations are overloaded\n"); goto out; } } diff --git a/net/netfilter/nf_conntrack_standalone.c b/net/netfilter/nf_conntrack_standalone.c index f37b9b74c6a..4da54b0b923 100644 --- a/net/netfilter/nf_conntrack_standalone.c +++ b/net/netfilter/nf_conntrack_standalone.c @@ -200,7 +200,7 @@ static void *ct_cpu_seq_start(struct seq_file *seq, loff_t *pos) if (*pos == 0) return SEQ_START_TOKEN; - for (cpu = *pos-1; cpu < NR_CPUS; ++cpu) { + for (cpu = *pos-1; cpu < nr_cpu_ids; ++cpu) { if (!cpu_possible(cpu)) continue; *pos = cpu + 1; @@ -215,7 +215,7 @@ static void *ct_cpu_seq_next(struct seq_file *seq, void *v, loff_t *pos) struct net *net = seq_file_net(seq); int cpu; - for (cpu = *pos; cpu < NR_CPUS; ++cpu) { + for (cpu = *pos; cpu < nr_cpu_ids; ++cpu) { if (!cpu_possible(cpu)) continue; *pos = cpu + 1; diff --git a/net/netlabel/netlabel_cipso_v4.c b/net/netlabel/netlabel_cipso_v4.c index fff32b70efa..bf1ab1a6790 100644 --- a/net/netlabel/netlabel_cipso_v4.c +++ b/net/netlabel/netlabel_cipso_v4.c @@ -130,6 +130,7 @@ static int netlbl_cipsov4_add_common(struct genl_info *info, /** * netlbl_cipsov4_add_std - Adds a CIPSO V4 DOI definition * @info: the Generic NETLINK info block + * @audit_info: NetLabel audit information * * Description: * Create a new CIPSO_V4_MAP_TRANS DOI definition based on the given ADD @@ -137,7 +138,8 @@ static int netlbl_cipsov4_add_common(struct genl_info *info, * non-zero on error. * */ -static int netlbl_cipsov4_add_std(struct genl_info *info) +static int netlbl_cipsov4_add_std(struct genl_info *info, + struct netlbl_audit *audit_info) { int ret_val = -EINVAL; struct cipso_v4_doi *doi_def = NULL; @@ -316,7 +318,7 @@ static int netlbl_cipsov4_add_std(struct genl_info *info) } } - ret_val = cipso_v4_doi_add(doi_def); + ret_val = cipso_v4_doi_add(doi_def, audit_info); if (ret_val != 0) goto add_std_failure; return 0; @@ -330,6 +332,7 @@ add_std_failure: /** * netlbl_cipsov4_add_pass - Adds a CIPSO V4 DOI definition * @info: the Generic NETLINK info block + * @audit_info: NetLabel audit information * * Description: * Create a new CIPSO_V4_MAP_PASS DOI definition based on the given ADD message @@ -337,7 +340,8 @@ add_std_failure: * error. * */ -static int netlbl_cipsov4_add_pass(struct genl_info *info) +static int netlbl_cipsov4_add_pass(struct genl_info *info, + struct netlbl_audit *audit_info) { int ret_val; struct cipso_v4_doi *doi_def = NULL; @@ -354,7 +358,7 @@ static int netlbl_cipsov4_add_pass(struct genl_info *info) if (ret_val != 0) goto add_pass_failure; - ret_val = cipso_v4_doi_add(doi_def); + ret_val = cipso_v4_doi_add(doi_def, audit_info); if (ret_val != 0) goto add_pass_failure; return 0; @@ -367,6 +371,7 @@ add_pass_failure: /** * netlbl_cipsov4_add_local - Adds a CIPSO V4 DOI definition * @info: the Generic NETLINK info block + * @audit_info: NetLabel audit information * * Description: * Create a new CIPSO_V4_MAP_LOCAL DOI definition based on the given ADD @@ -374,7 +379,8 @@ add_pass_failure: * non-zero on error. * */ -static int netlbl_cipsov4_add_local(struct genl_info *info) +static int netlbl_cipsov4_add_local(struct genl_info *info, + struct netlbl_audit *audit_info) { int ret_val; struct cipso_v4_doi *doi_def = NULL; @@ -391,7 +397,7 @@ static int netlbl_cipsov4_add_local(struct genl_info *info) if (ret_val != 0) goto add_local_failure; - ret_val = cipso_v4_doi_add(doi_def); + ret_val = cipso_v4_doi_add(doi_def, audit_info); if (ret_val != 0) goto add_local_failure; return 0; @@ -415,48 +421,31 @@ static int netlbl_cipsov4_add(struct sk_buff *skb, struct genl_info *info) { int ret_val = -EINVAL; - u32 type; - u32 doi; const char *type_str = "(unknown)"; - struct audit_buffer *audit_buf; struct netlbl_audit audit_info; if (!info->attrs[NLBL_CIPSOV4_A_DOI] || !info->attrs[NLBL_CIPSOV4_A_MTYPE]) return -EINVAL; - doi = nla_get_u32(info->attrs[NLBL_CIPSOV4_A_DOI]); netlbl_netlink_auditinfo(skb, &audit_info); - - type = nla_get_u32(info->attrs[NLBL_CIPSOV4_A_MTYPE]); - switch (type) { + switch (nla_get_u32(info->attrs[NLBL_CIPSOV4_A_MTYPE])) { case CIPSO_V4_MAP_TRANS: type_str = "trans"; - ret_val = netlbl_cipsov4_add_std(info); + ret_val = netlbl_cipsov4_add_std(info, &audit_info); break; case CIPSO_V4_MAP_PASS: type_str = "pass"; - ret_val = netlbl_cipsov4_add_pass(info); + ret_val = netlbl_cipsov4_add_pass(info, &audit_info); break; case CIPSO_V4_MAP_LOCAL: type_str = "local"; - ret_val = netlbl_cipsov4_add_local(info); + ret_val = netlbl_cipsov4_add_local(info, &audit_info); break; } if (ret_val == 0) atomic_inc(&netlabel_mgmt_protocount); - audit_buf = netlbl_audit_start_common(AUDIT_MAC_CIPSOV4_ADD, - &audit_info); - if (audit_buf != NULL) { - audit_log_format(audit_buf, - " cipso_doi=%u cipso_type=%s res=%u", - doi, - type_str, - ret_val == 0 ? 1 : 0); - audit_log_end(audit_buf); - } - return ret_val; } @@ -725,9 +714,7 @@ static int netlbl_cipsov4_remove_cb(struct netlbl_dom_map *entry, void *arg) static int netlbl_cipsov4_remove(struct sk_buff *skb, struct genl_info *info) { int ret_val = -EINVAL; - u32 doi = 0; struct netlbl_domhsh_walk_arg cb_arg; - struct audit_buffer *audit_buf; struct netlbl_audit audit_info; u32 skip_bkt = 0; u32 skip_chain = 0; @@ -735,29 +722,17 @@ static int netlbl_cipsov4_remove(struct sk_buff *skb, struct genl_info *info) if (!info->attrs[NLBL_CIPSOV4_A_DOI]) return -EINVAL; - doi = nla_get_u32(info->attrs[NLBL_CIPSOV4_A_DOI]); netlbl_netlink_auditinfo(skb, &audit_info); - - cb_arg.doi = doi; + cb_arg.doi = nla_get_u32(info->attrs[NLBL_CIPSOV4_A_DOI]); cb_arg.audit_info = &audit_info; ret_val = netlbl_domhsh_walk(&skip_bkt, &skip_chain, netlbl_cipsov4_remove_cb, &cb_arg); if (ret_val == 0 || ret_val == -ENOENT) { - ret_val = cipso_v4_doi_remove(doi, &audit_info); + ret_val = cipso_v4_doi_remove(cb_arg.doi, &audit_info); if (ret_val == 0) atomic_dec(&netlabel_mgmt_protocount); } - audit_buf = netlbl_audit_start_common(AUDIT_MAC_CIPSOV4_DEL, - &audit_info); - if (audit_buf != NULL) { - audit_log_format(audit_buf, - " cipso_doi=%u res=%u", - doi, - ret_val == 0 ? 1 : 0); - audit_log_end(audit_buf); - } - return ret_val; } diff --git a/net/netlabel/netlabel_domainhash.c b/net/netlabel/netlabel_domainhash.c index 5fadf10e5dd..7a10bbe02c1 100644 --- a/net/netlabel/netlabel_domainhash.c +++ b/net/netlabel/netlabel_domainhash.c @@ -483,6 +483,73 @@ int netlbl_domhsh_remove_entry(struct netlbl_dom_map *entry, } /** + * netlbl_domhsh_remove_af4 - Removes an address selector entry + * @domain: the domain + * @addr: IPv4 address + * @mask: IPv4 address mask + * @audit_info: NetLabel audit information + * + * Description: + * Removes an individual address selector from a domain mapping and potentially + * the entire mapping if it is empty. Returns zero on success, negative values + * on failure. + * + */ +int netlbl_domhsh_remove_af4(const char *domain, + const struct in_addr *addr, + const struct in_addr *mask, + struct netlbl_audit *audit_info) +{ + struct netlbl_dom_map *entry_map; + struct netlbl_af4list *entry_addr; + struct netlbl_af4list *iter4; +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) + struct netlbl_af6list *iter6; +#endif /* IPv6 */ + struct netlbl_domaddr4_map *entry; + + rcu_read_lock(); + + if (domain) + entry_map = netlbl_domhsh_search(domain); + else + entry_map = netlbl_domhsh_search_def(domain); + if (entry_map == NULL || entry_map->type != NETLBL_NLTYPE_ADDRSELECT) + goto remove_af4_failure; + + spin_lock(&netlbl_domhsh_lock); + entry_addr = netlbl_af4list_remove(addr->s_addr, mask->s_addr, + &entry_map->type_def.addrsel->list4); + spin_unlock(&netlbl_domhsh_lock); + + if (entry_addr == NULL) + goto remove_af4_failure; + netlbl_af4list_foreach_rcu(iter4, &entry_map->type_def.addrsel->list4) + goto remove_af4_single_addr; +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) + netlbl_af6list_foreach_rcu(iter6, &entry_map->type_def.addrsel->list6) + goto remove_af4_single_addr; +#endif /* IPv6 */ + /* the domain mapping is empty so remove it from the mapping table */ + netlbl_domhsh_remove_entry(entry_map, audit_info); + +remove_af4_single_addr: + rcu_read_unlock(); + /* yick, we can't use call_rcu here because we don't have a rcu head + * pointer but hopefully this should be a rare case so the pause + * shouldn't be a problem */ + synchronize_rcu(); + entry = netlbl_domhsh_addr4_entry(entry_addr); + cipso_v4_doi_putdef(entry->type_def.cipsov4); + kfree(entry); + return 0; + +remove_af4_failure: + rcu_read_unlock(); + return -ENOENT; +} + +/** * netlbl_domhsh_remove - Removes an entry from the domain hash table * @domain: the domain to remove * @audit_info: NetLabel audit information diff --git a/net/netlabel/netlabel_domainhash.h b/net/netlabel/netlabel_domainhash.h index bfcb6763a1a..0261dda3f2d 100644 --- a/net/netlabel/netlabel_domainhash.h +++ b/net/netlabel/netlabel_domainhash.h @@ -90,6 +90,10 @@ int netlbl_domhsh_add_default(struct netlbl_dom_map *entry, struct netlbl_audit *audit_info); int netlbl_domhsh_remove_entry(struct netlbl_dom_map *entry, struct netlbl_audit *audit_info); +int netlbl_domhsh_remove_af4(const char *domain, + const struct in_addr *addr, + const struct in_addr *mask, + struct netlbl_audit *audit_info); int netlbl_domhsh_remove(const char *domain, struct netlbl_audit *audit_info); int netlbl_domhsh_remove_default(struct netlbl_audit *audit_info); struct netlbl_dom_map *netlbl_domhsh_getentry(const char *domain); diff --git a/net/netlabel/netlabel_kapi.c b/net/netlabel/netlabel_kapi.c index b32eceb3ab0..fd9229db075 100644 --- a/net/netlabel/netlabel_kapi.c +++ b/net/netlabel/netlabel_kapi.c @@ -31,7 +31,10 @@ #include <linux/init.h> #include <linux/types.h> #include <linux/audit.h> +#include <linux/in.h> +#include <linux/in6.h> #include <net/ip.h> +#include <net/ipv6.h> #include <net/netlabel.h> #include <net/cipso_ipv4.h> #include <asm/bug.h> @@ -42,6 +45,7 @@ #include "netlabel_cipso_v4.h" #include "netlabel_user.h" #include "netlabel_mgmt.h" +#include "netlabel_addrlist.h" /* * Configuration Functions @@ -50,6 +54,9 @@ /** * netlbl_cfg_map_del - Remove a NetLabel/LSM domain mapping * @domain: the domain mapping to remove + * @family: address family + * @addr: IP address + * @mask: IP address mask * @audit_info: NetLabel audit information * * Description: @@ -58,14 +65,32 @@ * values on failure. * */ -int netlbl_cfg_map_del(const char *domain, struct netlbl_audit *audit_info) +int netlbl_cfg_map_del(const char *domain, + u16 family, + const void *addr, + const void *mask, + struct netlbl_audit *audit_info) { - return netlbl_domhsh_remove(domain, audit_info); + if (addr == NULL && mask == NULL) { + return netlbl_domhsh_remove(domain, audit_info); + } else if (addr != NULL && mask != NULL) { + switch (family) { + case AF_INET: + return netlbl_domhsh_remove_af4(domain, addr, mask, + audit_info); + default: + return -EPFNOSUPPORT; + } + } else + return -EINVAL; } /** - * netlbl_cfg_unlbl_add_map - Add an unlabeled NetLabel/LSM domain mapping + * netlbl_cfg_unlbl_map_add - Add a new unlabeled mapping * @domain: the domain mapping to add + * @family: address family + * @addr: IP address + * @mask: IP address mask * @audit_info: NetLabel audit information * * Description: @@ -74,11 +99,19 @@ int netlbl_cfg_map_del(const char *domain, struct netlbl_audit *audit_info) * negative values on failure. * */ -int netlbl_cfg_unlbl_add_map(const char *domain, +int netlbl_cfg_unlbl_map_add(const char *domain, + u16 family, + const void *addr, + const void *mask, struct netlbl_audit *audit_info) { int ret_val = -ENOMEM; struct netlbl_dom_map *entry; + struct netlbl_domaddr_map *addrmap = NULL; + struct netlbl_domaddr4_map *map4 = NULL; + struct netlbl_domaddr6_map *map6 = NULL; + const struct in_addr *addr4, *mask4; + const struct in6_addr *addr6, *mask6; entry = kzalloc(sizeof(*entry), GFP_ATOMIC); if (entry == NULL) @@ -86,49 +119,225 @@ int netlbl_cfg_unlbl_add_map(const char *domain, if (domain != NULL) { entry->domain = kstrdup(domain, GFP_ATOMIC); if (entry->domain == NULL) - goto cfg_unlbl_add_map_failure; + goto cfg_unlbl_map_add_failure; + } + + if (addr == NULL && mask == NULL) + entry->type = NETLBL_NLTYPE_UNLABELED; + else if (addr != NULL && mask != NULL) { + addrmap = kzalloc(sizeof(*addrmap), GFP_ATOMIC); + if (addrmap == NULL) + goto cfg_unlbl_map_add_failure; + INIT_LIST_HEAD(&addrmap->list4); + INIT_LIST_HEAD(&addrmap->list6); + + switch (family) { + case AF_INET: + addr4 = addr; + mask4 = mask; + map4 = kzalloc(sizeof(*map4), GFP_ATOMIC); + if (map4 == NULL) + goto cfg_unlbl_map_add_failure; + map4->type = NETLBL_NLTYPE_UNLABELED; + map4->list.addr = addr4->s_addr & mask4->s_addr; + map4->list.mask = mask4->s_addr; + map4->list.valid = 1; + ret_val = netlbl_af4list_add(&map4->list, + &addrmap->list4); + if (ret_val != 0) + goto cfg_unlbl_map_add_failure; + break; + case AF_INET6: + addr6 = addr; + mask6 = mask; + map6 = kzalloc(sizeof(*map6), GFP_ATOMIC); + if (map4 == NULL) + goto cfg_unlbl_map_add_failure; + map6->type = NETLBL_NLTYPE_UNLABELED; + ipv6_addr_copy(&map6->list.addr, addr6); + map6->list.addr.s6_addr32[0] &= mask6->s6_addr32[0]; + map6->list.addr.s6_addr32[1] &= mask6->s6_addr32[1]; + map6->list.addr.s6_addr32[2] &= mask6->s6_addr32[2]; + map6->list.addr.s6_addr32[3] &= mask6->s6_addr32[3]; + ipv6_addr_copy(&map6->list.mask, mask6); + map6->list.valid = 1; + ret_val = netlbl_af4list_add(&map4->list, + &addrmap->list4); + if (ret_val != 0) + goto cfg_unlbl_map_add_failure; + break; + default: + goto cfg_unlbl_map_add_failure; + break; + } + + entry->type_def.addrsel = addrmap; + entry->type = NETLBL_NLTYPE_ADDRSELECT; + } else { + ret_val = -EINVAL; + goto cfg_unlbl_map_add_failure; } - entry->type = NETLBL_NLTYPE_UNLABELED; ret_val = netlbl_domhsh_add(entry, audit_info); if (ret_val != 0) - goto cfg_unlbl_add_map_failure; + goto cfg_unlbl_map_add_failure; return 0; -cfg_unlbl_add_map_failure: +cfg_unlbl_map_add_failure: if (entry != NULL) kfree(entry->domain); kfree(entry); + kfree(addrmap); + kfree(map4); + kfree(map6); return ret_val; } + +/** + * netlbl_cfg_unlbl_static_add - Adds a new static label + * @net: network namespace + * @dev_name: interface name + * @addr: IP address in network byte order (struct in[6]_addr) + * @mask: address mask in network byte order (struct in[6]_addr) + * @family: address family + * @secid: LSM secid value for the entry + * @audit_info: NetLabel audit information + * + * Description: + * Adds a new NetLabel static label to be used when protocol provided labels + * are not present on incoming traffic. If @dev_name is NULL then the default + * interface will be used. Returns zero on success, negative values on failure. + * + */ +int netlbl_cfg_unlbl_static_add(struct net *net, + const char *dev_name, + const void *addr, + const void *mask, + u16 family, + u32 secid, + struct netlbl_audit *audit_info) +{ + u32 addr_len; + + switch (family) { + case AF_INET: + addr_len = sizeof(struct in_addr); + break; + case AF_INET6: + addr_len = sizeof(struct in6_addr); + break; + default: + return -EPFNOSUPPORT; + } + + return netlbl_unlhsh_add(net, + dev_name, addr, mask, addr_len, + secid, audit_info); +} + +/** + * netlbl_cfg_unlbl_static_del - Removes an existing static label + * @net: network namespace + * @dev_name: interface name + * @addr: IP address in network byte order (struct in[6]_addr) + * @mask: address mask in network byte order (struct in[6]_addr) + * @family: address family + * @secid: LSM secid value for the entry + * @audit_info: NetLabel audit information + * + * Description: + * Removes an existing NetLabel static label used when protocol provided labels + * are not present on incoming traffic. If @dev_name is NULL then the default + * interface will be used. Returns zero on success, negative values on failure. + * + */ +int netlbl_cfg_unlbl_static_del(struct net *net, + const char *dev_name, + const void *addr, + const void *mask, + u16 family, + struct netlbl_audit *audit_info) +{ + u32 addr_len; + + switch (family) { + case AF_INET: + addr_len = sizeof(struct in_addr); + break; + case AF_INET6: + addr_len = sizeof(struct in6_addr); + break; + default: + return -EPFNOSUPPORT; + } + + return netlbl_unlhsh_remove(net, + dev_name, addr, mask, addr_len, + audit_info); +} + +/** + * netlbl_cfg_cipsov4_add - Add a new CIPSOv4 DOI definition + * @doi_def: CIPSO DOI definition + * @audit_info: NetLabel audit information + * + * Description: + * Add a new CIPSO DOI definition as defined by @doi_def. Returns zero on + * success and negative values on failure. + * + */ +int netlbl_cfg_cipsov4_add(struct cipso_v4_doi *doi_def, + struct netlbl_audit *audit_info) +{ + return cipso_v4_doi_add(doi_def, audit_info); +} + +/** + * netlbl_cfg_cipsov4_del - Remove an existing CIPSOv4 DOI definition + * @doi: CIPSO DOI + * @audit_info: NetLabel audit information + * + * Description: + * Remove an existing CIPSO DOI definition matching @doi. Returns zero on + * success and negative values on failure. + * + */ +void netlbl_cfg_cipsov4_del(u32 doi, struct netlbl_audit *audit_info) +{ + cipso_v4_doi_remove(doi, audit_info); +} + /** - * netlbl_cfg_cipsov4_add_map - Add a new CIPSOv4 DOI definition and mapping - * @doi_def: the DOI definition + * netlbl_cfg_cipsov4_map_add - Add a new CIPSOv4 DOI mapping + * @doi: the CIPSO DOI * @domain: the domain mapping to add + * @addr: IP address + * @mask: IP address mask * @audit_info: NetLabel audit information * * Description: - * Add a new CIPSOv4 DOI definition and NetLabel/LSM domain mapping for this - * new DOI definition to the NetLabel subsystem. A @domain value of NULL adds - * a new default domain mapping. Returns zero on success, negative values on - * failure. + * Add a new NetLabel/LSM domain mapping for the given CIPSO DOI to the NetLabel + * subsystem. A @domain value of NULL adds a new default domain mapping. + * Returns zero on success, negative values on failure. * */ -int netlbl_cfg_cipsov4_add_map(struct cipso_v4_doi *doi_def, +int netlbl_cfg_cipsov4_map_add(u32 doi, const char *domain, + const struct in_addr *addr, + const struct in_addr *mask, struct netlbl_audit *audit_info) { int ret_val = -ENOMEM; - u32 doi; - u32 doi_type; + struct cipso_v4_doi *doi_def; struct netlbl_dom_map *entry; - const char *type_str; - struct audit_buffer *audit_buf; + struct netlbl_domaddr_map *addrmap = NULL; + struct netlbl_domaddr4_map *addrinfo = NULL; - doi = doi_def->doi; - doi_type = doi_def->type; + doi_def = cipso_v4_doi_getdef(doi); + if (doi_def == NULL) + return -ENOENT; entry = kzalloc(sizeof(*entry), GFP_ATOMIC); if (entry == NULL) @@ -136,56 +345,52 @@ int netlbl_cfg_cipsov4_add_map(struct cipso_v4_doi *doi_def, if (domain != NULL) { entry->domain = kstrdup(domain, GFP_ATOMIC); if (entry->domain == NULL) - goto cfg_cipsov4_add_map_failure; + goto cfg_cipsov4_map_add_failure; } - ret_val = cipso_v4_doi_add(doi_def); - if (ret_val != 0) - goto cfg_cipsov4_add_map_failure_remove_doi; - entry->type = NETLBL_NLTYPE_CIPSOV4; - entry->type_def.cipsov4 = cipso_v4_doi_getdef(doi); - if (entry->type_def.cipsov4 == NULL) { - ret_val = -ENOENT; - goto cfg_cipsov4_add_map_failure_remove_doi; + if (addr == NULL && mask == NULL) { + entry->type_def.cipsov4 = doi_def; + entry->type = NETLBL_NLTYPE_CIPSOV4; + } else if (addr != NULL && mask != NULL) { + addrmap = kzalloc(sizeof(*addrmap), GFP_ATOMIC); + if (addrmap == NULL) + goto cfg_cipsov4_map_add_failure; + INIT_LIST_HEAD(&addrmap->list4); + INIT_LIST_HEAD(&addrmap->list6); + + addrinfo = kzalloc(sizeof(*addrinfo), GFP_ATOMIC); + if (addrinfo == NULL) + goto cfg_cipsov4_map_add_failure; + addrinfo->type_def.cipsov4 = doi_def; + addrinfo->type = NETLBL_NLTYPE_CIPSOV4; + addrinfo->list.addr = addr->s_addr & mask->s_addr; + addrinfo->list.mask = mask->s_addr; + addrinfo->list.valid = 1; + ret_val = netlbl_af4list_add(&addrinfo->list, &addrmap->list4); + if (ret_val != 0) + goto cfg_cipsov4_map_add_failure; + + entry->type_def.addrsel = addrmap; + entry->type = NETLBL_NLTYPE_ADDRSELECT; + } else { + ret_val = -EINVAL; + goto cfg_cipsov4_map_add_failure; } + ret_val = netlbl_domhsh_add(entry, audit_info); if (ret_val != 0) - goto cfg_cipsov4_add_map_failure_release_doi; - -cfg_cipsov4_add_map_return: - audit_buf = netlbl_audit_start_common(AUDIT_MAC_CIPSOV4_ADD, - audit_info); - if (audit_buf != NULL) { - switch (doi_type) { - case CIPSO_V4_MAP_TRANS: - type_str = "trans"; - break; - case CIPSO_V4_MAP_PASS: - type_str = "pass"; - break; - case CIPSO_V4_MAP_LOCAL: - type_str = "local"; - break; - default: - type_str = "(unknown)"; - } - audit_log_format(audit_buf, - " cipso_doi=%u cipso_type=%s res=%u", - doi, type_str, ret_val == 0 ? 1 : 0); - audit_log_end(audit_buf); - } + goto cfg_cipsov4_map_add_failure; - return ret_val; + return 0; -cfg_cipsov4_add_map_failure_release_doi: +cfg_cipsov4_map_add_failure: cipso_v4_doi_putdef(doi_def); -cfg_cipsov4_add_map_failure_remove_doi: - cipso_v4_doi_remove(doi, audit_info); -cfg_cipsov4_add_map_failure: if (entry != NULL) kfree(entry->domain); kfree(entry); - goto cfg_cipsov4_add_map_return; + kfree(addrmap); + kfree(addrinfo); + return ret_val; } /* @@ -691,6 +896,28 @@ int netlbl_cache_add(const struct sk_buff *skb, } /* + * Protocol Engine Functions + */ + +/** + * netlbl_audit_start - Start an audit message + * @type: audit message type + * @audit_info: NetLabel audit information + * + * Description: + * Start an audit message using the type specified in @type and fill the audit + * message with some fields common to all NetLabel audit messages. This + * function should only be used by protocol engines, not LSMs. Returns a + * pointer to the audit buffer on success, NULL on failure. + * + */ +struct audit_buffer *netlbl_audit_start(int type, + struct netlbl_audit *audit_info) +{ + return netlbl_audit_start_common(type, audit_info); +} + +/* * Setup Functions */ diff --git a/net/netlabel/netlabel_unlabeled.c b/net/netlabel/netlabel_unlabeled.c index 8c030803217..f3c5c68c684 100644 --- a/net/netlabel/netlabel_unlabeled.c +++ b/net/netlabel/netlabel_unlabeled.c @@ -450,13 +450,13 @@ add_iface_failure: * success, negative values on failure. * */ -static int netlbl_unlhsh_add(struct net *net, - const char *dev_name, - const void *addr, - const void *mask, - u32 addr_len, - u32 secid, - struct netlbl_audit *audit_info) +int netlbl_unlhsh_add(struct net *net, + const char *dev_name, + const void *addr, + const void *mask, + u32 addr_len, + u32 secid, + struct netlbl_audit *audit_info) { int ret_val; int ifindex; @@ -720,12 +720,12 @@ unlhsh_condremove_failure: * Returns zero on success, negative values on failure. * */ -static int netlbl_unlhsh_remove(struct net *net, - const char *dev_name, - const void *addr, - const void *mask, - u32 addr_len, - struct netlbl_audit *audit_info) +int netlbl_unlhsh_remove(struct net *net, + const char *dev_name, + const void *addr, + const void *mask, + u32 addr_len, + struct netlbl_audit *audit_info) { int ret_val; struct net_device *dev; diff --git a/net/netlabel/netlabel_unlabeled.h b/net/netlabel/netlabel_unlabeled.h index 06b1301ac07..7aba6359513 100644 --- a/net/netlabel/netlabel_unlabeled.h +++ b/net/netlabel/netlabel_unlabeled.h @@ -221,6 +221,21 @@ int netlbl_unlabel_genl_init(void); /* General Unlabeled init function */ int netlbl_unlabel_init(u32 size); +/* Static/Fallback label management functions */ +int netlbl_unlhsh_add(struct net *net, + const char *dev_name, + const void *addr, + const void *mask, + u32 addr_len, + u32 secid, + struct netlbl_audit *audit_info); +int netlbl_unlhsh_remove(struct net *net, + const char *dev_name, + const void *addr, + const void *mask, + u32 addr_len, + struct netlbl_audit *audit_info); + /* Process Unlabeled incoming network packets */ int netlbl_unlabel_getattr(const struct sk_buff *skb, u16 family, diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c index 3e1191cecaf..1d3dd30099d 100644 --- a/net/netlink/genetlink.c +++ b/net/netlink/genetlink.c @@ -225,6 +225,7 @@ void genl_unregister_mc_group(struct genl_family *family, __genl_unregister_mc_group(family, grp); genl_unlock(); } +EXPORT_SYMBOL(genl_unregister_mc_group); static void genl_unregister_mc_groups(struct genl_family *family) { diff --git a/net/rfkill/rfkill.c b/net/rfkill/rfkill.c index 3c94f76d552..3eaa39403c1 100644 --- a/net/rfkill/rfkill.c +++ b/net/rfkill/rfkill.c @@ -54,10 +54,10 @@ static unsigned long rfkill_states_lockdflt[BITS_TO_LONGS(RFKILL_TYPE_MAX)]; static bool rfkill_epo_lock_active; +#ifdef CONFIG_RFKILL_LEDS static void rfkill_led_trigger(struct rfkill *rfkill, enum rfkill_state state) { -#ifdef CONFIG_RFKILL_LEDS struct led_trigger *led = &rfkill->led_trigger; if (!led->name) @@ -66,10 +66,8 @@ static void rfkill_led_trigger(struct rfkill *rfkill, led_trigger_event(led, LED_OFF); else led_trigger_event(led, LED_FULL); -#endif /* CONFIG_RFKILL_LEDS */ } -#ifdef CONFIG_RFKILL_LEDS static void rfkill_led_trigger_activate(struct led_classdev *led) { struct rfkill *rfkill = container_of(led->trigger, diff --git a/net/sched/Kconfig b/net/sched/Kconfig index 4f7ef0db302..929218a4762 100644 --- a/net/sched/Kconfig +++ b/net/sched/Kconfig @@ -335,9 +335,6 @@ config NET_CLS_CGROUP Say Y here if you want to classify packets based on the control cgroup of their process. - To compile this code as a module, choose M here: the - module will be called cls_cgroup. - config NET_EMATCH bool "Extended Matches" select NET_CLS diff --git a/net/sched/cls_cgroup.c b/net/sched/cls_cgroup.c index 0d68b197598..91a3db4a76f 100644 --- a/net/sched/cls_cgroup.c +++ b/net/sched/cls_cgroup.c @@ -24,10 +24,16 @@ struct cgroup_cls_state u32 classid; }; -static inline struct cgroup_cls_state *net_cls_state(struct cgroup *cgrp) +static inline struct cgroup_cls_state *cgrp_cls_state(struct cgroup *cgrp) { - return (struct cgroup_cls_state *) - cgroup_subsys_state(cgrp, net_cls_subsys_id); + return container_of(cgroup_subsys_state(cgrp, net_cls_subsys_id), + struct cgroup_cls_state, css); +} + +static inline struct cgroup_cls_state *task_cls_state(struct task_struct *p) +{ + return container_of(task_subsys_state(p, net_cls_subsys_id), + struct cgroup_cls_state, css); } static struct cgroup_subsys_state *cgrp_create(struct cgroup_subsys *ss, @@ -39,19 +45,19 @@ static struct cgroup_subsys_state *cgrp_create(struct cgroup_subsys *ss, return ERR_PTR(-ENOMEM); if (cgrp->parent) - cs->classid = net_cls_state(cgrp->parent)->classid; + cs->classid = cgrp_cls_state(cgrp->parent)->classid; return &cs->css; } static void cgrp_destroy(struct cgroup_subsys *ss, struct cgroup *cgrp) { - kfree(ss); + kfree(cgrp_cls_state(cgrp)); } static u64 read_classid(struct cgroup *cgrp, struct cftype *cft) { - return net_cls_state(cgrp)->classid; + return cgrp_cls_state(cgrp)->classid; } static int write_classid(struct cgroup *cgrp, struct cftype *cft, u64 value) @@ -59,7 +65,7 @@ static int write_classid(struct cgroup *cgrp, struct cftype *cft, u64 value) if (!cgroup_lock_live_group(cgrp)) return -ENODEV; - net_cls_state(cgrp)->classid = (u32) value; + cgrp_cls_state(cgrp)->classid = (u32) value; cgroup_unlock(); @@ -115,8 +121,7 @@ static int cls_cgroup_classify(struct sk_buff *skb, struct tcf_proto *tp, return -1; rcu_read_lock(); - cs = (struct cgroup_cls_state *) task_subsys_state(current, - net_cls_subsys_id); + cs = task_cls_state(current); if (cs->classid && tcf_em_tree_match(skb, &head->ematches, NULL)) { res->classid = cs->classid; res->class = 0; diff --git a/net/sched/cls_u32.c b/net/sched/cls_u32.c index 05d178008cb..07372f60bee 100644 --- a/net/sched/cls_u32.c +++ b/net/sched/cls_u32.c @@ -638,8 +638,9 @@ static int u32_change(struct tcf_proto *tp, unsigned long base, u32 handle, break; n->next = *ins; - wmb(); + tcf_tree_lock(tp); *ins = n; + tcf_tree_unlock(tp); *arg = (unsigned long)n; return 0; diff --git a/net/sctp/auth.c b/net/sctp/auth.c index 52db5f60daa..20c576f530f 100644 --- a/net/sctp/auth.c +++ b/net/sctp/auth.c @@ -141,8 +141,8 @@ void sctp_auth_destroy_keys(struct list_head *keys) /* Compare two byte vectors as numbers. Return values * are: * 0 - vectors are equal - * < 0 - vector 1 is smaller then vector2 - * > 0 - vector 1 is greater then vector2 + * < 0 - vector 1 is smaller than vector2 + * > 0 - vector 1 is greater than vector2 * * Algorithm is: * This is performed by selecting the numerically smaller key vector... diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index 1c4e5d6c29c..3a0cd075914 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -4268,9 +4268,9 @@ nomem: /* * Handle a protocol violation when the chunk length is invalid. - * "Invalid" length is identified as smaller then the minimal length a + * "Invalid" length is identified as smaller than the minimal length a * given chunk can be. For example, a SACK chunk has invalid length - * if it's length is set to be smaller then the size of sctp_sack_chunk_t. + * if its length is set to be smaller than the size of sctp_sack_chunk_t. * * We inform the other end by sending an ABORT with a Protocol Violation * error code. @@ -4300,7 +4300,7 @@ static sctp_disposition_t sctp_sf_violation_chunklen( /* * Handle a protocol violation when the parameter length is invalid. - * "Invalid" length is identified as smaller then the minimal length a + * "Invalid" length is identified as smaller than the minimal length a * given parameter can be. */ static sctp_disposition_t sctp_sf_violation_paramlen( diff --git a/net/sctp/socket.c b/net/sctp/socket.c index b14a8f33e42..ff0a8f88de0 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -2717,7 +2717,7 @@ static int sctp_setsockopt_associnfo(struct sock *sk, char __user *optval, int o paths++; } - /* Only validate asocmaxrxt if we have more then + /* Only validate asocmaxrxt if we have more than * one path/transport. We do this because path * retransmissions are only counted when we have more * then one path. diff --git a/net/sctp/tsnmap.c b/net/sctp/tsnmap.c index 35c73e82553..9bd64565021 100644 --- a/net/sctp/tsnmap.c +++ b/net/sctp/tsnmap.c @@ -227,7 +227,7 @@ void sctp_tsnmap_skip(struct sctp_tsnmap *map, __u32 tsn) */ bitmap_zero(map->tsn_map, map->len); } else { - /* If the gap is smaller then the map size, + /* If the gap is smaller than the map size, * shift the map by 'gap' bits and update further. */ bitmap_shift_right(map->tsn_map, map->tsn_map, gap, map->len); diff --git a/net/socket.c b/net/socket.c index 2c730fc718a..06603d73c41 100644 --- a/net/socket.c +++ b/net/socket.c @@ -1313,13 +1313,7 @@ asmlinkage long sys_socketpair(int family, int type, int protocol, goto out_fd1; } - err = audit_fd_pair(fd1, fd2); - if (err < 0) { - fput(newfile1); - fput(newfile2); - goto out_fd; - } - + audit_fd_pair(fd1, fd2); fd_install(fd1, newfile1); fd_install(fd2, newfile2); /* fd1 and fd2 may be already another descriptors. @@ -1349,7 +1343,6 @@ out_fd2: out_fd1: put_filp(newfile2); sock_release(sock2); -out_fd: put_unused_fd(fd1); put_unused_fd(fd2); goto out; @@ -2065,9 +2058,7 @@ asmlinkage long sys_socketcall(int call, unsigned long __user *args) if (copy_from_user(a, args, nargs[call])) return -EFAULT; - err = audit_socketcall(nargs[call] / sizeof(unsigned long), a); - if (err) - return err; + audit_socketcall(nargs[call] / sizeof(unsigned long), a); a0 = a[0]; a1 = a[1]; diff --git a/net/sunrpc/auth.c b/net/sunrpc/auth.c index 0443f834945..0c431c277af 100644 --- a/net/sunrpc/auth.c +++ b/net/sunrpc/auth.c @@ -234,7 +234,7 @@ rpcauth_prune_expired(struct list_head *free, int nr_to_scan) list_for_each_entry_safe(cred, next, &cred_unused, cr_lru) { /* Enforce a 60 second garbage collection moratorium */ - if (time_in_range(cred->cr_expire, expired, jiffies) && + if (time_in_range_open(cred->cr_expire, expired, jiffies) && test_bit(RPCAUTH_CRED_HASHED, &cred->cr_flags) != 0) continue; @@ -515,7 +515,7 @@ rpcauth_wrap_req(struct rpc_task *task, kxdrproc_t encode, void *rqstp, if (cred->cr_ops->crwrap_req) return cred->cr_ops->crwrap_req(task, encode, rqstp, data, obj); /* By default, we encode the arguments normally. */ - return rpc_call_xdrproc(encode, rqstp, data, obj); + return encode(rqstp, data, obj); } int @@ -530,7 +530,7 @@ rpcauth_unwrap_resp(struct rpc_task *task, kxdrproc_t decode, void *rqstp, return cred->cr_ops->crunwrap_resp(task, decode, rqstp, data, obj); /* By default, we decode the arguments normally. */ - return rpc_call_xdrproc(decode, rqstp, data, obj); + return decode(rqstp, data, obj); } int diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c index 853a4142cea..e630b38a604 100644 --- a/net/sunrpc/auth_gss/auth_gss.c +++ b/net/sunrpc/auth_gss/auth_gss.c @@ -72,11 +72,25 @@ struct gss_auth { struct gss_api_mech *mech; enum rpc_gss_svc service; struct rpc_clnt *client; - struct dentry *dentry; + /* + * There are two upcall pipes; dentry[1], named "gssd", is used + * for the new text-based upcall; dentry[0] is named after the + * mechanism (for example, "krb5") and exists for + * backwards-compatibility with older gssd's. + */ + struct dentry *dentry[2]; }; +/* pipe_version >= 0 if and only if someone has a pipe open. */ +static int pipe_version = -1; +static atomic_t pipe_users = ATOMIC_INIT(0); +static DEFINE_SPINLOCK(pipe_version_lock); +static struct rpc_wait_queue pipe_version_rpc_waitqueue; +static DECLARE_WAIT_QUEUE_HEAD(pipe_version_waitqueue); + static void gss_free_ctx(struct gss_cl_ctx *); -static struct rpc_pipe_ops gss_upcall_ops; +static struct rpc_pipe_ops gss_upcall_ops_v0; +static struct rpc_pipe_ops gss_upcall_ops_v1; static inline struct gss_cl_ctx * gss_get_ctx(struct gss_cl_ctx *ctx) @@ -220,6 +234,7 @@ err: return p; } +#define UPCALL_BUF_LEN 128 struct gss_upcall_msg { atomic_t count; @@ -227,16 +242,41 @@ struct gss_upcall_msg { struct rpc_pipe_msg msg; struct list_head list; struct gss_auth *auth; + struct rpc_inode *inode; struct rpc_wait_queue rpc_waitqueue; wait_queue_head_t waitqueue; struct gss_cl_ctx *ctx; + char databuf[UPCALL_BUF_LEN]; }; +static int get_pipe_version(void) +{ + int ret; + + spin_lock(&pipe_version_lock); + if (pipe_version >= 0) { + atomic_inc(&pipe_users); + ret = pipe_version; + } else + ret = -EAGAIN; + spin_unlock(&pipe_version_lock); + return ret; +} + +static void put_pipe_version(void) +{ + if (atomic_dec_and_lock(&pipe_users, &pipe_version_lock)) { + pipe_version = -1; + spin_unlock(&pipe_version_lock); + } +} + static void gss_release_msg(struct gss_upcall_msg *gss_msg) { if (!atomic_dec_and_test(&gss_msg->count)) return; + put_pipe_version(); BUG_ON(!list_empty(&gss_msg->list)); if (gss_msg->ctx != NULL) gss_put_ctx(gss_msg->ctx); @@ -266,8 +306,8 @@ __gss_find_upcall(struct rpc_inode *rpci, uid_t uid) static inline struct gss_upcall_msg * gss_add_msg(struct gss_auth *gss_auth, struct gss_upcall_msg *gss_msg) { - struct inode *inode = gss_auth->dentry->d_inode; - struct rpc_inode *rpci = RPC_I(inode); + struct rpc_inode *rpci = gss_msg->inode; + struct inode *inode = &rpci->vfs_inode; struct gss_upcall_msg *old; spin_lock(&inode->i_lock); @@ -293,8 +333,7 @@ __gss_unhash_msg(struct gss_upcall_msg *gss_msg) static void gss_unhash_msg(struct gss_upcall_msg *gss_msg) { - struct gss_auth *gss_auth = gss_msg->auth; - struct inode *inode = gss_auth->dentry->d_inode; + struct inode *inode = &gss_msg->inode->vfs_inode; if (list_empty(&gss_msg->list)) return; @@ -310,7 +349,7 @@ gss_upcall_callback(struct rpc_task *task) struct gss_cred *gss_cred = container_of(task->tk_msg.rpc_cred, struct gss_cred, gc_base); struct gss_upcall_msg *gss_msg = gss_cred->gc_upcall; - struct inode *inode = gss_msg->auth->dentry->d_inode; + struct inode *inode = &gss_msg->inode->vfs_inode; spin_lock(&inode->i_lock); if (gss_msg->ctx) @@ -323,22 +362,75 @@ gss_upcall_callback(struct rpc_task *task) gss_release_msg(gss_msg); } +static void gss_encode_v0_msg(struct gss_upcall_msg *gss_msg) +{ + gss_msg->msg.data = &gss_msg->uid; + gss_msg->msg.len = sizeof(gss_msg->uid); +} + +static void gss_encode_v1_msg(struct gss_upcall_msg *gss_msg, + struct rpc_clnt *clnt, int machine_cred) +{ + char *p = gss_msg->databuf; + int len = 0; + + gss_msg->msg.len = sprintf(gss_msg->databuf, "mech=%s uid=%d ", + gss_msg->auth->mech->gm_name, + gss_msg->uid); + p += gss_msg->msg.len; + if (clnt->cl_principal) { + len = sprintf(p, "target=%s ", clnt->cl_principal); + p += len; + gss_msg->msg.len += len; + } + if (machine_cred) { + len = sprintf(p, "service=* "); + p += len; + gss_msg->msg.len += len; + } else if (!strcmp(clnt->cl_program->name, "nfs4_cb")) { + len = sprintf(p, "service=nfs "); + p += len; + gss_msg->msg.len += len; + } + len = sprintf(p, "\n"); + gss_msg->msg.len += len; + + gss_msg->msg.data = gss_msg->databuf; + BUG_ON(gss_msg->msg.len > UPCALL_BUF_LEN); +} + +static void gss_encode_msg(struct gss_upcall_msg *gss_msg, + struct rpc_clnt *clnt, int machine_cred) +{ + if (pipe_version == 0) + gss_encode_v0_msg(gss_msg); + else /* pipe_version == 1 */ + gss_encode_v1_msg(gss_msg, clnt, machine_cred); +} + static inline struct gss_upcall_msg * -gss_alloc_msg(struct gss_auth *gss_auth, uid_t uid) +gss_alloc_msg(struct gss_auth *gss_auth, uid_t uid, struct rpc_clnt *clnt, + int machine_cred) { struct gss_upcall_msg *gss_msg; + int vers; gss_msg = kzalloc(sizeof(*gss_msg), GFP_NOFS); - if (gss_msg != NULL) { - INIT_LIST_HEAD(&gss_msg->list); - rpc_init_wait_queue(&gss_msg->rpc_waitqueue, "RPCSEC_GSS upcall waitq"); - init_waitqueue_head(&gss_msg->waitqueue); - atomic_set(&gss_msg->count, 1); - gss_msg->msg.data = &gss_msg->uid; - gss_msg->msg.len = sizeof(gss_msg->uid); - gss_msg->uid = uid; - gss_msg->auth = gss_auth; + if (gss_msg == NULL) + return ERR_PTR(-ENOMEM); + vers = get_pipe_version(); + if (vers < 0) { + kfree(gss_msg); + return ERR_PTR(vers); } + gss_msg->inode = RPC_I(gss_auth->dentry[vers]->d_inode); + INIT_LIST_HEAD(&gss_msg->list); + rpc_init_wait_queue(&gss_msg->rpc_waitqueue, "RPCSEC_GSS upcall waitq"); + init_waitqueue_head(&gss_msg->waitqueue); + atomic_set(&gss_msg->count, 1); + gss_msg->uid = uid; + gss_msg->auth = gss_auth; + gss_encode_msg(gss_msg, clnt, machine_cred); return gss_msg; } @@ -350,16 +442,13 @@ gss_setup_upcall(struct rpc_clnt *clnt, struct gss_auth *gss_auth, struct rpc_cr struct gss_upcall_msg *gss_new, *gss_msg; uid_t uid = cred->cr_uid; - /* Special case: rpc.gssd assumes that uid == 0 implies machine creds */ - if (gss_cred->gc_machine_cred != 0) - uid = 0; - - gss_new = gss_alloc_msg(gss_auth, uid); - if (gss_new == NULL) - return ERR_PTR(-ENOMEM); + gss_new = gss_alloc_msg(gss_auth, uid, clnt, gss_cred->gc_machine_cred); + if (IS_ERR(gss_new)) + return gss_new; gss_msg = gss_add_msg(gss_auth, gss_new); if (gss_msg == gss_new) { - int res = rpc_queue_upcall(gss_auth->dentry->d_inode, &gss_new->msg); + struct inode *inode = &gss_new->inode->vfs_inode; + int res = rpc_queue_upcall(inode, &gss_new->msg); if (res) { gss_unhash_msg(gss_new); gss_msg = ERR_PTR(res); @@ -369,6 +458,18 @@ gss_setup_upcall(struct rpc_clnt *clnt, struct gss_auth *gss_auth, struct rpc_cr return gss_msg; } +static void warn_gssd(void) +{ + static unsigned long ratelimit; + unsigned long now = jiffies; + + if (time_after(now, ratelimit)) { + printk(KERN_WARNING "RPC: AUTH_GSS upcall timed out.\n" + "Please check user daemon is running.\n"); + ratelimit = now + 15*HZ; + } +} + static inline int gss_refresh_upcall(struct rpc_task *task) { @@ -378,16 +479,25 @@ gss_refresh_upcall(struct rpc_task *task) struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base); struct gss_upcall_msg *gss_msg; - struct inode *inode = gss_auth->dentry->d_inode; + struct inode *inode; int err = 0; dprintk("RPC: %5u gss_refresh_upcall for uid %u\n", task->tk_pid, cred->cr_uid); gss_msg = gss_setup_upcall(task->tk_client, gss_auth, cred); + if (IS_ERR(gss_msg) == -EAGAIN) { + /* XXX: warning on the first, under the assumption we + * shouldn't normally hit this case on a refresh. */ + warn_gssd(); + task->tk_timeout = 15*HZ; + rpc_sleep_on(&pipe_version_rpc_waitqueue, task, NULL); + return 0; + } if (IS_ERR(gss_msg)) { err = PTR_ERR(gss_msg); goto out; } + inode = &gss_msg->inode->vfs_inode; spin_lock(&inode->i_lock); if (gss_cred->gc_upcall != NULL) rpc_sleep_on(&gss_cred->gc_upcall->rpc_waitqueue, task, NULL); @@ -414,18 +524,29 @@ out: static inline int gss_create_upcall(struct gss_auth *gss_auth, struct gss_cred *gss_cred) { - struct inode *inode = gss_auth->dentry->d_inode; + struct inode *inode; struct rpc_cred *cred = &gss_cred->gc_base; struct gss_upcall_msg *gss_msg; DEFINE_WAIT(wait); int err = 0; dprintk("RPC: gss_upcall for uid %u\n", cred->cr_uid); +retry: gss_msg = gss_setup_upcall(gss_auth->client, gss_auth, cred); + if (PTR_ERR(gss_msg) == -EAGAIN) { + err = wait_event_interruptible_timeout(pipe_version_waitqueue, + pipe_version >= 0, 15*HZ); + if (err) + goto out; + if (pipe_version < 0) + warn_gssd(); + goto retry; + } if (IS_ERR(gss_msg)) { err = PTR_ERR(gss_msg); goto out; } + inode = &gss_msg->inode->vfs_inode; for (;;) { prepare_to_wait(&gss_msg->waitqueue, &wait, TASK_INTERRUPTIBLE); spin_lock(&inode->i_lock); @@ -543,6 +664,38 @@ out: return err; } +static int gss_pipe_open(struct inode *inode, int new_version) +{ + int ret = 0; + + spin_lock(&pipe_version_lock); + if (pipe_version < 0) { + /* First open of any gss pipe determines the version: */ + pipe_version = new_version; + rpc_wake_up(&pipe_version_rpc_waitqueue); + wake_up(&pipe_version_waitqueue); + } else if (pipe_version != new_version) { + /* Trying to open a pipe of a different version */ + ret = -EBUSY; + goto out; + } + atomic_inc(&pipe_users); +out: + spin_unlock(&pipe_version_lock); + return ret; + +} + +static int gss_pipe_open_v0(struct inode *inode) +{ + return gss_pipe_open(inode, 0); +} + +static int gss_pipe_open_v1(struct inode *inode) +{ + return gss_pipe_open(inode, 1); +} + static void gss_pipe_release(struct inode *inode) { @@ -562,27 +715,22 @@ gss_pipe_release(struct inode *inode) spin_lock(&inode->i_lock); } spin_unlock(&inode->i_lock); + + put_pipe_version(); } static void gss_pipe_destroy_msg(struct rpc_pipe_msg *msg) { struct gss_upcall_msg *gss_msg = container_of(msg, struct gss_upcall_msg, msg); - static unsigned long ratelimit; if (msg->errno < 0) { dprintk("RPC: gss_pipe_destroy_msg releasing msg %p\n", gss_msg); atomic_inc(&gss_msg->count); gss_unhash_msg(gss_msg); - if (msg->errno == -ETIMEDOUT) { - unsigned long now = jiffies; - if (time_after(now, ratelimit)) { - printk(KERN_WARNING "RPC: AUTH_GSS upcall timed out.\n" - "Please check user daemon is running!\n"); - ratelimit = now + 15*HZ; - } - } + if (msg->errno == -ETIMEDOUT) + warn_gssd(); gss_release_msg(gss_msg); } } @@ -623,20 +771,38 @@ gss_create(struct rpc_clnt *clnt, rpc_authflavor_t flavor) atomic_set(&auth->au_count, 1); kref_init(&gss_auth->kref); - gss_auth->dentry = rpc_mkpipe(clnt->cl_dentry, gss_auth->mech->gm_name, - clnt, &gss_upcall_ops, RPC_PIPE_WAIT_FOR_OPEN); - if (IS_ERR(gss_auth->dentry)) { - err = PTR_ERR(gss_auth->dentry); + /* + * Note: if we created the old pipe first, then someone who + * examined the directory at the right moment might conclude + * that we supported only the old pipe. So we instead create + * the new pipe first. + */ + gss_auth->dentry[1] = rpc_mkpipe(clnt->cl_dentry, + "gssd", + clnt, &gss_upcall_ops_v1, + RPC_PIPE_WAIT_FOR_OPEN); + if (IS_ERR(gss_auth->dentry[1])) { + err = PTR_ERR(gss_auth->dentry[1]); goto err_put_mech; } + gss_auth->dentry[0] = rpc_mkpipe(clnt->cl_dentry, + gss_auth->mech->gm_name, + clnt, &gss_upcall_ops_v0, + RPC_PIPE_WAIT_FOR_OPEN); + if (IS_ERR(gss_auth->dentry[0])) { + err = PTR_ERR(gss_auth->dentry[0]); + goto err_unlink_pipe_1; + } err = rpcauth_init_credcache(auth); if (err) - goto err_unlink_pipe; + goto err_unlink_pipe_0; return auth; -err_unlink_pipe: - rpc_unlink(gss_auth->dentry); +err_unlink_pipe_0: + rpc_unlink(gss_auth->dentry[0]); +err_unlink_pipe_1: + rpc_unlink(gss_auth->dentry[1]); err_put_mech: gss_mech_put(gss_auth->mech); err_free: @@ -649,8 +815,8 @@ out_dec: static void gss_free(struct gss_auth *gss_auth) { - rpc_unlink(gss_auth->dentry); - gss_auth->dentry = NULL; + rpc_unlink(gss_auth->dentry[1]); + rpc_unlink(gss_auth->dentry[0]); gss_mech_put(gss_auth->mech); kfree(gss_auth); @@ -693,7 +859,7 @@ gss_destroying_context(struct rpc_cred *cred) struct rpc_task *task; if (gss_cred->gc_ctx == NULL || - test_and_clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags) == 0) + test_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags) == 0) return 0; gss_cred->gc_ctx->gc_proc = RPC_GSS_PROC_DESTROY; @@ -757,14 +923,12 @@ gss_free_cred_callback(struct rcu_head *head) } static void -gss_destroy_cred(struct rpc_cred *cred) +gss_destroy_nullcred(struct rpc_cred *cred) { struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base); struct gss_auth *gss_auth = container_of(cred->cr_auth, struct gss_auth, rpc_auth); struct gss_cl_ctx *ctx = gss_cred->gc_ctx; - if (gss_destroying_context(cred)) - return; rcu_assign_pointer(gss_cred->gc_ctx, NULL); call_rcu(&cred->cr_rcu, gss_free_cred_callback); if (ctx) @@ -772,6 +936,15 @@ gss_destroy_cred(struct rpc_cred *cred) kref_put(&gss_auth->kref, gss_free_callback); } +static void +gss_destroy_cred(struct rpc_cred *cred) +{ + + if (gss_destroying_context(cred)) + return; + gss_destroy_nullcred(cred); +} + /* * Lookup RPCSEC_GSS cred for the current process */ @@ -1017,7 +1190,7 @@ gss_wrap_req_integ(struct rpc_cred *cred, struct gss_cl_ctx *ctx, offset = (u8 *)p - (u8 *)snd_buf->head[0].iov_base; *p++ = htonl(rqstp->rq_seqno); - status = rpc_call_xdrproc(encode, rqstp, p, obj); + status = encode(rqstp, p, obj); if (status) return status; @@ -1111,7 +1284,7 @@ gss_wrap_req_priv(struct rpc_cred *cred, struct gss_cl_ctx *ctx, offset = (u8 *)p - (u8 *)snd_buf->head[0].iov_base; *p++ = htonl(rqstp->rq_seqno); - status = rpc_call_xdrproc(encode, rqstp, p, obj); + status = encode(rqstp, p, obj); if (status) return status; @@ -1170,12 +1343,12 @@ gss_wrap_req(struct rpc_task *task, /* The spec seems a little ambiguous here, but I think that not * wrapping context destruction requests makes the most sense. */ - status = rpc_call_xdrproc(encode, rqstp, p, obj); + status = encode(rqstp, p, obj); goto out; } switch (gss_cred->gc_service) { case RPC_GSS_SVC_NONE: - status = rpc_call_xdrproc(encode, rqstp, p, obj); + status = encode(rqstp, p, obj); break; case RPC_GSS_SVC_INTEGRITY: status = gss_wrap_req_integ(cred, ctx, encode, @@ -1291,7 +1464,7 @@ gss_unwrap_resp(struct rpc_task *task, cred->cr_auth->au_rslack = cred->cr_auth->au_verfsize + (p - savedp) + (savedlen - head->iov_len); out_decode: - status = rpc_call_xdrproc(decode, rqstp, p, obj); + status = decode(rqstp, p, obj); out: gss_put_ctx(ctx); dprintk("RPC: %5u gss_unwrap_resp returning %d\n", task->tk_pid, @@ -1324,7 +1497,7 @@ static const struct rpc_credops gss_credops = { static const struct rpc_credops gss_nullops = { .cr_name = "AUTH_GSS", - .crdestroy = gss_destroy_cred, + .crdestroy = gss_destroy_nullcred, .crbind = rpcauth_generic_bind_cred, .crmatch = gss_match, .crmarshal = gss_marshal, @@ -1334,10 +1507,19 @@ static const struct rpc_credops gss_nullops = { .crunwrap_resp = gss_unwrap_resp, }; -static struct rpc_pipe_ops gss_upcall_ops = { +static struct rpc_pipe_ops gss_upcall_ops_v0 = { + .upcall = gss_pipe_upcall, + .downcall = gss_pipe_downcall, + .destroy_msg = gss_pipe_destroy_msg, + .open_pipe = gss_pipe_open_v0, + .release_pipe = gss_pipe_release, +}; + +static struct rpc_pipe_ops gss_upcall_ops_v1 = { .upcall = gss_pipe_upcall, .downcall = gss_pipe_downcall, .destroy_msg = gss_pipe_destroy_msg, + .open_pipe = gss_pipe_open_v1, .release_pipe = gss_pipe_release, }; @@ -1354,6 +1536,7 @@ static int __init init_rpcsec_gss(void) err = gss_svc_init(); if (err) goto out_unregister; + rpc_init_wait_queue(&pipe_version_rpc_waitqueue, "gss pipe version"); return 0; out_unregister: rpcauth_unregister(&authgss_ops); diff --git a/net/sunrpc/auth_gss/gss_generic_token.c b/net/sunrpc/auth_gss/gss_generic_token.c index d83b881685f..c0ba39c4f5f 100644 --- a/net/sunrpc/auth_gss/gss_generic_token.c +++ b/net/sunrpc/auth_gss/gss_generic_token.c @@ -152,7 +152,7 @@ g_token_size(struct xdr_netobj *mech, unsigned int body_size) return(1 + der_length_size(body_size) + body_size); } -EXPORT_SYMBOL(g_token_size); +EXPORT_SYMBOL_GPL(g_token_size); /* fills in a buffer with the token header. The buffer is assumed to be the right size. buf is advanced past the token header */ @@ -167,7 +167,7 @@ g_make_token_header(struct xdr_netobj *mech, int body_size, unsigned char **buf) TWRITE_STR(*buf, mech->data, ((int) mech->len)); } -EXPORT_SYMBOL(g_make_token_header); +EXPORT_SYMBOL_GPL(g_make_token_header); /* * Given a buffer containing a token, reads and verifies the token, @@ -231,5 +231,5 @@ g_verify_token_header(struct xdr_netobj *mech, int *body_size, return(ret); } -EXPORT_SYMBOL(g_verify_token_header); +EXPORT_SYMBOL_GPL(g_verify_token_header); diff --git a/net/sunrpc/auth_gss/gss_mech_switch.c b/net/sunrpc/auth_gss/gss_mech_switch.c index bce9d527af0..6efbb0cd3c7 100644 --- a/net/sunrpc/auth_gss/gss_mech_switch.c +++ b/net/sunrpc/auth_gss/gss_mech_switch.c @@ -117,7 +117,7 @@ gss_mech_register(struct gss_api_mech *gm) return 0; } -EXPORT_SYMBOL(gss_mech_register); +EXPORT_SYMBOL_GPL(gss_mech_register); void gss_mech_unregister(struct gss_api_mech *gm) @@ -129,7 +129,7 @@ gss_mech_unregister(struct gss_api_mech *gm) gss_mech_free(gm); } -EXPORT_SYMBOL(gss_mech_unregister); +EXPORT_SYMBOL_GPL(gss_mech_unregister); struct gss_api_mech * gss_mech_get(struct gss_api_mech *gm) @@ -138,7 +138,7 @@ gss_mech_get(struct gss_api_mech *gm) return gm; } -EXPORT_SYMBOL(gss_mech_get); +EXPORT_SYMBOL_GPL(gss_mech_get); struct gss_api_mech * gss_mech_get_by_name(const char *name) @@ -158,7 +158,7 @@ gss_mech_get_by_name(const char *name) } -EXPORT_SYMBOL(gss_mech_get_by_name); +EXPORT_SYMBOL_GPL(gss_mech_get_by_name); static inline int mech_supports_pseudoflavor(struct gss_api_mech *gm, u32 pseudoflavor) @@ -191,7 +191,7 @@ gss_mech_get_by_pseudoflavor(u32 pseudoflavor) return gm; } -EXPORT_SYMBOL(gss_mech_get_by_pseudoflavor); +EXPORT_SYMBOL_GPL(gss_mech_get_by_pseudoflavor); u32 gss_svc_to_pseudoflavor(struct gss_api_mech *gm, u32 service) @@ -205,7 +205,7 @@ gss_svc_to_pseudoflavor(struct gss_api_mech *gm, u32 service) } return RPC_AUTH_MAXFLAVOR; /* illegal value */ } -EXPORT_SYMBOL(gss_svc_to_pseudoflavor); +EXPORT_SYMBOL_GPL(gss_svc_to_pseudoflavor); u32 gss_pseudoflavor_to_service(struct gss_api_mech *gm, u32 pseudoflavor) @@ -219,7 +219,7 @@ gss_pseudoflavor_to_service(struct gss_api_mech *gm, u32 pseudoflavor) return 0; } -EXPORT_SYMBOL(gss_pseudoflavor_to_service); +EXPORT_SYMBOL_GPL(gss_pseudoflavor_to_service); char * gss_service_to_auth_domain_name(struct gss_api_mech *gm, u32 service) @@ -233,7 +233,7 @@ gss_service_to_auth_domain_name(struct gss_api_mech *gm, u32 service) return NULL; } -EXPORT_SYMBOL(gss_service_to_auth_domain_name); +EXPORT_SYMBOL_GPL(gss_service_to_auth_domain_name); void gss_mech_put(struct gss_api_mech * gm) @@ -242,7 +242,7 @@ gss_mech_put(struct gss_api_mech * gm) module_put(gm->gm_owner); } -EXPORT_SYMBOL(gss_mech_put); +EXPORT_SYMBOL_GPL(gss_mech_put); /* The mech could probably be determined from the token instead, but it's just * as easy for now to pass it in. */ diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c index 81ae3d62a0c..2278a50c644 100644 --- a/net/sunrpc/auth_gss/svcauth_gss.c +++ b/net/sunrpc/auth_gss/svcauth_gss.c @@ -332,6 +332,7 @@ struct rsc { struct svc_cred cred; struct gss_svc_seq_data seqdata; struct gss_ctx *mechctx; + char *client_name; }; static struct cache_head *rsc_table[RSC_HASHMAX]; @@ -346,6 +347,7 @@ static void rsc_free(struct rsc *rsci) gss_delete_sec_context(&rsci->mechctx); if (rsci->cred.cr_group_info) put_group_info(rsci->cred.cr_group_info); + kfree(rsci->client_name); } static void rsc_put(struct kref *ref) @@ -383,6 +385,7 @@ rsc_init(struct cache_head *cnew, struct cache_head *ctmp) tmp->handle.data = NULL; new->mechctx = NULL; new->cred.cr_group_info = NULL; + new->client_name = NULL; } static void @@ -397,6 +400,8 @@ update_rsc(struct cache_head *cnew, struct cache_head *ctmp) spin_lock_init(&new->seqdata.sd_lock); new->cred = tmp->cred; tmp->cred.cr_group_info = NULL; + new->client_name = tmp->client_name; + tmp->client_name = NULL; } static struct cache_head * @@ -486,6 +491,15 @@ static int rsc_parse(struct cache_detail *cd, status = gss_import_sec_context(buf, len, gm, &rsci.mechctx); if (status) goto out; + + /* get client name */ + len = qword_get(&mesg, buf, mlen); + if (len > 0) { + rsci.client_name = kstrdup(buf, GFP_KERNEL); + if (!rsci.client_name) + goto out; + } + } rsci.h.expiry_time = expiry; rscp = rsc_update(&rsci, rscp); @@ -746,7 +760,7 @@ u32 svcauth_gss_flavor(struct auth_domain *dom) return gd->pseudoflavor; } -EXPORT_SYMBOL(svcauth_gss_flavor); +EXPORT_SYMBOL_GPL(svcauth_gss_flavor); int svcauth_gss_register_pseudoflavor(u32 pseudoflavor, char * name) @@ -780,7 +794,7 @@ out: return stat; } -EXPORT_SYMBOL(svcauth_gss_register_pseudoflavor); +EXPORT_SYMBOL_GPL(svcauth_gss_register_pseudoflavor); static inline int read_u32_from_xdr_buf(struct xdr_buf *buf, int base, u32 *obj) @@ -913,6 +927,16 @@ struct gss_svc_data { struct rsc *rsci; }; +char *svc_gss_principal(struct svc_rqst *rqstp) +{ + struct gss_svc_data *gd = (struct gss_svc_data *)rqstp->rq_auth_data; + + if (gd && gd->rsci) + return gd->rsci->client_name; + return NULL; +} +EXPORT_SYMBOL_GPL(svc_gss_principal); + static int svcauth_gss_set_client(struct svc_rqst *rqstp) { diff --git a/net/sunrpc/cache.c b/net/sunrpc/cache.c index c9966713282..4735caad26e 100644 --- a/net/sunrpc/cache.c +++ b/net/sunrpc/cache.c @@ -98,7 +98,7 @@ struct cache_head *sunrpc_cache_lookup(struct cache_detail *detail, return new; } -EXPORT_SYMBOL(sunrpc_cache_lookup); +EXPORT_SYMBOL_GPL(sunrpc_cache_lookup); static void queue_loose(struct cache_detail *detail, struct cache_head *ch); @@ -173,7 +173,7 @@ struct cache_head *sunrpc_cache_update(struct cache_detail *detail, cache_put(old, detail); return tmp; } -EXPORT_SYMBOL(sunrpc_cache_update); +EXPORT_SYMBOL_GPL(sunrpc_cache_update); static int cache_make_upcall(struct cache_detail *detail, struct cache_head *h); /* @@ -245,7 +245,7 @@ int cache_check(struct cache_detail *detail, cache_put(h, detail); return rv; } -EXPORT_SYMBOL(cache_check); +EXPORT_SYMBOL_GPL(cache_check); /* * caches need to be periodically cleaned. @@ -373,7 +373,7 @@ int cache_register(struct cache_detail *cd) schedule_delayed_work(&cache_cleaner, 0); return 0; } -EXPORT_SYMBOL(cache_register); +EXPORT_SYMBOL_GPL(cache_register); void cache_unregister(struct cache_detail *cd) { @@ -399,7 +399,7 @@ void cache_unregister(struct cache_detail *cd) out: printk(KERN_ERR "nfsd: failed to unregister %s cache\n", cd->name); } -EXPORT_SYMBOL(cache_unregister); +EXPORT_SYMBOL_GPL(cache_unregister); /* clean cache tries to find something to clean * and cleans it. @@ -514,7 +514,7 @@ void cache_flush(void) while (cache_clean() != -1) cond_resched(); } -EXPORT_SYMBOL(cache_flush); +EXPORT_SYMBOL_GPL(cache_flush); void cache_purge(struct cache_detail *detail) { @@ -523,7 +523,7 @@ void cache_purge(struct cache_detail *detail) cache_flush(); detail->flush_time = 1; } -EXPORT_SYMBOL(cache_purge); +EXPORT_SYMBOL_GPL(cache_purge); /* @@ -988,7 +988,7 @@ void qword_add(char **bpp, int *lp, char *str) *bpp = bp; *lp = len; } -EXPORT_SYMBOL(qword_add); +EXPORT_SYMBOL_GPL(qword_add); void qword_addhex(char **bpp, int *lp, char *buf, int blen) { @@ -1017,7 +1017,7 @@ void qword_addhex(char **bpp, int *lp, char *buf, int blen) *bpp = bp; *lp = len; } -EXPORT_SYMBOL(qword_addhex); +EXPORT_SYMBOL_GPL(qword_addhex); static void warn_no_listener(struct cache_detail *detail) { @@ -1140,7 +1140,7 @@ int qword_get(char **bpp, char *dest, int bufsize) *dest = '\0'; return len; } -EXPORT_SYMBOL(qword_get); +EXPORT_SYMBOL_GPL(qword_get); /* diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index 3ca518386d1..836f15c0c4a 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -197,6 +197,12 @@ static struct rpc_clnt * rpc_new_client(const struct rpc_create_args *args, stru clnt->cl_rtt = &clnt->cl_rtt_default; rpc_init_rtt(&clnt->cl_rtt_default, clnt->cl_timeout->to_initval); + clnt->cl_principal = NULL; + if (args->client_name) { + clnt->cl_principal = kstrdup(args->client_name, GFP_KERNEL); + if (!clnt->cl_principal) + goto out_no_principal; + } kref_init(&clnt->cl_kref); @@ -226,6 +232,8 @@ out_no_auth: rpc_put_mount(); } out_no_path: + kfree(clnt->cl_principal); +out_no_principal: rpc_free_iostats(clnt->cl_metrics); out_no_stats: if (clnt->cl_server != clnt->cl_inline_name) @@ -354,6 +362,11 @@ rpc_clone_client(struct rpc_clnt *clnt) new->cl_metrics = rpc_alloc_iostats(clnt); if (new->cl_metrics == NULL) goto out_no_stats; + if (clnt->cl_principal) { + new->cl_principal = kstrdup(clnt->cl_principal, GFP_KERNEL); + if (new->cl_principal == NULL) + goto out_no_principal; + } kref_init(&new->cl_kref); err = rpc_setup_pipedir(new, clnt->cl_program->pipe_dir_name); if (err != 0) @@ -366,6 +379,8 @@ rpc_clone_client(struct rpc_clnt *clnt) rpciod_up(); return new; out_no_path: + kfree(new->cl_principal); +out_no_principal: rpc_free_iostats(new->cl_metrics); out_no_stats: kfree(new); @@ -417,6 +432,7 @@ rpc_free_client(struct kref *kref) out_free: rpc_unregister_client(clnt); rpc_free_iostats(clnt->cl_metrics); + kfree(clnt->cl_principal); clnt->cl_metrics = NULL; xprt_put(clnt->cl_xprt); rpciod_down(); diff --git a/net/sunrpc/rpc_pipe.c b/net/sunrpc/rpc_pipe.c index 23a2b8f6dc4..577385a4a5d 100644 --- a/net/sunrpc/rpc_pipe.c +++ b/net/sunrpc/rpc_pipe.c @@ -113,7 +113,7 @@ out: wake_up(&rpci->waitq); return res; } -EXPORT_SYMBOL(rpc_queue_upcall); +EXPORT_SYMBOL_GPL(rpc_queue_upcall); static inline void rpc_inode_setowner(struct inode *inode, void *private) @@ -126,13 +126,14 @@ rpc_close_pipes(struct inode *inode) { struct rpc_inode *rpci = RPC_I(inode); struct rpc_pipe_ops *ops; + int need_release; mutex_lock(&inode->i_mutex); ops = rpci->ops; if (ops != NULL) { LIST_HEAD(free_list); - spin_lock(&inode->i_lock); + need_release = rpci->nreaders != 0 || rpci->nwriters != 0; rpci->nreaders = 0; list_splice_init(&rpci->in_upcall, &free_list); list_splice_init(&rpci->pipe, &free_list); @@ -141,7 +142,7 @@ rpc_close_pipes(struct inode *inode) spin_unlock(&inode->i_lock); rpc_purge_list(rpci, &free_list, ops->destroy_msg, -EPIPE); rpci->nwriters = 0; - if (ops->release_pipe) + if (need_release && ops->release_pipe) ops->release_pipe(inode); cancel_delayed_work_sync(&rpci->queue_timeout); } @@ -169,16 +170,24 @@ static int rpc_pipe_open(struct inode *inode, struct file *filp) { struct rpc_inode *rpci = RPC_I(inode); + int first_open; int res = -ENXIO; mutex_lock(&inode->i_mutex); - if (rpci->ops != NULL) { - if (filp->f_mode & FMODE_READ) - rpci->nreaders ++; - if (filp->f_mode & FMODE_WRITE) - rpci->nwriters ++; - res = 0; + if (rpci->ops == NULL) + goto out; + first_open = rpci->nreaders == 0 && rpci->nwriters == 0; + if (first_open && rpci->ops->open_pipe) { + res = rpci->ops->open_pipe(inode); + if (res) + goto out; } + if (filp->f_mode & FMODE_READ) + rpci->nreaders++; + if (filp->f_mode & FMODE_WRITE) + rpci->nwriters++; + res = 0; +out: mutex_unlock(&inode->i_mutex); return res; } @@ -188,6 +197,7 @@ rpc_pipe_release(struct inode *inode, struct file *filp) { struct rpc_inode *rpci = RPC_I(inode); struct rpc_pipe_msg *msg; + int last_close; mutex_lock(&inode->i_mutex); if (rpci->ops == NULL) @@ -214,7 +224,8 @@ rpc_pipe_release(struct inode *inode, struct file *filp) rpci->ops->destroy_msg, -EAGAIN); } } - if (rpci->ops->release_pipe) + last_close = rpci->nwriters == 0 && rpci->nreaders == 0; + if (last_close && rpci->ops->release_pipe) rpci->ops->release_pipe(inode); out: mutex_unlock(&inode->i_mutex); @@ -396,6 +407,7 @@ enum { RPCAUTH_nfs, RPCAUTH_portmap, RPCAUTH_statd, + RPCAUTH_nfsd4_cb, RPCAUTH_RootEOF }; @@ -429,6 +441,10 @@ static struct rpc_filelist files[] = { .name = "statd", .mode = S_IFDIR | S_IRUGO | S_IXUGO, }, + [RPCAUTH_nfsd4_cb] = { + .name = "nfsd4_cb", + .mode = S_IFDIR | S_IRUGO | S_IXUGO, + }, }; enum { @@ -506,8 +522,6 @@ rpc_get_inode(struct super_block *sb, int mode) if (!inode) return NULL; inode->i_mode = mode; - inode->i_uid = inode->i_gid = 0; - inode->i_blocks = 0; inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; switch(mode & S_IFMT) { case S_IFDIR: @@ -748,7 +762,7 @@ rpc_rmdir(struct dentry *dentry) * @name: name of pipe * @private: private data to associate with the pipe, for the caller's use * @ops: operations defining the behavior of the pipe: upcall, downcall, - * release_pipe, and destroy_msg. + * release_pipe, open_pipe, and destroy_msg. * @flags: rpc_inode flags * * Data is made available for userspace to read by calls to @@ -808,7 +822,7 @@ err_dput: -ENOMEM); goto out; } -EXPORT_SYMBOL(rpc_mkpipe); +EXPORT_SYMBOL_GPL(rpc_mkpipe); /** * rpc_unlink - remove a pipe @@ -839,7 +853,7 @@ rpc_unlink(struct dentry *dentry) dput(parent); return error; } -EXPORT_SYMBOL(rpc_unlink); +EXPORT_SYMBOL_GPL(rpc_unlink); /* * populate the filesystem diff --git a/net/sunrpc/stats.c b/net/sunrpc/stats.c index 50b049c6598..085372ef4fe 100644 --- a/net/sunrpc/stats.c +++ b/net/sunrpc/stats.c @@ -106,7 +106,7 @@ void svc_seq_show(struct seq_file *seq, const struct svc_stat *statp) { seq_putc(seq, '\n'); } } -EXPORT_SYMBOL(svc_seq_show); +EXPORT_SYMBOL_GPL(svc_seq_show); /** * rpc_alloc_iostats - allocate an rpc_iostats structure @@ -249,14 +249,14 @@ svc_proc_register(struct svc_stat *statp, const struct file_operations *fops) { return do_register(statp->program->pg_name, statp, fops); } -EXPORT_SYMBOL(svc_proc_register); +EXPORT_SYMBOL_GPL(svc_proc_register); void svc_proc_unregister(const char *name) { remove_proc_entry(name, proc_net_rpc); } -EXPORT_SYMBOL(svc_proc_unregister); +EXPORT_SYMBOL_GPL(svc_proc_unregister); void rpc_proc_init(void) diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c index 54c98d87684..c51fed4d1af 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c @@ -431,7 +431,7 @@ svc_create(struct svc_program *prog, unsigned int bufsize, { return __svc_create(prog, bufsize, /*npools*/1, family, shutdown); } -EXPORT_SYMBOL(svc_create); +EXPORT_SYMBOL_GPL(svc_create); struct svc_serv * svc_create_pooled(struct svc_program *prog, unsigned int bufsize, @@ -450,7 +450,7 @@ svc_create_pooled(struct svc_program *prog, unsigned int bufsize, return serv; } -EXPORT_SYMBOL(svc_create_pooled); +EXPORT_SYMBOL_GPL(svc_create_pooled); /* * Destroy an RPC service. Should be called with appropriate locking to @@ -492,7 +492,7 @@ svc_destroy(struct svc_serv *serv) kfree(serv->sv_pools); kfree(serv); } -EXPORT_SYMBOL(svc_destroy); +EXPORT_SYMBOL_GPL(svc_destroy); /* * Allocate an RPC server's buffer space. @@ -567,7 +567,7 @@ out_thread: out_enomem: return ERR_PTR(-ENOMEM); } -EXPORT_SYMBOL(svc_prepare_thread); +EXPORT_SYMBOL_GPL(svc_prepare_thread); /* * Choose a pool in which to create a new thread, for svc_set_num_threads @@ -689,7 +689,7 @@ svc_set_num_threads(struct svc_serv *serv, struct svc_pool *pool, int nrservs) return error; } -EXPORT_SYMBOL(svc_set_num_threads); +EXPORT_SYMBOL_GPL(svc_set_num_threads); /* * Called from a server thread as it's exiting. Caller must hold the BKL or @@ -717,7 +717,7 @@ svc_exit_thread(struct svc_rqst *rqstp) if (serv) svc_destroy(serv); } -EXPORT_SYMBOL(svc_exit_thread); +EXPORT_SYMBOL_GPL(svc_exit_thread); #ifdef CONFIG_SUNRPC_REGISTER_V4 @@ -1231,7 +1231,7 @@ err_bad: svc_putnl(resv, ntohl(rpc_stat)); goto sendit; } -EXPORT_SYMBOL(svc_process); +EXPORT_SYMBOL_GPL(svc_process); /* * Return (transport-specific) limit on the rpc payload. diff --git a/net/sunrpc/svc_xprt.c b/net/sunrpc/svc_xprt.c index bf5b5cdafeb..e588df5d6b3 100644 --- a/net/sunrpc/svc_xprt.c +++ b/net/sunrpc/svc_xprt.c @@ -440,7 +440,7 @@ void svc_reserve(struct svc_rqst *rqstp, int space) svc_xprt_enqueue(xprt); } } -EXPORT_SYMBOL(svc_reserve); +EXPORT_SYMBOL_GPL(svc_reserve); static void svc_xprt_release(struct svc_rqst *rqstp) { @@ -448,6 +448,9 @@ static void svc_xprt_release(struct svc_rqst *rqstp) rqstp->rq_xprt->xpt_ops->xpo_release_rqst(rqstp); + kfree(rqstp->rq_deferred); + rqstp->rq_deferred = NULL; + svc_free_res_pages(rqstp); rqstp->rq_res.page_len = 0; rqstp->rq_res.page_base = 0; @@ -498,7 +501,7 @@ void svc_wake_up(struct svc_serv *serv) spin_unlock_bh(&pool->sp_lock); } } -EXPORT_SYMBOL(svc_wake_up); +EXPORT_SYMBOL_GPL(svc_wake_up); int svc_port_is_privileged(struct sockaddr *sin) { @@ -515,8 +518,10 @@ int svc_port_is_privileged(struct sockaddr *sin) } /* - * Make sure that we don't have too many active connections. If we - * have, something must be dropped. + * Make sure that we don't have too many active connections. If we have, + * something must be dropped. It's not clear what will happen if we allow + * "too many" connections, but when dealing with network-facing software, + * we have to code defensively. Here we do that by imposing hard limits. * * There's no point in trying to do random drop here for DoS * prevention. The NFS clients does 1 reconnect in 15 seconds. An @@ -525,19 +530,27 @@ int svc_port_is_privileged(struct sockaddr *sin) * The only somewhat efficient mechanism would be if drop old * connections from the same IP first. But right now we don't even * record the client IP in svc_sock. + * + * single-threaded services that expect a lot of clients will probably + * need to set sv_maxconn to override the default value which is based + * on the number of threads */ static void svc_check_conn_limits(struct svc_serv *serv) { - if (serv->sv_tmpcnt > (serv->sv_nrthreads+3)*20) { + unsigned int limit = serv->sv_maxconn ? serv->sv_maxconn : + (serv->sv_nrthreads+3) * 20; + + if (serv->sv_tmpcnt > limit) { struct svc_xprt *xprt = NULL; spin_lock_bh(&serv->sv_lock); if (!list_empty(&serv->sv_tempsocks)) { if (net_ratelimit()) { /* Try to help the admin */ printk(KERN_NOTICE "%s: too many open " - "connections, consider increasing the " - "number of nfsd threads\n", - serv->sv_name); + "connections, consider increasing %s\n", + serv->sv_name, serv->sv_maxconn ? + "the max number of connections." : + "the number of threads."); } /* * Always select the oldest connection. It's not fair, @@ -730,7 +743,7 @@ int svc_recv(struct svc_rqst *rqstp, long timeout) serv->sv_stats->netcnt++; return len; } -EXPORT_SYMBOL(svc_recv); +EXPORT_SYMBOL_GPL(svc_recv); /* * Drop request @@ -740,7 +753,7 @@ void svc_drop(struct svc_rqst *rqstp) dprintk("svc: xprt %p dropped request\n", rqstp->rq_xprt); svc_xprt_release(rqstp); } -EXPORT_SYMBOL(svc_drop); +EXPORT_SYMBOL_GPL(svc_drop); /* * Return reply to client. @@ -837,6 +850,11 @@ static void svc_age_temp_xprts(unsigned long closure) void svc_delete_xprt(struct svc_xprt *xprt) { struct svc_serv *serv = xprt->xpt_server; + struct svc_deferred_req *dr; + + /* Only do this once */ + if (test_and_set_bit(XPT_DEAD, &xprt->xpt_flags)) + return; dprintk("svc: svc_delete_xprt(%p)\n", xprt); xprt->xpt_ops->xpo_detach(xprt); @@ -851,12 +869,16 @@ void svc_delete_xprt(struct svc_xprt *xprt) * while still attached to a queue, the queue itself * is about to be destroyed (in svc_destroy). */ - if (!test_and_set_bit(XPT_DEAD, &xprt->xpt_flags)) { - BUG_ON(atomic_read(&xprt->xpt_ref.refcount) < 2); - if (test_bit(XPT_TEMP, &xprt->xpt_flags)) - serv->sv_tmpcnt--; + if (test_bit(XPT_TEMP, &xprt->xpt_flags)) + serv->sv_tmpcnt--; + + for (dr = svc_deferred_dequeue(xprt); dr; + dr = svc_deferred_dequeue(xprt)) { svc_xprt_put(xprt); + kfree(dr); } + + svc_xprt_put(xprt); spin_unlock_bh(&serv->sv_lock); } @@ -902,17 +924,19 @@ static void svc_revisit(struct cache_deferred_req *dreq, int too_many) container_of(dreq, struct svc_deferred_req, handle); struct svc_xprt *xprt = dr->xprt; - if (too_many) { + spin_lock(&xprt->xpt_lock); + set_bit(XPT_DEFERRED, &xprt->xpt_flags); + if (too_many || test_bit(XPT_DEAD, &xprt->xpt_flags)) { + spin_unlock(&xprt->xpt_lock); + dprintk("revisit canceled\n"); svc_xprt_put(xprt); kfree(dr); return; } dprintk("revisit queued\n"); dr->xprt = NULL; - spin_lock(&xprt->xpt_lock); list_add(&dr->handle.recent, &xprt->xpt_deferred); spin_unlock(&xprt->xpt_lock); - set_bit(XPT_DEFERRED, &xprt->xpt_flags); svc_xprt_enqueue(xprt); svc_xprt_put(xprt); } diff --git a/net/sunrpc/svcauth.c b/net/sunrpc/svcauth.c index 8a73cbb1605..e64109b02ae 100644 --- a/net/sunrpc/svcauth.c +++ b/net/sunrpc/svcauth.c @@ -57,13 +57,13 @@ svc_authenticate(struct svc_rqst *rqstp, __be32 *authp) rqstp->rq_authop = aops; return aops->accept(rqstp, authp); } -EXPORT_SYMBOL(svc_authenticate); +EXPORT_SYMBOL_GPL(svc_authenticate); int svc_set_client(struct svc_rqst *rqstp) { return rqstp->rq_authop->set_client(rqstp); } -EXPORT_SYMBOL(svc_set_client); +EXPORT_SYMBOL_GPL(svc_set_client); /* A request, which was authenticated, has now executed. * Time to finalise the credentials and verifier @@ -95,7 +95,7 @@ svc_auth_register(rpc_authflavor_t flavor, struct auth_ops *aops) spin_unlock(&authtab_lock); return rv; } -EXPORT_SYMBOL(svc_auth_register); +EXPORT_SYMBOL_GPL(svc_auth_register); void svc_auth_unregister(rpc_authflavor_t flavor) @@ -105,7 +105,7 @@ svc_auth_unregister(rpc_authflavor_t flavor) authtab[flavor] = NULL; spin_unlock(&authtab_lock); } -EXPORT_SYMBOL(svc_auth_unregister); +EXPORT_SYMBOL_GPL(svc_auth_unregister); /************************************************** * 'auth_domains' are stored in a hash table indexed by name. @@ -132,7 +132,7 @@ void auth_domain_put(struct auth_domain *dom) spin_unlock(&auth_domain_lock); } } -EXPORT_SYMBOL(auth_domain_put); +EXPORT_SYMBOL_GPL(auth_domain_put); struct auth_domain * auth_domain_lookup(char *name, struct auth_domain *new) @@ -157,10 +157,10 @@ auth_domain_lookup(char *name, struct auth_domain *new) spin_unlock(&auth_domain_lock); return new; } -EXPORT_SYMBOL(auth_domain_lookup); +EXPORT_SYMBOL_GPL(auth_domain_lookup); struct auth_domain *auth_domain_find(char *name) { return auth_domain_lookup(name, NULL); } -EXPORT_SYMBOL(auth_domain_find); +EXPORT_SYMBOL_GPL(auth_domain_find); diff --git a/net/sunrpc/svcauth_unix.c b/net/sunrpc/svcauth_unix.c index 82240e6127b..5c865e2d299 100644 --- a/net/sunrpc/svcauth_unix.c +++ b/net/sunrpc/svcauth_unix.c @@ -64,7 +64,7 @@ struct auth_domain *unix_domain_find(char *name) rv = auth_domain_lookup(name, &new->h); } } -EXPORT_SYMBOL(unix_domain_find); +EXPORT_SYMBOL_GPL(unix_domain_find); static void svcauth_unix_domain_release(struct auth_domain *dom) { @@ -358,7 +358,7 @@ int auth_unix_add_addr(struct in6_addr *addr, struct auth_domain *dom) else return -ENOMEM; } -EXPORT_SYMBOL(auth_unix_add_addr); +EXPORT_SYMBOL_GPL(auth_unix_add_addr); int auth_unix_forget_old(struct auth_domain *dom) { @@ -370,7 +370,7 @@ int auth_unix_forget_old(struct auth_domain *dom) udom->addr_changes++; return 0; } -EXPORT_SYMBOL(auth_unix_forget_old); +EXPORT_SYMBOL_GPL(auth_unix_forget_old); struct auth_domain *auth_unix_lookup(struct in6_addr *addr) { @@ -395,13 +395,13 @@ struct auth_domain *auth_unix_lookup(struct in6_addr *addr) cache_put(&ipm->h, &ip_map_cache); return rv; } -EXPORT_SYMBOL(auth_unix_lookup); +EXPORT_SYMBOL_GPL(auth_unix_lookup); void svcauth_unix_purge(void) { cache_purge(&ip_map_cache); } -EXPORT_SYMBOL(svcauth_unix_purge); +EXPORT_SYMBOL_GPL(svcauth_unix_purge); static inline struct ip_map * ip_map_cached_get(struct svc_rqst *rqstp) @@ -714,7 +714,7 @@ svcauth_unix_set_client(struct svc_rqst *rqstp) return SVC_OK; } -EXPORT_SYMBOL(svcauth_unix_set_client); +EXPORT_SYMBOL_GPL(svcauth_unix_set_client); static int svcauth_null_accept(struct svc_rqst *rqstp, __be32 *authp) diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c index ef3238d665e..5763e6460fe 100644 --- a/net/sunrpc/svcsock.c +++ b/net/sunrpc/svcsock.c @@ -59,6 +59,7 @@ static void svc_udp_data_ready(struct sock *, int); static int svc_udp_recvfrom(struct svc_rqst *); static int svc_udp_sendto(struct svc_rqst *); static void svc_sock_detach(struct svc_xprt *); +static void svc_tcp_sock_detach(struct svc_xprt *); static void svc_sock_free(struct svc_xprt *); static struct svc_xprt *svc_create_socket(struct svc_serv *, int, @@ -102,7 +103,6 @@ static void svc_reclassify_socket(struct socket *sock) static void svc_release_skb(struct svc_rqst *rqstp) { struct sk_buff *skb = rqstp->rq_xprt_ctxt; - struct svc_deferred_req *dr = rqstp->rq_deferred; if (skb) { struct svc_sock *svsk = @@ -112,10 +112,6 @@ static void svc_release_skb(struct svc_rqst *rqstp) dprintk("svc: service %p, releasing skb %p\n", rqstp, skb); skb_free_datagram(svsk->sk_sk, skb); } - if (dr) { - rqstp->rq_deferred = NULL; - kfree(dr); - } } union svc_pktinfo_u { @@ -289,7 +285,7 @@ svc_sock_names(char *buf, struct svc_serv *serv, char *toclose) return -ENOENT; return len; } -EXPORT_SYMBOL(svc_sock_names); +EXPORT_SYMBOL_GPL(svc_sock_names); /* * Check input queue length @@ -1017,7 +1013,7 @@ static struct svc_xprt_ops svc_tcp_ops = { .xpo_recvfrom = svc_tcp_recvfrom, .xpo_sendto = svc_tcp_sendto, .xpo_release_rqst = svc_release_skb, - .xpo_detach = svc_sock_detach, + .xpo_detach = svc_tcp_sock_detach, .xpo_free = svc_sock_free, .xpo_prep_reply_hdr = svc_tcp_prep_reply_hdr, .xpo_has_wspace = svc_tcp_has_wspace, @@ -1101,7 +1097,7 @@ void svc_sock_update_bufs(struct svc_serv *serv) } spin_unlock_bh(&serv->sv_lock); } -EXPORT_SYMBOL(svc_sock_update_bufs); +EXPORT_SYMBOL_GPL(svc_sock_update_bufs); /* * Initialize socket for RPC use and create svc_sock struct @@ -1287,6 +1283,24 @@ static void svc_sock_detach(struct svc_xprt *xprt) sk->sk_state_change = svsk->sk_ostate; sk->sk_data_ready = svsk->sk_odata; sk->sk_write_space = svsk->sk_owspace; + + if (sk->sk_sleep && waitqueue_active(sk->sk_sleep)) + wake_up_interruptible(sk->sk_sleep); +} + +/* + * Disconnect the socket, and reset the callbacks + */ +static void svc_tcp_sock_detach(struct svc_xprt *xprt) +{ + struct svc_sock *svsk = container_of(xprt, struct svc_sock, sk_xprt); + + dprintk("svc: svc_tcp_sock_detach(%p)\n", svsk); + + svc_sock_detach(xprt); + + if (!test_bit(XPT_LISTENER, &xprt->xpt_flags)) + kernel_sock_shutdown(svsk->sk_sock, SHUT_RDWR); } /* diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c index 79a55d56cc9..406e26de584 100644 --- a/net/sunrpc/xdr.c +++ b/net/sunrpc/xdr.c @@ -28,7 +28,7 @@ xdr_encode_netobj(__be32 *p, const struct xdr_netobj *obj) memcpy(p, obj->data, obj->len); return p + XDR_QUADLEN(obj->len); } -EXPORT_SYMBOL(xdr_encode_netobj); +EXPORT_SYMBOL_GPL(xdr_encode_netobj); __be32 * xdr_decode_netobj(__be32 *p, struct xdr_netobj *obj) @@ -41,7 +41,7 @@ xdr_decode_netobj(__be32 *p, struct xdr_netobj *obj) obj->data = (u8 *) p; return p + XDR_QUADLEN(len); } -EXPORT_SYMBOL(xdr_decode_netobj); +EXPORT_SYMBOL_GPL(xdr_decode_netobj); /** * xdr_encode_opaque_fixed - Encode fixed length opaque data @@ -71,7 +71,7 @@ __be32 *xdr_encode_opaque_fixed(__be32 *p, const void *ptr, unsigned int nbytes) } return p; } -EXPORT_SYMBOL(xdr_encode_opaque_fixed); +EXPORT_SYMBOL_GPL(xdr_encode_opaque_fixed); /** * xdr_encode_opaque - Encode variable length opaque data @@ -86,14 +86,14 @@ __be32 *xdr_encode_opaque(__be32 *p, const void *ptr, unsigned int nbytes) *p++ = htonl(nbytes); return xdr_encode_opaque_fixed(p, ptr, nbytes); } -EXPORT_SYMBOL(xdr_encode_opaque); +EXPORT_SYMBOL_GPL(xdr_encode_opaque); __be32 * xdr_encode_string(__be32 *p, const char *string) { return xdr_encode_array(p, string, strlen(string)); } -EXPORT_SYMBOL(xdr_encode_string); +EXPORT_SYMBOL_GPL(xdr_encode_string); __be32 * xdr_decode_string_inplace(__be32 *p, char **sp, @@ -108,7 +108,7 @@ xdr_decode_string_inplace(__be32 *p, char **sp, *sp = (char *) p; return p + XDR_QUADLEN(len); } -EXPORT_SYMBOL(xdr_decode_string_inplace); +EXPORT_SYMBOL_GPL(xdr_decode_string_inplace); void xdr_encode_pages(struct xdr_buf *xdr, struct page **pages, unsigned int base, @@ -136,7 +136,7 @@ xdr_encode_pages(struct xdr_buf *xdr, struct page **pages, unsigned int base, xdr->buflen += len; xdr->len += len; } -EXPORT_SYMBOL(xdr_encode_pages); +EXPORT_SYMBOL_GPL(xdr_encode_pages); void xdr_inline_pages(struct xdr_buf *xdr, unsigned int offset, @@ -158,7 +158,7 @@ xdr_inline_pages(struct xdr_buf *xdr, unsigned int offset, xdr->buflen += len; } -EXPORT_SYMBOL(xdr_inline_pages); +EXPORT_SYMBOL_GPL(xdr_inline_pages); /* * Helper routines for doing 'memmove' like operations on a struct xdr_buf @@ -428,7 +428,7 @@ xdr_shift_buf(struct xdr_buf *buf, size_t len) { xdr_shrink_bufhead(buf, len); } -EXPORT_SYMBOL(xdr_shift_buf); +EXPORT_SYMBOL_GPL(xdr_shift_buf); /** * xdr_init_encode - Initialize a struct xdr_stream for sending data. @@ -465,7 +465,7 @@ void xdr_init_encode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p) iov->iov_len += len; } } -EXPORT_SYMBOL(xdr_init_encode); +EXPORT_SYMBOL_GPL(xdr_init_encode); /** * xdr_reserve_space - Reserve buffer space for sending @@ -492,7 +492,7 @@ __be32 * xdr_reserve_space(struct xdr_stream *xdr, size_t nbytes) xdr->buf->len += nbytes; return p; } -EXPORT_SYMBOL(xdr_reserve_space); +EXPORT_SYMBOL_GPL(xdr_reserve_space); /** * xdr_write_pages - Insert a list of pages into an XDR buffer for sending @@ -527,7 +527,7 @@ void xdr_write_pages(struct xdr_stream *xdr, struct page **pages, unsigned int b buf->buflen += len; buf->len += len; } -EXPORT_SYMBOL(xdr_write_pages); +EXPORT_SYMBOL_GPL(xdr_write_pages); /** * xdr_init_decode - Initialize an xdr_stream for decoding data. @@ -547,7 +547,7 @@ void xdr_init_decode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p) xdr->p = p; xdr->end = (__be32 *)((char *)iov->iov_base + len); } -EXPORT_SYMBOL(xdr_init_decode); +EXPORT_SYMBOL_GPL(xdr_init_decode); /** * xdr_inline_decode - Retrieve non-page XDR data to decode @@ -569,7 +569,7 @@ __be32 * xdr_inline_decode(struct xdr_stream *xdr, size_t nbytes) xdr->p = q; return p; } -EXPORT_SYMBOL(xdr_inline_decode); +EXPORT_SYMBOL_GPL(xdr_inline_decode); /** * xdr_read_pages - Ensure page-based XDR data to decode is aligned at current pointer position @@ -613,7 +613,7 @@ void xdr_read_pages(struct xdr_stream *xdr, unsigned int len) xdr->p = (__be32 *)((char *)iov->iov_base + padding); xdr->end = (__be32 *)((char *)iov->iov_base + end); } -EXPORT_SYMBOL(xdr_read_pages); +EXPORT_SYMBOL_GPL(xdr_read_pages); /** * xdr_enter_page - decode data from the XDR page @@ -638,7 +638,7 @@ void xdr_enter_page(struct xdr_stream *xdr, unsigned int len) xdr->p = (__be32 *)(kaddr + xdr->buf->page_base); xdr->end = (__be32 *)((char *)xdr->p + len); } -EXPORT_SYMBOL(xdr_enter_page); +EXPORT_SYMBOL_GPL(xdr_enter_page); static struct kvec empty_iov = {.iov_base = NULL, .iov_len = 0}; @@ -650,7 +650,7 @@ xdr_buf_from_iov(struct kvec *iov, struct xdr_buf *buf) buf->page_len = 0; buf->buflen = buf->len = iov->iov_len; } -EXPORT_SYMBOL(xdr_buf_from_iov); +EXPORT_SYMBOL_GPL(xdr_buf_from_iov); /* Sets subbuf to the portion of buf of length len beginning base bytes * from the start of buf. Returns -1 if base of length are out of bounds. */ @@ -699,7 +699,7 @@ xdr_buf_subsegment(struct xdr_buf *buf, struct xdr_buf *subbuf, return -1; return 0; } -EXPORT_SYMBOL(xdr_buf_subsegment); +EXPORT_SYMBOL_GPL(xdr_buf_subsegment); static void __read_bytes_from_xdr_buf(struct xdr_buf *subbuf, void *obj, unsigned int len) { @@ -730,7 +730,7 @@ int read_bytes_from_xdr_buf(struct xdr_buf *buf, unsigned int base, void *obj, u __read_bytes_from_xdr_buf(&subbuf, obj, len); return 0; } -EXPORT_SYMBOL(read_bytes_from_xdr_buf); +EXPORT_SYMBOL_GPL(read_bytes_from_xdr_buf); static void __write_bytes_to_xdr_buf(struct xdr_buf *subbuf, void *obj, unsigned int len) { @@ -774,7 +774,7 @@ xdr_decode_word(struct xdr_buf *buf, unsigned int base, u32 *obj) *obj = ntohl(raw); return 0; } -EXPORT_SYMBOL(xdr_decode_word); +EXPORT_SYMBOL_GPL(xdr_decode_word); int xdr_encode_word(struct xdr_buf *buf, unsigned int base, u32 obj) @@ -783,7 +783,7 @@ xdr_encode_word(struct xdr_buf *buf, unsigned int base, u32 obj) return write_bytes_to_xdr_buf(buf, base, &raw, sizeof(obj)); } -EXPORT_SYMBOL(xdr_encode_word); +EXPORT_SYMBOL_GPL(xdr_encode_word); /* If the netobj starting offset bytes from the start of xdr_buf is contained * entirely in the head or the tail, set object to point to it; otherwise @@ -821,7 +821,7 @@ int xdr_buf_read_netobj(struct xdr_buf *buf, struct xdr_netobj *obj, unsigned in __read_bytes_from_xdr_buf(&subbuf, obj->data, obj->len); return 0; } -EXPORT_SYMBOL(xdr_buf_read_netobj); +EXPORT_SYMBOL_GPL(xdr_buf_read_netobj); /* Returns 0 on success, or else a negative error code. */ static int @@ -1027,7 +1027,7 @@ xdr_decode_array2(struct xdr_buf *buf, unsigned int base, return xdr_xcode_array2(buf, base, desc, 0); } -EXPORT_SYMBOL(xdr_decode_array2); +EXPORT_SYMBOL_GPL(xdr_decode_array2); int xdr_encode_array2(struct xdr_buf *buf, unsigned int base, @@ -1039,7 +1039,7 @@ xdr_encode_array2(struct xdr_buf *buf, unsigned int base, return xdr_xcode_array2(buf, base, desc, 1); } -EXPORT_SYMBOL(xdr_encode_array2); +EXPORT_SYMBOL_GPL(xdr_encode_array2); int xdr_process_buf(struct xdr_buf *buf, unsigned int offset, unsigned int len, @@ -1106,5 +1106,5 @@ xdr_process_buf(struct xdr_buf *buf, unsigned int offset, unsigned int len, out: return ret; } -EXPORT_SYMBOL(xdr_process_buf); +EXPORT_SYMBOL_GPL(xdr_process_buf); diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index c6250d0055d..d1b89820ab4 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -836,7 +836,11 @@ static int unix_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) err = mnt_want_write(nd.path.mnt); if (err) goto out_mknod_dput; + err = security_path_mknod(&nd.path, dentry, mode, 0); + if (err) + goto out_mknod_drop_write; err = vfs_mknod(nd.path.dentry->d_inode, dentry, mode, 0); +out_mknod_drop_write: mnt_drop_write(nd.path.mnt); if (err) goto out_mknod_dput; diff --git a/net/wimax/Kconfig b/net/wimax/Kconfig new file mode 100644 index 00000000000..0bdbb692820 --- /dev/null +++ b/net/wimax/Kconfig @@ -0,0 +1,38 @@ +# +# WiMAX LAN device configuration +# + +menuconfig WIMAX + tristate "WiMAX Wireless Broadband support" + help + + Select to configure support for devices that provide + wireless broadband connectivity using the WiMAX protocol + (IEEE 802.16). + + Please note that most of these devices require signing up + for a service plan with a provider. + + The different WiMAX drivers can be enabled in the menu entry + + Device Drivers > Network device support > WiMAX Wireless + Broadband devices + + If unsure, it is safe to select M (module). + +config WIMAX_DEBUG_LEVEL + int "WiMAX debug level" + depends on WIMAX + default 8 + help + + Select the maximum debug verbosity level to be compiled into + the WiMAX stack code. + + By default, debug messages are disabled at runtime and can + be selectively enabled for different parts of the code using + the sysfs debug-levels file. + + If set at zero, this will compile out all the debug code. + + It is recommended that it is left at 8. diff --git a/net/wimax/Makefile b/net/wimax/Makefile new file mode 100644 index 00000000000..5b80b941c2c --- /dev/null +++ b/net/wimax/Makefile @@ -0,0 +1,13 @@ + +obj-$(CONFIG_WIMAX) += wimax.o + +wimax-y := \ + id-table.o \ + op-msg.o \ + op-reset.o \ + op-rfkill.o \ + stack.o + +wimax-$(CONFIG_DEBUG_FS) += debugfs.o + + diff --git a/net/wimax/debug-levels.h b/net/wimax/debug-levels.h new file mode 100644 index 00000000000..1c29123a3aa --- /dev/null +++ b/net/wimax/debug-levels.h @@ -0,0 +1,42 @@ +/* + * Linux WiMAX Stack + * Debug levels control file for the wimax module + * + * + * Copyright (C) 2007-2008 Intel Corporation <linux-wimax@intel.com> + * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ +#ifndef __debug_levels__h__ +#define __debug_levels__h__ + +/* Maximum compile and run time debug level for all submodules */ +#define D_MODULENAME wimax +#define D_MASTER CONFIG_WIMAX_DEBUG_LEVEL + +#include <linux/wimax/debug.h> + +/* List of all the enabled modules */ +enum d_module { + D_SUBMODULE_DECLARE(debugfs), + D_SUBMODULE_DECLARE(id_table), + D_SUBMODULE_DECLARE(op_msg), + D_SUBMODULE_DECLARE(op_reset), + D_SUBMODULE_DECLARE(op_rfkill), + D_SUBMODULE_DECLARE(stack), +}; + +#endif /* #ifndef __debug_levels__h__ */ diff --git a/net/wimax/debugfs.c b/net/wimax/debugfs.c new file mode 100644 index 00000000000..87cf4430079 --- /dev/null +++ b/net/wimax/debugfs.c @@ -0,0 +1,90 @@ +/* + * Linux WiMAX + * Debugfs support + * + * + * Copyright (C) 2005-2006 Intel Corporation <linux-wimax@intel.com> + * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ +#include <linux/debugfs.h> +#include <linux/wimax.h> +#include "wimax-internal.h" + +#define D_SUBMODULE debugfs +#include "debug-levels.h" + + +/* Debug framework control of debug levels */ +struct d_level D_LEVEL[] = { + D_SUBMODULE_DEFINE(debugfs), + D_SUBMODULE_DEFINE(id_table), + D_SUBMODULE_DEFINE(op_msg), + D_SUBMODULE_DEFINE(op_reset), + D_SUBMODULE_DEFINE(op_rfkill), + D_SUBMODULE_DEFINE(stack), +}; +size_t D_LEVEL_SIZE = ARRAY_SIZE(D_LEVEL); + +#define __debugfs_register(prefix, name, parent) \ +do { \ + result = d_level_register_debugfs(prefix, name, parent); \ + if (result < 0) \ + goto error; \ +} while (0) + + +int wimax_debugfs_add(struct wimax_dev *wimax_dev) +{ + int result; + struct net_device *net_dev = wimax_dev->net_dev; + struct device *dev = net_dev->dev.parent; + struct dentry *dentry; + char buf[128]; + + snprintf(buf, sizeof(buf), "wimax:%s", net_dev->name); + dentry = debugfs_create_dir(buf, NULL); + result = PTR_ERR(dentry); + if (IS_ERR(dentry)) { + if (result == -ENODEV) + result = 0; /* No debugfs support */ + else + dev_err(dev, "Can't create debugfs dentry: %d\n", + result); + goto out; + } + wimax_dev->debugfs_dentry = dentry; + __debugfs_register("wimax_dl_", debugfs, dentry); + __debugfs_register("wimax_dl_", id_table, dentry); + __debugfs_register("wimax_dl_", op_msg, dentry); + __debugfs_register("wimax_dl_", op_reset, dentry); + __debugfs_register("wimax_dl_", op_rfkill, dentry); + __debugfs_register("wimax_dl_", stack, dentry); + result = 0; +out: + return result; + +error: + debugfs_remove_recursive(wimax_dev->debugfs_dentry); + return result; +} + +void wimax_debugfs_rm(struct wimax_dev *wimax_dev) +{ + debugfs_remove_recursive(wimax_dev->debugfs_dentry); +} + + diff --git a/net/wimax/id-table.c b/net/wimax/id-table.c new file mode 100644 index 00000000000..d3b88558682 --- /dev/null +++ b/net/wimax/id-table.c @@ -0,0 +1,142 @@ +/* + * Linux WiMAX + * Mappping of generic netlink family IDs to net devices + * + * + * Copyright (C) 2005-2006 Intel Corporation <linux-wimax@intel.com> + * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * + * We assign a single generic netlink family ID to each device (to + * simplify lookup). + * + * We need a way to map family ID to a wimax_dev pointer. + * + * The idea is to use a very simple lookup. Using a netlink attribute + * with (for example) the interface name implies a heavier search over + * all the network devices; seemed kind of a waste given that we know + * we are looking for a WiMAX device and that most systems will have + * just a single WiMAX adapter. + * + * We put all the WiMAX devices in the system in a linked list and + * match the generic link family ID against the list. + * + * By using a linked list, the case of a single adapter in the system + * becomes (almost) no overhead, while still working for many more. If + * it ever goes beyond two, I'll be surprised. + */ +#include <linux/device.h> +#include <net/genetlink.h> +#include <linux/netdevice.h> +#include <linux/list.h> +#include <linux/wimax.h> +#include "wimax-internal.h" + + +#define D_SUBMODULE id_table +#include "debug-levels.h" + + +static DEFINE_SPINLOCK(wimax_id_table_lock); +static struct list_head wimax_id_table = LIST_HEAD_INIT(wimax_id_table); + + +/* + * wimax_id_table_add - add a gennetlink familiy ID / wimax_dev mapping + * + * @wimax_dev: WiMAX device descriptor to associate to the Generic + * Netlink family ID. + * + * Look for an empty spot in the ID table; if none found, double the + * table's size and get the first spot. + */ +void wimax_id_table_add(struct wimax_dev *wimax_dev) +{ + d_fnstart(3, NULL, "(wimax_dev %p)\n", wimax_dev); + spin_lock(&wimax_id_table_lock); + list_add(&wimax_dev->id_table_node, &wimax_id_table); + spin_unlock(&wimax_id_table_lock); + d_fnend(3, NULL, "(wimax_dev %p)\n", wimax_dev); +} + + +/* + * wimax_get_netdev_by_info - lookup a wimax_dev from the gennetlink info + * + * The generic netlink family ID has been filled out in the + * nlmsghdr->nlmsg_type field, so we pull it from there, look it up in + * the mapping table and reference the wimax_dev. + * + * When done, the reference should be dropped with + * 'dev_put(wimax_dev->net_dev)'. + */ +struct wimax_dev *wimax_dev_get_by_genl_info( + struct genl_info *info, int ifindex) +{ + struct wimax_dev *wimax_dev = NULL; + + d_fnstart(3, NULL, "(info %p ifindex %d)\n", info, ifindex); + spin_lock(&wimax_id_table_lock); + list_for_each_entry(wimax_dev, &wimax_id_table, id_table_node) { + if (wimax_dev->net_dev->ifindex == ifindex) { + dev_hold(wimax_dev->net_dev); + break; + } + } + if (wimax_dev == NULL) + d_printf(1, NULL, "wimax: no devices found with ifindex %d\n", + ifindex); + spin_unlock(&wimax_id_table_lock); + d_fnend(3, NULL, "(info %p ifindex %d) = %p\n", + info, ifindex, wimax_dev); + return wimax_dev; +} + + +/* + * wimax_id_table_rm - Remove a gennetlink familiy ID / wimax_dev mapping + * + * @id: family ID to remove from the table + */ +void wimax_id_table_rm(struct wimax_dev *wimax_dev) +{ + spin_lock(&wimax_id_table_lock); + list_del_init(&wimax_dev->id_table_node); + spin_unlock(&wimax_id_table_lock); +} + + +/* + * Release the gennetlink family id / mapping table + * + * On debug, verify that the table is empty upon removal. + */ +void wimax_id_table_release(void) +{ +#ifndef CONFIG_BUG + return; +#endif + struct wimax_dev *wimax_dev; + + spin_lock(&wimax_id_table_lock); + list_for_each_entry(wimax_dev, &wimax_id_table, id_table_node) { + printk(KERN_ERR "BUG: %s wimax_dev %p ifindex %d not cleared\n", + __func__, wimax_dev, wimax_dev->net_dev->ifindex); + WARN_ON(1); + } + spin_unlock(&wimax_id_table_lock); +} diff --git a/net/wimax/op-msg.c b/net/wimax/op-msg.c new file mode 100644 index 00000000000..cb3b4ad5368 --- /dev/null +++ b/net/wimax/op-msg.c @@ -0,0 +1,421 @@ +/* + * Linux WiMAX + * Generic messaging interface between userspace and driver/device + * + * + * Copyright (C) 2007-2008 Intel Corporation <linux-wimax@intel.com> + * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * + * This implements a direct communication channel between user space and + * the driver/device, by which free form messages can be sent back and + * forth. + * + * This is intended for device-specific features, vendor quirks, etc. + * + * See include/net/wimax.h + * + * GENERIC NETLINK ENCODING AND CAPACITY + * + * A destination "pipe name" is added to each message; it is up to the + * drivers to assign or use those names (if using them at all). + * + * Messages are encoded as a binary netlink attribute using nla_put() + * using type NLA_UNSPEC (as some versions of libnl still in + * deployment don't yet understand NLA_BINARY). + * + * The maximum capacity of this transport is PAGESIZE per message (so + * the actual payload will be bit smaller depending on the + * netlink/generic netlink attributes and headers). + * + * RECEPTION OF MESSAGES + * + * When a message is received from user space, it is passed verbatim + * to the driver calling wimax_dev->op_msg_from_user(). The return + * value from this function is passed back to user space as an ack + * over the generic netlink protocol. + * + * The stack doesn't do any processing or interpretation of these + * messages. + * + * SENDING MESSAGES + * + * Messages can be sent with wimax_msg(). + * + * If the message delivery needs to happen on a different context to + * that of its creation, wimax_msg_alloc() can be used to get a + * pointer to the message that can be delivered later on with + * wimax_msg_send(). + * + * ROADMAP + * + * wimax_gnl_doit_msg_from_user() Process a message from user space + * wimax_dev_get_by_genl_info() + * wimax_dev->op_msg_from_user() Delivery of message to the driver + * + * wimax_msg() Send a message to user space + * wimax_msg_alloc() + * wimax_msg_send() + */ +#include <linux/device.h> +#include <net/genetlink.h> +#include <linux/netdevice.h> +#include <linux/wimax.h> +#include <linux/security.h> +#include "wimax-internal.h" + + +#define D_SUBMODULE op_msg +#include "debug-levels.h" + + +/** + * wimax_msg_alloc - Create a new skb for sending a message to userspace + * + * @wimax_dev: WiMAX device descriptor + * @pipe_name: "named pipe" the message will be sent to + * @msg: pointer to the message data to send + * @size: size of the message to send (in bytes), including the header. + * @gfp_flags: flags for memory allocation. + * + * Returns: %0 if ok, negative errno code on error + * + * Description: + * + * Allocates an skb that will contain the message to send to user + * space over the messaging pipe and initializes it, copying the + * payload. + * + * Once this call is done, you can deliver it with + * wimax_msg_send(). + * + * IMPORTANT: + * + * Don't use skb_push()/skb_pull()/skb_reserve() on the skb, as + * wimax_msg_send() depends on skb->data being placed at the + * beginning of the user message. + */ +struct sk_buff *wimax_msg_alloc(struct wimax_dev *wimax_dev, + const char *pipe_name, + const void *msg, size_t size, + gfp_t gfp_flags) +{ + int result; + struct device *dev = wimax_dev->net_dev->dev.parent; + size_t msg_size; + void *genl_msg; + struct sk_buff *skb; + + msg_size = nla_total_size(size) + + nla_total_size(sizeof(u32)) + + (pipe_name ? nla_total_size(strlen(pipe_name)) : 0); + result = -ENOMEM; + skb = genlmsg_new(msg_size, gfp_flags); + if (skb == NULL) + goto error_new; + genl_msg = genlmsg_put(skb, 0, 0, &wimax_gnl_family, + 0, WIMAX_GNL_OP_MSG_TO_USER); + if (genl_msg == NULL) { + dev_err(dev, "no memory to create generic netlink message\n"); + goto error_genlmsg_put; + } + result = nla_put_u32(skb, WIMAX_GNL_MSG_IFIDX, + wimax_dev->net_dev->ifindex); + if (result < 0) { + dev_err(dev, "no memory to add ifindex attribute\n"); + goto error_nla_put; + } + if (pipe_name) { + result = nla_put_string(skb, WIMAX_GNL_MSG_PIPE_NAME, + pipe_name); + if (result < 0) { + dev_err(dev, "no memory to add pipe_name attribute\n"); + goto error_nla_put; + } + } + result = nla_put(skb, WIMAX_GNL_MSG_DATA, size, msg); + if (result < 0) { + dev_err(dev, "no memory to add payload in attribute\n"); + goto error_nla_put; + } + genlmsg_end(skb, genl_msg); + return skb; + +error_nla_put: +error_genlmsg_put: +error_new: + nlmsg_free(skb); + return ERR_PTR(result); + +} +EXPORT_SYMBOL_GPL(wimax_msg_alloc); + + +/** + * wimax_msg_data_len - Return a pointer and size of a message's payload + * + * @msg: Pointer to a message created with wimax_msg_alloc() + * @size: Pointer to where to store the message's size + * + * Returns the pointer to the message data. + */ +const void *wimax_msg_data_len(struct sk_buff *msg, size_t *size) +{ + struct nlmsghdr *nlh = (void *) msg->head; + struct nlattr *nla; + + nla = nlmsg_find_attr(nlh, sizeof(struct genlmsghdr), + WIMAX_GNL_MSG_DATA); + if (nla == NULL) { + printk(KERN_ERR "Cannot find attribute WIMAX_GNL_MSG_DATA\n"); + return NULL; + } + *size = nla_len(nla); + return nla_data(nla); +} +EXPORT_SYMBOL_GPL(wimax_msg_data_len); + + +/** + * wimax_msg_data - Return a pointer to a message's payload + * + * @msg: Pointer to a message created with wimax_msg_alloc() + */ +const void *wimax_msg_data(struct sk_buff *msg) +{ + struct nlmsghdr *nlh = (void *) msg->head; + struct nlattr *nla; + + nla = nlmsg_find_attr(nlh, sizeof(struct genlmsghdr), + WIMAX_GNL_MSG_DATA); + if (nla == NULL) { + printk(KERN_ERR "Cannot find attribute WIMAX_GNL_MSG_DATA\n"); + return NULL; + } + return nla_data(nla); +} +EXPORT_SYMBOL_GPL(wimax_msg_data); + + +/** + * wimax_msg_len - Return a message's payload length + * + * @msg: Pointer to a message created with wimax_msg_alloc() + */ +ssize_t wimax_msg_len(struct sk_buff *msg) +{ + struct nlmsghdr *nlh = (void *) msg->head; + struct nlattr *nla; + + nla = nlmsg_find_attr(nlh, sizeof(struct genlmsghdr), + WIMAX_GNL_MSG_DATA); + if (nla == NULL) { + printk(KERN_ERR "Cannot find attribute WIMAX_GNL_MSG_DATA\n"); + return -EINVAL; + } + return nla_len(nla); +} +EXPORT_SYMBOL_GPL(wimax_msg_len); + + +/** + * wimax_msg_send - Send a pre-allocated message to user space + * + * @wimax_dev: WiMAX device descriptor + * + * @skb: &struct sk_buff returned by wimax_msg_alloc(). Note the + * ownership of @skb is transferred to this function. + * + * Returns: 0 if ok, < 0 errno code on error + * + * Description: + * + * Sends a free-form message that was preallocated with + * wimax_msg_alloc() and filled up. + * + * Assumes that once you pass an skb to this function for sending, it + * owns it and will release it when done (on success). + * + * IMPORTANT: + * + * Don't use skb_push()/skb_pull()/skb_reserve() on the skb, as + * wimax_msg_send() depends on skb->data being placed at the + * beginning of the user message. + */ +int wimax_msg_send(struct wimax_dev *wimax_dev, struct sk_buff *skb) +{ + int result; + struct device *dev = wimax_dev->net_dev->dev.parent; + void *msg = skb->data; + size_t size = skb->len; + might_sleep(); + + d_printf(1, dev, "CTX: wimax msg, %zu bytes\n", size); + d_dump(2, dev, msg, size); + result = genlmsg_multicast(skb, 0, wimax_gnl_mcg.id, GFP_KERNEL); + d_printf(1, dev, "CTX: genl multicast result %d\n", result); + if (result == -ESRCH) /* Nobody connected, ignore it */ + result = 0; /* btw, the skb is freed already */ + return result; +} +EXPORT_SYMBOL_GPL(wimax_msg_send); + + +/** + * wimax_msg - Send a message to user space + * + * @wimax_dev: WiMAX device descriptor (properly referenced) + * @pipe_name: "named pipe" the message will be sent to + * @buf: pointer to the message to send. + * @size: size of the buffer pointed to by @buf (in bytes). + * @gfp_flags: flags for memory allocation. + * + * Returns: %0 if ok, negative errno code on error. + * + * Description: + * + * Sends a free-form message to user space on the device @wimax_dev. + * + * NOTES: + * + * Once the @skb is given to this function, who will own it and will + * release it when done (unless it returns error). + */ +int wimax_msg(struct wimax_dev *wimax_dev, const char *pipe_name, + const void *buf, size_t size, gfp_t gfp_flags) +{ + int result = -ENOMEM; + struct sk_buff *skb; + + skb = wimax_msg_alloc(wimax_dev, pipe_name, buf, size, gfp_flags); + if (skb == NULL) + goto error_msg_new; + result = wimax_msg_send(wimax_dev, skb); +error_msg_new: + return result; +} +EXPORT_SYMBOL_GPL(wimax_msg); + + +static const +struct nla_policy wimax_gnl_msg_policy[WIMAX_GNL_ATTR_MAX + 1] = { + [WIMAX_GNL_MSG_IFIDX] = { + .type = NLA_U32, + }, + [WIMAX_GNL_MSG_DATA] = { + .type = NLA_UNSPEC, /* libnl doesn't grok BINARY yet */ + }, +}; + + +/* + * Relays a message from user space to the driver + * + * The skb is passed to the driver-specific function with the netlink + * and generic netlink headers already stripped. + * + * This call will block while handling/relaying the message. + */ +static +int wimax_gnl_doit_msg_from_user(struct sk_buff *skb, struct genl_info *info) +{ + int result, ifindex; + struct wimax_dev *wimax_dev; + struct device *dev; + struct nlmsghdr *nlh = info->nlhdr; + char *pipe_name; + void *msg_buf; + size_t msg_len; + + might_sleep(); + d_fnstart(3, NULL, "(skb %p info %p)\n", skb, info); + result = -ENODEV; + if (info->attrs[WIMAX_GNL_MSG_IFIDX] == NULL) { + printk(KERN_ERR "WIMAX_GNL_MSG_FROM_USER: can't find IFIDX " + "attribute\n"); + goto error_no_wimax_dev; + } + ifindex = nla_get_u32(info->attrs[WIMAX_GNL_MSG_IFIDX]); + wimax_dev = wimax_dev_get_by_genl_info(info, ifindex); + if (wimax_dev == NULL) + goto error_no_wimax_dev; + dev = wimax_dev_to_dev(wimax_dev); + + /* Unpack arguments */ + result = -EINVAL; + if (info->attrs[WIMAX_GNL_MSG_DATA] == NULL) { + dev_err(dev, "WIMAX_GNL_MSG_FROM_USER: can't find MSG_DATA " + "attribute\n"); + goto error_no_data; + } + msg_buf = nla_data(info->attrs[WIMAX_GNL_MSG_DATA]); + msg_len = nla_len(info->attrs[WIMAX_GNL_MSG_DATA]); + + if (info->attrs[WIMAX_GNL_MSG_PIPE_NAME] == NULL) + pipe_name = NULL; + else { + struct nlattr *attr = info->attrs[WIMAX_GNL_MSG_PIPE_NAME]; + size_t attr_len = nla_len(attr); + /* libnl-1.1 does not yet support NLA_NUL_STRING */ + result = -ENOMEM; + pipe_name = kstrndup(nla_data(attr), attr_len + 1, GFP_KERNEL); + if (pipe_name == NULL) + goto error_alloc; + pipe_name[attr_len] = 0; + } + mutex_lock(&wimax_dev->mutex); + result = wimax_dev_is_ready(wimax_dev); + if (result < 0) + goto error_not_ready; + result = -ENOSYS; + if (wimax_dev->op_msg_from_user == NULL) + goto error_noop; + + d_printf(1, dev, + "CRX: nlmsghdr len %u type %u flags 0x%04x seq 0x%x pid %u\n", + nlh->nlmsg_len, nlh->nlmsg_type, nlh->nlmsg_flags, + nlh->nlmsg_seq, nlh->nlmsg_pid); + d_printf(1, dev, "CRX: wimax message %zu bytes\n", msg_len); + d_dump(2, dev, msg_buf, msg_len); + + result = wimax_dev->op_msg_from_user(wimax_dev, pipe_name, + msg_buf, msg_len, info); +error_noop: +error_not_ready: + mutex_unlock(&wimax_dev->mutex); +error_alloc: + kfree(pipe_name); +error_no_data: + dev_put(wimax_dev->net_dev); +error_no_wimax_dev: + d_fnend(3, NULL, "(skb %p info %p) = %d\n", skb, info, result); + return result; +} + + +/* + * Generic Netlink glue + */ + +struct genl_ops wimax_gnl_msg_from_user = { + .cmd = WIMAX_GNL_OP_MSG_FROM_USER, + .flags = GENL_ADMIN_PERM, + .policy = wimax_gnl_msg_policy, + .doit = wimax_gnl_doit_msg_from_user, + .dumpit = NULL, +}; + diff --git a/net/wimax/op-reset.c b/net/wimax/op-reset.c new file mode 100644 index 00000000000..ca269178c4d --- /dev/null +++ b/net/wimax/op-reset.c @@ -0,0 +1,143 @@ +/* + * Linux WiMAX + * Implement and export a method for resetting a WiMAX device + * + * + * Copyright (C) 2008 Intel Corporation <linux-wimax@intel.com> + * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * + * This implements a simple synchronous call to reset a WiMAX device. + * + * Resets aim at being warm, keeping the device handles active; + * however, when that fails, it falls back to a cold reset (that will + * disconnect and reconnect the device). + */ + +#include <net/wimax.h> +#include <net/genetlink.h> +#include <linux/wimax.h> +#include <linux/security.h> +#include "wimax-internal.h" + +#define D_SUBMODULE op_reset +#include "debug-levels.h" + + +/** + * wimax_reset - Reset a WiMAX device + * + * @wimax_dev: WiMAX device descriptor + * + * Returns: + * + * %0 if ok and a warm reset was done (the device still exists in + * the system). + * + * -%ENODEV if a cold/bus reset had to be done (device has + * disconnected and reconnected, so current handle is not valid + * any more). + * + * -%EINVAL if the device is not even registered. + * + * Any other negative error code shall be considered as + * non-recoverable. + * + * Description: + * + * Called when wanting to reset the device for any reason. Device is + * taken back to power on status. + * + * This call blocks; on succesful return, the device has completed the + * reset process and is ready to operate. + */ +int wimax_reset(struct wimax_dev *wimax_dev) +{ + int result = -EINVAL; + struct device *dev = wimax_dev_to_dev(wimax_dev); + enum wimax_st state; + + might_sleep(); + d_fnstart(3, dev, "(wimax_dev %p)\n", wimax_dev); + mutex_lock(&wimax_dev->mutex); + dev_hold(wimax_dev->net_dev); + state = wimax_dev->state; + mutex_unlock(&wimax_dev->mutex); + + if (state >= WIMAX_ST_DOWN) { + mutex_lock(&wimax_dev->mutex_reset); + result = wimax_dev->op_reset(wimax_dev); + mutex_unlock(&wimax_dev->mutex_reset); + } + dev_put(wimax_dev->net_dev); + + d_fnend(3, dev, "(wimax_dev %p) = %d\n", wimax_dev, result); + return result; +} +EXPORT_SYMBOL(wimax_reset); + + +static const +struct nla_policy wimax_gnl_reset_policy[WIMAX_GNL_ATTR_MAX + 1] = { + [WIMAX_GNL_RESET_IFIDX] = { + .type = NLA_U32, + }, +}; + + +/* + * Exporting to user space over generic netlink + * + * Parse the reset command from user space, return error code. + * + * No attributes. + */ +static +int wimax_gnl_doit_reset(struct sk_buff *skb, struct genl_info *info) +{ + int result, ifindex; + struct wimax_dev *wimax_dev; + struct device *dev; + + d_fnstart(3, NULL, "(skb %p info %p)\n", skb, info); + result = -ENODEV; + if (info->attrs[WIMAX_GNL_RESET_IFIDX] == NULL) { + printk(KERN_ERR "WIMAX_GNL_OP_RFKILL: can't find IFIDX " + "attribute\n"); + goto error_no_wimax_dev; + } + ifindex = nla_get_u32(info->attrs[WIMAX_GNL_RESET_IFIDX]); + wimax_dev = wimax_dev_get_by_genl_info(info, ifindex); + if (wimax_dev == NULL) + goto error_no_wimax_dev; + dev = wimax_dev_to_dev(wimax_dev); + /* Execute the operation and send the result back to user space */ + result = wimax_reset(wimax_dev); + dev_put(wimax_dev->net_dev); +error_no_wimax_dev: + d_fnend(3, NULL, "(skb %p info %p) = %d\n", skb, info, result); + return result; +} + + +struct genl_ops wimax_gnl_reset = { + .cmd = WIMAX_GNL_OP_RESET, + .flags = GENL_ADMIN_PERM, + .policy = wimax_gnl_reset_policy, + .doit = wimax_gnl_doit_reset, + .dumpit = NULL, +}; diff --git a/net/wimax/op-rfkill.c b/net/wimax/op-rfkill.c new file mode 100644 index 00000000000..8745bac173f --- /dev/null +++ b/net/wimax/op-rfkill.c @@ -0,0 +1,532 @@ +/* + * Linux WiMAX + * RF-kill framework integration + * + * + * Copyright (C) 2008 Intel Corporation <linux-wimax@intel.com> + * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * + * This integrates into the Linux Kernel rfkill susbystem so that the + * drivers just have to do the bare minimal work, which is providing a + * method to set the software RF-Kill switch and to report changes in + * the software and hardware switch status. + * + * A non-polled generic rfkill device is embedded into the WiMAX + * subsystem's representation of a device. + * + * FIXME: Need polled support? use a timer or add the implementation + * to the stack. + * + * All device drivers have to do is after wimax_dev_init(), call + * wimax_report_rfkill_hw() and wimax_report_rfkill_sw() to update + * initial state and then every time it changes. See wimax.h:struct + * wimax_dev for more information. + * + * ROADMAP + * + * wimax_gnl_doit_rfkill() User space calling wimax_rfkill() + * wimax_rfkill() Kernel calling wimax_rfkill() + * __wimax_rf_toggle_radio() + * + * wimax_rfkill_toggle_radio() RF-Kill subsytem calling + * __wimax_rf_toggle_radio() + * + * __wimax_rf_toggle_radio() + * wimax_dev->op_rfkill_sw_toggle() Driver backend + * __wimax_state_change() + * + * wimax_report_rfkill_sw() Driver reports state change + * __wimax_state_change() + * + * wimax_report_rfkill_hw() Driver reports state change + * __wimax_state_change() + * + * wimax_rfkill_add() Initialize/shutdown rfkill support + * wimax_rfkill_rm() [called by wimax_dev_add/rm()] + */ + +#include <net/wimax.h> +#include <net/genetlink.h> +#include <linux/wimax.h> +#include <linux/security.h> +#include <linux/rfkill.h> +#include <linux/input.h> +#include "wimax-internal.h" + +#define D_SUBMODULE op_rfkill +#include "debug-levels.h" + +#ifdef CONFIG_RFKILL + + +/** + * wimax_report_rfkill_hw - Reports changes in the hardware RF switch + * + * @wimax_dev: WiMAX device descriptor + * + * @state: New state of the RF Kill switch. %WIMAX_RF_ON radio on, + * %WIMAX_RF_OFF radio off. + * + * When the device detects a change in the state of thehardware RF + * switch, it must call this function to let the WiMAX kernel stack + * know that the state has changed so it can be properly propagated. + * + * The WiMAX stack caches the state (the driver doesn't need to). As + * well, as the change is propagated it will come back as a request to + * change the software state to mirror the hardware state. + * + * If the device doesn't have a hardware kill switch, just report + * it on initialization as always on (%WIMAX_RF_ON, radio on). + */ +void wimax_report_rfkill_hw(struct wimax_dev *wimax_dev, + enum wimax_rf_state state) +{ + int result; + struct device *dev = wimax_dev_to_dev(wimax_dev); + enum wimax_st wimax_state; + enum rfkill_state rfkill_state; + + d_fnstart(3, dev, "(wimax_dev %p state %u)\n", wimax_dev, state); + BUG_ON(state == WIMAX_RF_QUERY); + BUG_ON(state != WIMAX_RF_ON && state != WIMAX_RF_OFF); + + mutex_lock(&wimax_dev->mutex); + result = wimax_dev_is_ready(wimax_dev); + if (result < 0) + goto error_not_ready; + + if (state != wimax_dev->rf_hw) { + wimax_dev->rf_hw = state; + rfkill_state = state == WIMAX_RF_ON ? + RFKILL_STATE_OFF : RFKILL_STATE_ON; + if (wimax_dev->rf_hw == WIMAX_RF_ON + && wimax_dev->rf_sw == WIMAX_RF_ON) + wimax_state = WIMAX_ST_READY; + else + wimax_state = WIMAX_ST_RADIO_OFF; + __wimax_state_change(wimax_dev, wimax_state); + input_report_key(wimax_dev->rfkill_input, KEY_WIMAX, + rfkill_state); + } +error_not_ready: + mutex_unlock(&wimax_dev->mutex); + d_fnend(3, dev, "(wimax_dev %p state %u) = void [%d]\n", + wimax_dev, state, result); +} +EXPORT_SYMBOL_GPL(wimax_report_rfkill_hw); + + +/** + * wimax_report_rfkill_sw - Reports changes in the software RF switch + * + * @wimax_dev: WiMAX device descriptor + * + * @state: New state of the RF kill switch. %WIMAX_RF_ON radio on, + * %WIMAX_RF_OFF radio off. + * + * Reports changes in the software RF switch state to the the WiMAX + * stack. + * + * The main use is during initialization, so the driver can query the + * device for its current software radio kill switch state and feed it + * to the system. + * + * On the side, the device does not change the software state by + * itself. In practice, this can happen, as the device might decide to + * switch (in software) the radio off for different reasons. + */ +void wimax_report_rfkill_sw(struct wimax_dev *wimax_dev, + enum wimax_rf_state state) +{ + int result; + struct device *dev = wimax_dev_to_dev(wimax_dev); + enum wimax_st wimax_state; + + d_fnstart(3, dev, "(wimax_dev %p state %u)\n", wimax_dev, state); + BUG_ON(state == WIMAX_RF_QUERY); + BUG_ON(state != WIMAX_RF_ON && state != WIMAX_RF_OFF); + + mutex_lock(&wimax_dev->mutex); + result = wimax_dev_is_ready(wimax_dev); + if (result < 0) + goto error_not_ready; + + if (state != wimax_dev->rf_sw) { + wimax_dev->rf_sw = state; + if (wimax_dev->rf_hw == WIMAX_RF_ON + && wimax_dev->rf_sw == WIMAX_RF_ON) + wimax_state = WIMAX_ST_READY; + else + wimax_state = WIMAX_ST_RADIO_OFF; + __wimax_state_change(wimax_dev, wimax_state); + } +error_not_ready: + mutex_unlock(&wimax_dev->mutex); + d_fnend(3, dev, "(wimax_dev %p state %u) = void [%d]\n", + wimax_dev, state, result); +} +EXPORT_SYMBOL_GPL(wimax_report_rfkill_sw); + + +/* + * Callback for the RF Kill toggle operation + * + * This function is called by: + * + * - The rfkill subsystem when the RF-Kill key is pressed in the + * hardware and the driver notifies through + * wimax_report_rfkill_hw(). The rfkill subsystem ends up calling back + * here so the software RF Kill switch state is changed to reflect + * the hardware switch state. + * + * - When the user sets the state through sysfs' rfkill/state file + * + * - When the user calls wimax_rfkill(). + * + * This call blocks! + * + * WARNING! When we call rfkill_unregister(), this will be called with + * state 0! + * + * WARNING: wimax_dev must be locked + */ +static +int __wimax_rf_toggle_radio(struct wimax_dev *wimax_dev, + enum wimax_rf_state state) +{ + int result = 0; + struct device *dev = wimax_dev_to_dev(wimax_dev); + enum wimax_st wimax_state; + + might_sleep(); + d_fnstart(3, dev, "(wimax_dev %p state %u)\n", wimax_dev, state); + if (wimax_dev->rf_sw == state) + goto out_no_change; + if (wimax_dev->op_rfkill_sw_toggle != NULL) + result = wimax_dev->op_rfkill_sw_toggle(wimax_dev, state); + else if (state == WIMAX_RF_OFF) /* No op? can't turn off */ + result = -ENXIO; + else /* No op? can turn on */ + result = 0; /* should never happen tho */ + if (result >= 0) { + result = 0; + wimax_dev->rf_sw = state; + wimax_state = state == WIMAX_RF_ON ? + WIMAX_ST_READY : WIMAX_ST_RADIO_OFF; + __wimax_state_change(wimax_dev, wimax_state); + } +out_no_change: + d_fnend(3, dev, "(wimax_dev %p state %u) = %d\n", + wimax_dev, state, result); + return result; +} + + +/* + * Translate from rfkill state to wimax state + * + * NOTE: Special state handling rules here + * + * Just pretend the call didn't happen if we are in a state where + * we know for sure it cannot be handled (WIMAX_ST_DOWN or + * __WIMAX_ST_QUIESCING). rfkill() needs it to register and + * unregister, as it will run this path. + * + * NOTE: This call will block until the operation is completed. + */ +static +int wimax_rfkill_toggle_radio(void *data, enum rfkill_state state) +{ + int result; + struct wimax_dev *wimax_dev = data; + struct device *dev = wimax_dev_to_dev(wimax_dev); + enum wimax_rf_state rf_state; + + d_fnstart(3, dev, "(wimax_dev %p state %u)\n", wimax_dev, state); + switch (state) { + case RFKILL_STATE_ON: + rf_state = WIMAX_RF_OFF; + break; + case RFKILL_STATE_OFF: + rf_state = WIMAX_RF_ON; + break; + default: + BUG(); + } + mutex_lock(&wimax_dev->mutex); + if (wimax_dev->state <= __WIMAX_ST_QUIESCING) + result = 0; /* just pretend it didn't happen */ + else + result = __wimax_rf_toggle_radio(wimax_dev, rf_state); + mutex_unlock(&wimax_dev->mutex); + d_fnend(3, dev, "(wimax_dev %p state %u) = %d\n", + wimax_dev, state, result); + return result; +} + + +/** + * wimax_rfkill - Set the software RF switch state for a WiMAX device + * + * @wimax_dev: WiMAX device descriptor + * + * @state: New RF state. + * + * Returns: + * + * >= 0 toggle state if ok, < 0 errno code on error. The toggle state + * is returned as a bitmap, bit 0 being the hardware RF state, bit 1 + * the software RF state. + * + * 0 means disabled (%WIMAX_RF_ON, radio on), 1 means enabled radio + * off (%WIMAX_RF_OFF). + * + * Description: + * + * Called by the user when he wants to request the WiMAX radio to be + * switched on (%WIMAX_RF_ON) or off (%WIMAX_RF_OFF). With + * %WIMAX_RF_QUERY, just the current state is returned. + * + * NOTE: + * + * This call will block until the operation is complete. + */ +int wimax_rfkill(struct wimax_dev *wimax_dev, enum wimax_rf_state state) +{ + int result; + struct device *dev = wimax_dev_to_dev(wimax_dev); + + d_fnstart(3, dev, "(wimax_dev %p state %u)\n", wimax_dev, state); + mutex_lock(&wimax_dev->mutex); + result = wimax_dev_is_ready(wimax_dev); + if (result < 0) + goto error_not_ready; + switch (state) { + case WIMAX_RF_ON: + case WIMAX_RF_OFF: + result = __wimax_rf_toggle_radio(wimax_dev, state); + if (result < 0) + goto error; + break; + case WIMAX_RF_QUERY: + break; + default: + result = -EINVAL; + goto error; + } + result = wimax_dev->rf_sw << 1 | wimax_dev->rf_hw; +error: +error_not_ready: + mutex_unlock(&wimax_dev->mutex); + d_fnend(3, dev, "(wimax_dev %p state %u) = %d\n", + wimax_dev, state, result); + return result; +} +EXPORT_SYMBOL(wimax_rfkill); + + +/* + * Register a new WiMAX device's RF Kill support + * + * WARNING: wimax_dev->mutex must be unlocked + */ +int wimax_rfkill_add(struct wimax_dev *wimax_dev) +{ + int result; + struct rfkill *rfkill; + struct input_dev *input_dev; + struct device *dev = wimax_dev_to_dev(wimax_dev); + + d_fnstart(3, dev, "(wimax_dev %p)\n", wimax_dev); + /* Initialize RF Kill */ + result = -ENOMEM; + rfkill = rfkill_allocate(dev, RFKILL_TYPE_WIMAX); + if (rfkill == NULL) + goto error_rfkill_allocate; + wimax_dev->rfkill = rfkill; + + rfkill->name = wimax_dev->name; + rfkill->state = RFKILL_STATE_OFF; + rfkill->data = wimax_dev; + rfkill->toggle_radio = wimax_rfkill_toggle_radio; + rfkill->user_claim_unsupported = 1; + + /* Initialize the input device for the hw key */ + input_dev = input_allocate_device(); + if (input_dev == NULL) + goto error_input_allocate; + wimax_dev->rfkill_input = input_dev; + d_printf(1, dev, "rfkill %p input %p\n", rfkill, input_dev); + + input_dev->name = wimax_dev->name; + /* FIXME: get a real device bus ID and stuff? do we care? */ + input_dev->id.bustype = BUS_HOST; + input_dev->id.vendor = 0xffff; + input_dev->evbit[0] = BIT(EV_KEY); + set_bit(KEY_WIMAX, input_dev->keybit); + + /* Register both */ + result = input_register_device(wimax_dev->rfkill_input); + if (result < 0) + goto error_input_register; + result = rfkill_register(wimax_dev->rfkill); + if (result < 0) + goto error_rfkill_register; + + /* If there is no SW toggle op, SW RFKill is always on */ + if (wimax_dev->op_rfkill_sw_toggle == NULL) + wimax_dev->rf_sw = WIMAX_RF_ON; + + d_fnend(3, dev, "(wimax_dev %p) = 0\n", wimax_dev); + return 0; + + /* if rfkill_register() suceeds, can't use rfkill_free() any + * more, only rfkill_unregister() [it owns the refcount]; with + * the input device we have the same issue--hence the if. */ +error_rfkill_register: + input_unregister_device(wimax_dev->rfkill_input); + wimax_dev->rfkill_input = NULL; +error_input_register: + if (wimax_dev->rfkill_input) + input_free_device(wimax_dev->rfkill_input); +error_input_allocate: + rfkill_free(wimax_dev->rfkill); +error_rfkill_allocate: + d_fnend(3, dev, "(wimax_dev %p) = %d\n", wimax_dev, result); + return result; +} + + +/* + * Deregister a WiMAX device's RF Kill support + * + * Ick, we can't call rfkill_free() after rfkill_unregister()...oh + * well. + * + * WARNING: wimax_dev->mutex must be unlocked + */ +void wimax_rfkill_rm(struct wimax_dev *wimax_dev) +{ + struct device *dev = wimax_dev_to_dev(wimax_dev); + d_fnstart(3, dev, "(wimax_dev %p)\n", wimax_dev); + rfkill_unregister(wimax_dev->rfkill); /* frees */ + input_unregister_device(wimax_dev->rfkill_input); + d_fnend(3, dev, "(wimax_dev %p)\n", wimax_dev); +} + + +#else /* #ifdef CONFIG_RFKILL */ + +void wimax_report_rfkill_hw(struct wimax_dev *wimax_dev, + enum wimax_rf_state state) +{ +} +EXPORT_SYMBOL_GPL(wimax_report_rfkill_hw); + +void wimax_report_rfkill_sw(struct wimax_dev *wimax_dev, + enum wimax_rf_state state) +{ +} +EXPORT_SYMBOL_GPL(wimax_report_rfkill_sw); + +int wimax_rfkill(struct wimax_dev *wimax_dev, + enum wimax_rf_state state) +{ + return WIMAX_RF_ON << 1 | WIMAX_RF_ON; +} +EXPORT_SYMBOL_GPL(wimax_rfkill); + +int wimax_rfkill_add(struct wimax_dev *wimax_dev) +{ + return 0; +} + +void wimax_rfkill_rm(struct wimax_dev *wimax_dev) +{ +} + +#endif /* #ifdef CONFIG_RFKILL */ + + +/* + * Exporting to user space over generic netlink + * + * Parse the rfkill command from user space, return a combination + * value that describe the states of the different toggles. + * + * Only one attribute: the new state requested (on, off or no change, + * just query). + */ + +static const +struct nla_policy wimax_gnl_rfkill_policy[WIMAX_GNL_ATTR_MAX + 1] = { + [WIMAX_GNL_RFKILL_IFIDX] = { + .type = NLA_U32, + }, + [WIMAX_GNL_RFKILL_STATE] = { + .type = NLA_U32 /* enum wimax_rf_state */ + }, +}; + + +static +int wimax_gnl_doit_rfkill(struct sk_buff *skb, struct genl_info *info) +{ + int result, ifindex; + struct wimax_dev *wimax_dev; + struct device *dev; + enum wimax_rf_state new_state; + + d_fnstart(3, NULL, "(skb %p info %p)\n", skb, info); + result = -ENODEV; + if (info->attrs[WIMAX_GNL_RFKILL_IFIDX] == NULL) { + printk(KERN_ERR "WIMAX_GNL_OP_RFKILL: can't find IFIDX " + "attribute\n"); + goto error_no_wimax_dev; + } + ifindex = nla_get_u32(info->attrs[WIMAX_GNL_RFKILL_IFIDX]); + wimax_dev = wimax_dev_get_by_genl_info(info, ifindex); + if (wimax_dev == NULL) + goto error_no_wimax_dev; + dev = wimax_dev_to_dev(wimax_dev); + result = -EINVAL; + if (info->attrs[WIMAX_GNL_RFKILL_STATE] == NULL) { + dev_err(dev, "WIMAX_GNL_RFKILL: can't find RFKILL_STATE " + "attribute\n"); + goto error_no_pid; + } + new_state = nla_get_u32(info->attrs[WIMAX_GNL_RFKILL_STATE]); + + /* Execute the operation and send the result back to user space */ + result = wimax_rfkill(wimax_dev, new_state); +error_no_pid: + dev_put(wimax_dev->net_dev); +error_no_wimax_dev: + d_fnend(3, NULL, "(skb %p info %p) = %d\n", skb, info, result); + return result; +} + + +struct genl_ops wimax_gnl_rfkill = { + .cmd = WIMAX_GNL_OP_RFKILL, + .flags = GENL_ADMIN_PERM, + .policy = wimax_gnl_rfkill_policy, + .doit = wimax_gnl_doit_rfkill, + .dumpit = NULL, +}; + diff --git a/net/wimax/stack.c b/net/wimax/stack.c new file mode 100644 index 00000000000..d4da92f8981 --- /dev/null +++ b/net/wimax/stack.c @@ -0,0 +1,599 @@ +/* + * Linux WiMAX + * Initialization, addition and removal of wimax devices + * + * + * Copyright (C) 2005-2006 Intel Corporation <linux-wimax@intel.com> + * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * + * This implements: + * + * - basic life cycle of 'struct wimax_dev' [wimax_dev_*()]; on + * addition/registration initialize all subfields and allocate + * generic netlink resources for user space communication. On + * removal/unregistration, undo all that. + * + * - device state machine [wimax_state_change()] and support to send + * reports to user space when the state changes + * [wimax_gnl_re_state_change*()]. + * + * See include/net/wimax.h for rationales and design. + * + * ROADMAP + * + * [__]wimax_state_change() Called by drivers to update device's state + * wimax_gnl_re_state_change_alloc() + * wimax_gnl_re_state_change_send() + * + * wimax_dev_init() Init a device + * wimax_dev_add() Register + * wimax_rfkill_add() + * wimax_gnl_add() Register all the generic netlink resources. + * wimax_id_table_add() + * wimax_dev_rm() Unregister + * wimax_id_table_rm() + * wimax_gnl_rm() + * wimax_rfkill_rm() + */ +#include <linux/device.h> +#include <net/genetlink.h> +#include <linux/netdevice.h> +#include <linux/wimax.h> +#include "wimax-internal.h" + + +#define D_SUBMODULE stack +#include "debug-levels.h" + +/* + * Authoritative source for the RE_STATE_CHANGE attribute policy + * + * We don't really use it here, but /me likes to keep the definition + * close to where the data is generated. + */ +/* +static const +struct nla_policy wimax_gnl_re_status_change[WIMAX_GNL_ATTR_MAX + 1] = { + [WIMAX_GNL_STCH_STATE_OLD] = { .type = NLA_U8 }, + [WIMAX_GNL_STCH_STATE_NEW] = { .type = NLA_U8 }, +}; +*/ + + +/* + * Allocate a Report State Change message + * + * @header: save it, you need it for _send() + * + * Creates and fills a basic state change message; different code + * paths can then add more attributes to the message as needed. + * + * Use wimax_gnl_re_state_change_send() to send the returned skb. + * + * Returns: skb with the genl message if ok, IS_ERR() ptr on error + * with an errno code. + */ +static +struct sk_buff *wimax_gnl_re_state_change_alloc( + struct wimax_dev *wimax_dev, + enum wimax_st new_state, enum wimax_st old_state, + void **header) +{ + int result; + struct device *dev = wimax_dev_to_dev(wimax_dev); + void *data; + struct sk_buff *report_skb; + + d_fnstart(3, dev, "(wimax_dev %p new_state %u old_state %u)\n", + wimax_dev, new_state, old_state); + result = -ENOMEM; + report_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (report_skb == NULL) { + dev_err(dev, "RE_STCH: can't create message\n"); + goto error_new; + } + data = genlmsg_put(report_skb, 0, wimax_gnl_mcg.id, &wimax_gnl_family, + 0, WIMAX_GNL_RE_STATE_CHANGE); + if (data == NULL) { + dev_err(dev, "RE_STCH: can't put data into message\n"); + goto error_put; + } + *header = data; + + result = nla_put_u8(report_skb, WIMAX_GNL_STCH_STATE_OLD, old_state); + if (result < 0) { + dev_err(dev, "RE_STCH: Error adding OLD attr: %d\n", result); + goto error_put; + } + result = nla_put_u8(report_skb, WIMAX_GNL_STCH_STATE_NEW, new_state); + if (result < 0) { + dev_err(dev, "RE_STCH: Error adding NEW attr: %d\n", result); + goto error_put; + } + result = nla_put_u32(report_skb, WIMAX_GNL_STCH_IFIDX, + wimax_dev->net_dev->ifindex); + if (result < 0) { + dev_err(dev, "RE_STCH: Error adding IFINDEX attribute\n"); + goto error_put; + } + d_fnend(3, dev, "(wimax_dev %p new_state %u old_state %u) = %p\n", + wimax_dev, new_state, old_state, report_skb); + return report_skb; + +error_put: + nlmsg_free(report_skb); +error_new: + d_fnend(3, dev, "(wimax_dev %p new_state %u old_state %u) = %d\n", + wimax_dev, new_state, old_state, result); + return ERR_PTR(result); +} + + +/* + * Send a Report State Change message (as created with _alloc). + * + * @report_skb: as returned by wimax_gnl_re_state_change_alloc() + * @header: as returned by wimax_gnl_re_state_change_alloc() + * + * Returns: 0 if ok, < 0 errno code on error. + * + * If the message is NULL, pretend it didn't happen. + */ +static +int wimax_gnl_re_state_change_send( + struct wimax_dev *wimax_dev, struct sk_buff *report_skb, + void *header) +{ + int result = 0; + struct device *dev = wimax_dev_to_dev(wimax_dev); + d_fnstart(3, dev, "(wimax_dev %p report_skb %p)\n", + wimax_dev, report_skb); + if (report_skb == NULL) + goto out; + genlmsg_end(report_skb, header); + result = genlmsg_multicast(report_skb, 0, wimax_gnl_mcg.id, GFP_KERNEL); + if (result == -ESRCH) /* Nobody connected, ignore it */ + result = 0; /* btw, the skb is freed already */ + if (result < 0) { + dev_err(dev, "RE_STCH: Error sending: %d\n", result); + nlmsg_free(report_skb); + } +out: + d_fnend(3, dev, "(wimax_dev %p report_skb %p) = %d\n", + wimax_dev, report_skb, result); + return result; +} + + +static +void __check_new_state(enum wimax_st old_state, enum wimax_st new_state, + unsigned allowed_states_bm) +{ + if (WARN_ON(((1 << new_state) & allowed_states_bm) == 0)) { + printk(KERN_ERR "SW BUG! Forbidden state change %u -> %u\n", + old_state, new_state); + } +} + + +/* + * Set the current state of a WiMAX device [unlocking version of + * wimax_state_change(). + */ +void __wimax_state_change(struct wimax_dev *wimax_dev, enum wimax_st new_state) +{ + struct device *dev = wimax_dev_to_dev(wimax_dev); + enum wimax_st old_state = wimax_dev->state; + struct sk_buff *stch_skb; + void *header; + + d_fnstart(3, dev, "(wimax_dev %p new_state %u [old %u])\n", + wimax_dev, new_state, old_state); + + if (WARN_ON(new_state >= __WIMAX_ST_INVALID)) { + dev_err(dev, "SW BUG: requesting invalid state %u\n", + new_state); + goto out; + } + if (old_state == new_state) + goto out; + header = NULL; /* gcc complains? can't grok why */ + stch_skb = wimax_gnl_re_state_change_alloc( + wimax_dev, new_state, old_state, &header); + + /* Verify the state transition and do exit-from-state actions */ + switch (old_state) { + case __WIMAX_ST_NULL: + __check_new_state(old_state, new_state, + 1 << WIMAX_ST_DOWN); + break; + case WIMAX_ST_DOWN: + __check_new_state(old_state, new_state, + 1 << __WIMAX_ST_QUIESCING + | 1 << WIMAX_ST_UNINITIALIZED + | 1 << WIMAX_ST_RADIO_OFF); + break; + case __WIMAX_ST_QUIESCING: + __check_new_state(old_state, new_state, 1 << WIMAX_ST_DOWN); + break; + case WIMAX_ST_UNINITIALIZED: + __check_new_state(old_state, new_state, + 1 << __WIMAX_ST_QUIESCING + | 1 << WIMAX_ST_RADIO_OFF); + break; + case WIMAX_ST_RADIO_OFF: + __check_new_state(old_state, new_state, + 1 << __WIMAX_ST_QUIESCING + | 1 << WIMAX_ST_READY); + break; + case WIMAX_ST_READY: + __check_new_state(old_state, new_state, + 1 << __WIMAX_ST_QUIESCING + | 1 << WIMAX_ST_RADIO_OFF + | 1 << WIMAX_ST_SCANNING + | 1 << WIMAX_ST_CONNECTING + | 1 << WIMAX_ST_CONNECTED); + break; + case WIMAX_ST_SCANNING: + __check_new_state(old_state, new_state, + 1 << __WIMAX_ST_QUIESCING + | 1 << WIMAX_ST_RADIO_OFF + | 1 << WIMAX_ST_READY + | 1 << WIMAX_ST_CONNECTING + | 1 << WIMAX_ST_CONNECTED); + break; + case WIMAX_ST_CONNECTING: + __check_new_state(old_state, new_state, + 1 << __WIMAX_ST_QUIESCING + | 1 << WIMAX_ST_RADIO_OFF + | 1 << WIMAX_ST_READY + | 1 << WIMAX_ST_SCANNING + | 1 << WIMAX_ST_CONNECTED); + break; + case WIMAX_ST_CONNECTED: + __check_new_state(old_state, new_state, + 1 << __WIMAX_ST_QUIESCING + | 1 << WIMAX_ST_RADIO_OFF + | 1 << WIMAX_ST_READY); + netif_tx_disable(wimax_dev->net_dev); + netif_carrier_off(wimax_dev->net_dev); + break; + case __WIMAX_ST_INVALID: + default: + dev_err(dev, "SW BUG: wimax_dev %p is in unknown state %u\n", + wimax_dev, wimax_dev->state); + WARN_ON(1); + goto out; + } + + /* Execute the actions of entry to the new state */ + switch (new_state) { + case __WIMAX_ST_NULL: + dev_err(dev, "SW BUG: wimax_dev %p entering NULL state " + "from %u\n", wimax_dev, wimax_dev->state); + WARN_ON(1); /* Nobody can enter this state */ + break; + case WIMAX_ST_DOWN: + break; + case __WIMAX_ST_QUIESCING: + break; + case WIMAX_ST_UNINITIALIZED: + break; + case WIMAX_ST_RADIO_OFF: + break; + case WIMAX_ST_READY: + break; + case WIMAX_ST_SCANNING: + break; + case WIMAX_ST_CONNECTING: + break; + case WIMAX_ST_CONNECTED: + netif_carrier_on(wimax_dev->net_dev); + netif_wake_queue(wimax_dev->net_dev); + break; + case __WIMAX_ST_INVALID: + default: + BUG(); + } + __wimax_state_set(wimax_dev, new_state); + if (stch_skb) + wimax_gnl_re_state_change_send(wimax_dev, stch_skb, header); +out: + d_fnend(3, dev, "(wimax_dev %p new_state %u [old %u]) = void\n", + wimax_dev, new_state, old_state); + return; +} + + +/** + * wimax_state_change - Set the current state of a WiMAX device + * + * @wimax_dev: WiMAX device descriptor (properly referenced) + * @new_state: New state to switch to + * + * This implements the state changes for the wimax devices. It will + * + * - verify that the state transition is legal (for now it'll just + * print a warning if not) according to the table in + * linux/wimax.h's documentation for 'enum wimax_st'. + * + * - perform the actions needed for leaving the current state and + * whichever are needed for entering the new state. + * + * - issue a report to user space indicating the new state (and an + * optional payload with information about the new state). + * + * NOTE: @wimax_dev must be locked + */ +void wimax_state_change(struct wimax_dev *wimax_dev, enum wimax_st new_state) +{ + mutex_lock(&wimax_dev->mutex); + __wimax_state_change(wimax_dev, new_state); + mutex_unlock(&wimax_dev->mutex); + return; +} +EXPORT_SYMBOL_GPL(wimax_state_change); + + +/** + * wimax_state_get() - Return the current state of a WiMAX device + * + * @wimax_dev: WiMAX device descriptor + * + * Returns: Current state of the device according to its driver. + */ +enum wimax_st wimax_state_get(struct wimax_dev *wimax_dev) +{ + enum wimax_st state; + mutex_lock(&wimax_dev->mutex); + state = wimax_dev->state; + mutex_unlock(&wimax_dev->mutex); + return state; +} +EXPORT_SYMBOL_GPL(wimax_state_get); + + +/** + * wimax_dev_init - initialize a newly allocated instance + * + * @wimax_dev: WiMAX device descriptor to initialize. + * + * Initializes fields of a freshly allocated @wimax_dev instance. This + * function assumes that after allocation, the memory occupied by + * @wimax_dev was zeroed. + */ +void wimax_dev_init(struct wimax_dev *wimax_dev) +{ + INIT_LIST_HEAD(&wimax_dev->id_table_node); + __wimax_state_set(wimax_dev, WIMAX_ST_UNINITIALIZED); + mutex_init(&wimax_dev->mutex); + mutex_init(&wimax_dev->mutex_reset); +} +EXPORT_SYMBOL_GPL(wimax_dev_init); + +/* + * This extern is declared here because it's easier to keep track -- + * both declarations are a list of the same + */ +extern struct genl_ops + wimax_gnl_msg_from_user, + wimax_gnl_reset, + wimax_gnl_rfkill; + +static +struct genl_ops *wimax_gnl_ops[] = { + &wimax_gnl_msg_from_user, + &wimax_gnl_reset, + &wimax_gnl_rfkill, +}; + + +static +size_t wimax_addr_scnprint(char *addr_str, size_t addr_str_size, + unsigned char *addr, size_t addr_len) +{ + unsigned cnt, total; + for (total = cnt = 0; cnt < addr_len; cnt++) + total += scnprintf(addr_str + total, addr_str_size - total, + "%02x%c", addr[cnt], + cnt == addr_len - 1 ? '\0' : ':'); + return total; +} + + +/** + * wimax_dev_add - Register a new WiMAX device + * + * @wimax_dev: WiMAX device descriptor (as embedded in your @net_dev's + * priv data). You must have called wimax_dev_init() on it before. + * + * @net_dev: net device the @wimax_dev is associated with. The + * function expects SET_NETDEV_DEV() and register_netdev() were + * already called on it. + * + * Registers the new WiMAX device, sets up the user-kernel control + * interface (generic netlink) and common WiMAX infrastructure. + * + * Note that the parts that will allow interaction with user space are + * setup at the very end, when the rest is in place, as once that + * happens, the driver might get user space control requests via + * netlink or from debugfs that might translate into calls into + * wimax_dev->op_*(). + */ +int wimax_dev_add(struct wimax_dev *wimax_dev, struct net_device *net_dev) +{ + int result; + struct device *dev = net_dev->dev.parent; + char addr_str[32]; + + d_fnstart(3, dev, "(wimax_dev %p net_dev %p)\n", wimax_dev, net_dev); + + /* Do the RFKILL setup before locking, as RFKILL will call + * into our functions. */ + wimax_dev->net_dev = net_dev; + result = wimax_rfkill_add(wimax_dev); + if (result < 0) + goto error_rfkill_add; + + /* Set up user-space interaction */ + mutex_lock(&wimax_dev->mutex); + wimax_id_table_add(wimax_dev); + result = wimax_debugfs_add(wimax_dev); + if (result < 0) { + dev_err(dev, "cannot initialize debugfs: %d\n", + result); + goto error_debugfs_add; + } + + __wimax_state_set(wimax_dev, WIMAX_ST_DOWN); + mutex_unlock(&wimax_dev->mutex); + + wimax_addr_scnprint(addr_str, sizeof(addr_str), + net_dev->dev_addr, net_dev->addr_len); + dev_err(dev, "WiMAX interface %s (%s) ready\n", + net_dev->name, addr_str); + d_fnend(3, dev, "(wimax_dev %p net_dev %p) = 0\n", wimax_dev, net_dev); + return 0; + +error_debugfs_add: + wimax_id_table_rm(wimax_dev); + mutex_unlock(&wimax_dev->mutex); + wimax_rfkill_rm(wimax_dev); +error_rfkill_add: + d_fnend(3, dev, "(wimax_dev %p net_dev %p) = %d\n", + wimax_dev, net_dev, result); + return result; +} +EXPORT_SYMBOL_GPL(wimax_dev_add); + + +/** + * wimax_dev_rm - Unregister an existing WiMAX device + * + * @wimax_dev: WiMAX device descriptor + * + * Unregisters a WiMAX device previously registered for use with + * wimax_add_rm(). + * + * IMPORTANT! Must call before calling unregister_netdev(). + * + * After this function returns, you will not get any more user space + * control requests (via netlink or debugfs) and thus to wimax_dev->ops. + * + * Reentrancy control is ensured by setting the state to + * %__WIMAX_ST_QUIESCING. rfkill operations coming through + * wimax_*rfkill*() will be stopped by the quiescing state; ops coming + * from the rfkill subsystem will be stopped by the support being + * removed by wimax_rfkill_rm(). + */ +void wimax_dev_rm(struct wimax_dev *wimax_dev) +{ + d_fnstart(3, NULL, "(wimax_dev %p)\n", wimax_dev); + + mutex_lock(&wimax_dev->mutex); + __wimax_state_change(wimax_dev, __WIMAX_ST_QUIESCING); + wimax_debugfs_rm(wimax_dev); + wimax_id_table_rm(wimax_dev); + __wimax_state_change(wimax_dev, WIMAX_ST_DOWN); + mutex_unlock(&wimax_dev->mutex); + wimax_rfkill_rm(wimax_dev); + d_fnend(3, NULL, "(wimax_dev %p) = void\n", wimax_dev); +} +EXPORT_SYMBOL_GPL(wimax_dev_rm); + +struct genl_family wimax_gnl_family = { + .id = GENL_ID_GENERATE, + .name = "WiMAX", + .version = WIMAX_GNL_VERSION, + .hdrsize = 0, + .maxattr = WIMAX_GNL_ATTR_MAX, +}; + +struct genl_multicast_group wimax_gnl_mcg = { + .name = "msg", +}; + + + +/* Shutdown the wimax stack */ +static +int __init wimax_subsys_init(void) +{ + int result, cnt; + + d_fnstart(4, NULL, "()\n"); + snprintf(wimax_gnl_family.name, sizeof(wimax_gnl_family.name), + "WiMAX"); + result = genl_register_family(&wimax_gnl_family); + if (unlikely(result < 0)) { + printk(KERN_ERR "cannot register generic netlink family: %d\n", + result); + goto error_register_family; + } + + for (cnt = 0; cnt < ARRAY_SIZE(wimax_gnl_ops); cnt++) { + result = genl_register_ops(&wimax_gnl_family, + wimax_gnl_ops[cnt]); + d_printf(4, NULL, "registering generic netlink op code " + "%u: %d\n", wimax_gnl_ops[cnt]->cmd, result); + if (unlikely(result < 0)) { + printk(KERN_ERR "cannot register generic netlink op " + "code %u: %d\n", + wimax_gnl_ops[cnt]->cmd, result); + goto error_register_ops; + } + } + + result = genl_register_mc_group(&wimax_gnl_family, &wimax_gnl_mcg); + if (result < 0) + goto error_mc_group; + d_fnend(4, NULL, "() = 0\n"); + return 0; + +error_mc_group: +error_register_ops: + for (cnt--; cnt >= 0; cnt--) + genl_unregister_ops(&wimax_gnl_family, + wimax_gnl_ops[cnt]); + genl_unregister_family(&wimax_gnl_family); +error_register_family: + d_fnend(4, NULL, "() = %d\n", result); + return result; + +} +module_init(wimax_subsys_init); + + +/* Shutdown the wimax stack */ +static +void __exit wimax_subsys_exit(void) +{ + int cnt; + wimax_id_table_release(); + genl_unregister_mc_group(&wimax_gnl_family, &wimax_gnl_mcg); + for (cnt = ARRAY_SIZE(wimax_gnl_ops) - 1; cnt >= 0; cnt--) + genl_unregister_ops(&wimax_gnl_family, + wimax_gnl_ops[cnt]); + genl_unregister_family(&wimax_gnl_family); +} +module_exit(wimax_subsys_exit); + +MODULE_AUTHOR("Intel Corporation <linux-wimax@intel.com>"); +MODULE_DESCRIPTION("Linux WiMAX stack"); +MODULE_LICENSE("GPL"); + diff --git a/net/wimax/wimax-internal.h b/net/wimax/wimax-internal.h new file mode 100644 index 00000000000..1e743d21485 --- /dev/null +++ b/net/wimax/wimax-internal.h @@ -0,0 +1,91 @@ +/* + * Linux WiMAX + * Internal API for kernel space WiMAX stack + * + * + * Copyright (C) 2007 Intel Corporation <linux-wimax@intel.com> + * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * + * This header file is for declarations and definitions internal to + * the WiMAX stack. For public APIs and documentation, see + * include/net/wimax.h and include/linux/wimax.h. + */ + +#ifndef __WIMAX_INTERNAL_H__ +#define __WIMAX_INTERNAL_H__ +#ifdef __KERNEL__ + +#include <linux/device.h> +#include <net/wimax.h> + + +/* + * Decide if a (locked) device is ready for use + * + * Before using the device structure, it must be locked + * (wimax_dev->mutex). As well, most operations need to call this + * function to check if the state is the right one. + * + * An error value will be returned if the state is not the right + * one. In that case, the caller should not attempt to use the device + * and just unlock it. + */ +static inline __must_check +int wimax_dev_is_ready(struct wimax_dev *wimax_dev) +{ + if (wimax_dev->state == __WIMAX_ST_NULL) + return -EINVAL; /* Device is not even registered! */ + if (wimax_dev->state == WIMAX_ST_DOWN) + return -ENOMEDIUM; + if (wimax_dev->state == __WIMAX_ST_QUIESCING) + return -ESHUTDOWN; + return 0; +} + + +static inline +void __wimax_state_set(struct wimax_dev *wimax_dev, enum wimax_st state) +{ + wimax_dev->state = state; +} +extern void __wimax_state_change(struct wimax_dev *, enum wimax_st); + +#ifdef CONFIG_DEBUG_FS +extern int wimax_debugfs_add(struct wimax_dev *); +extern void wimax_debugfs_rm(struct wimax_dev *); +#else +static inline int wimax_debugfs_add(struct wimax_dev *wimax_dev) +{ + return 0; +} +static inline void wimax_debugfs_rm(struct wimax_dev *wimax_dev) {} +#endif + +extern void wimax_id_table_add(struct wimax_dev *); +extern struct wimax_dev *wimax_dev_get_by_genl_info(struct genl_info *, int); +extern void wimax_id_table_rm(struct wimax_dev *); +extern void wimax_id_table_release(void); + +extern int wimax_rfkill_add(struct wimax_dev *); +extern void wimax_rfkill_rm(struct wimax_dev *); + +extern struct genl_family wimax_gnl_family; +extern struct genl_multicast_group wimax_gnl_mcg; + +#endif /* #ifdef __KERNEL__ */ +#endif /* #ifndef __WIMAX_INTERNAL_H__ */ diff --git a/net/xfrm/xfrm_proc.c b/net/xfrm/xfrm_proc.c index 284eaef1dbf..a2adb51849a 100644 --- a/net/xfrm/xfrm_proc.c +++ b/net/xfrm/xfrm_proc.c @@ -44,27 +44,14 @@ static struct snmp_mib xfrm_mib_list[] = { SNMP_MIB_SENTINEL }; -static unsigned long -fold_field(void *mib[], int offt) -{ - unsigned long res = 0; - int i; - - for_each_possible_cpu(i) { - res += *(((unsigned long *)per_cpu_ptr(mib[0], i)) + offt); - res += *(((unsigned long *)per_cpu_ptr(mib[1], i)) + offt); - } - return res; -} - static int xfrm_statistics_seq_show(struct seq_file *seq, void *v) { struct net *net = seq->private; int i; for (i=0; xfrm_mib_list[i].name; i++) seq_printf(seq, "%-24s\t%lu\n", xfrm_mib_list[i].name, - fold_field((void **)net->mib.xfrm_statistics, - xfrm_mib_list[i].entry)); + snmp_fold_field((void **)net->mib.xfrm_statistics, + xfrm_mib_list[i].entry)); return 0; } |