int pfq_setsockopt(struct socket *sock, int level, int optname, char __user * optval, #if(LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,31)) unsigned #endif int optlen) { struct pfq_sock *so = pfq_sk(sock->sk); bool found = true; if (so == NULL) return -EINVAL; switch(optname) { case Q_SO_ENABLE: { unsigned long addr; int err = 0; if (optlen != sizeof(addr)) return -EINVAL; if (copy_from_user(&addr, optval, optlen)) return -EFAULT; err = pfq_shared_queue_enable(so, addr); if (err < 0) { printk(KERN_INFO "[PFQ|%d] enable error!\n", so->id.value); return err; } return 0; } break; case Q_SO_DISABLE: { int err = 0; size_t n; for(n = 0; n < so->tx_opt.num_queues; n++) { if (so->tx_opt.queue[n].task) { pr_devel("[PFQ|%d] stopping Tx[%zu] thread@%p\n", so->id.value, n, so->tx_opt.queue[n].task); kthread_stop(so->tx_opt.queue[n].task); so->tx_opt.queue[n].task = NULL; } } err = pfq_shared_queue_disable(so); if (err < 0) { printk(KERN_INFO "[PFQ|%d] disable error!\n", so->id.value); return err; } } break; case Q_SO_GROUP_BIND: { struct pfq_binding bind; pfq_gid_t gid; if (optlen != sizeof(struct pfq_binding)) return -EINVAL; if (copy_from_user(&bind, optval, optlen)) return -EFAULT; gid.value = bind.gid; if (!pfq_has_joined_group(gid, so->id)) { printk(KERN_INFO "[PFQ|%d] add bind: gid=%d not joined!\n", so->id.value, bind.gid); return -EACCES; } rcu_read_lock(); if (!dev_get_by_index_rcu(sock_net(&so->sk), bind.if_index)) { rcu_read_unlock(); printk(KERN_INFO "[PFQ|%d] bind: invalid if_index=%d!\n", so->id.value, bind.if_index); return -EACCES; } rcu_read_unlock(); pfq_devmap_update(map_set, bind.if_index, bind.hw_queue, gid); } break; case Q_SO_GROUP_UNBIND: { struct pfq_binding bind; pfq_gid_t gid; if (optlen != sizeof(struct pfq_binding)) return -EINVAL; if (copy_from_user(&bind, optval, optlen)) return -EFAULT; gid.value = bind.gid; if (!pfq_has_joined_group(gid, so->id)) { printk(KERN_INFO "[PFQ|%d] remove bind: gid=%d not joined!\n", so->id.value, bind.gid); return -EACCES; } rcu_read_lock(); if (!dev_get_by_index_rcu(sock_net(&so->sk), bind.if_index)) { rcu_read_unlock(); printk(KERN_INFO "[PFQ|%d] unbind: invalid if_index=%d\n", so->id.value, bind.if_index); return -EPERM; } rcu_read_unlock(); pfq_devmap_update(map_reset, bind.if_index, bind.hw_queue, gid); } break; case Q_SO_EGRESS_BIND: { struct pfq_binding info; if (optlen != sizeof(info)) return -EINVAL; if (copy_from_user(&info, optval, optlen)) return -EFAULT; rcu_read_lock(); if (!dev_get_by_index_rcu(sock_net(&so->sk), info.if_index)) { rcu_read_unlock(); printk(KERN_INFO "[PFQ|%d] egress bind: invalid if_index=%d\n", so->id.value, info.if_index); return -EPERM; } rcu_read_unlock(); if (info.hw_queue < -1) { printk(KERN_INFO "[PFQ|%d] egress bind: invalid queue=%d\n", so->id.value, info.hw_queue); return -EPERM; } so->egress_type = pfq_endpoint_device; so->egress_index = info.if_index; so->egress_queue = info.hw_queue; pr_devel("[PFQ|%d] egress bind: device if_index=%d hw_queue=%d\n", so->id.value, so->egress_index, so->egress_queue); } break; case Q_SO_EGRESS_UNBIND: { so->egress_type = pfq_endpoint_socket; so->egress_index = 0; so->egress_queue = 0; pr_devel("[PFQ|%d] egress unbind.\n", so->id.value); } break; case Q_SO_SET_RX_TSTAMP: { int tstamp; if (optlen != sizeof(so->rx_opt.tstamp)) return -EINVAL; if (copy_from_user(&tstamp, optval, optlen)) return -EFAULT; tstamp = tstamp ? 1 : 0; so->rx_opt.tstamp = tstamp; pr_devel("[PFQ|%d] timestamp enabled.\n", so->id.value); } break; case Q_SO_SET_RX_CAPLEN: { typeof(so->rx_opt.caplen) caplen; if (optlen != sizeof(caplen)) return -EINVAL; if (copy_from_user(&caplen, optval, optlen)) return -EFAULT; if (caplen > (size_t)cap_len) { printk(KERN_INFO "[PFQ|%d] invalid caplen=%zu (max %d)\n", so->id.value, caplen, cap_len); return -EPERM; } so->rx_opt.caplen = caplen; so->rx_opt.slot_size = Q_MPDB_QUEUE_SLOT_SIZE(so->rx_opt.caplen); pr_devel("[PFQ|%d] caplen=%zu, slot_size=%zu\n", so->id.value, so->rx_opt.caplen, so->rx_opt.slot_size); } break; case Q_SO_SET_RX_SLOTS: { typeof(so->rx_opt.queue_size) slots; if (optlen != sizeof(slots)) return -EINVAL; if (copy_from_user(&slots, optval, optlen)) return -EFAULT; if (slots > (size_t)max_queue_slots) { printk(KERN_INFO "[PFQ|%d] invalid Rx slots=%zu (max %d)\n", so->id.value, slots, max_queue_slots); return -EPERM; } so->rx_opt.queue_size = slots; pr_devel("[PFQ|%d] rx_queue slots=%zu\n", so->id.value, so->rx_opt.queue_size); } break; case Q_SO_SET_TX_SLOTS: { typeof (so->tx_opt.queue_size) slots; if (optlen != sizeof(slots)) return -EINVAL; if (copy_from_user(&slots, optval, optlen)) return -EFAULT; if (slots > (size_t)max_queue_slots) { printk(KERN_INFO "[PFQ|%d] invalid Tx slots=%zu (max %d)\n", so->id.value, slots, max_queue_slots); return -EPERM; } so->tx_opt.queue_size = slots; pr_devel("[PFQ|%d] tx_queue slots=%zu\n", so->id.value, so->tx_opt.queue_size); } break; case Q_SO_GROUP_LEAVE: { pfq_gid_t gid; if (optlen != sizeof(gid.value)) return -EINVAL; if (copy_from_user(&gid.value, optval, optlen)) return -EFAULT; if (pfq_leave_group(gid, so->id) < 0) return -EFAULT; pr_devel("[PFQ|%d] leave: gid=%d\n", so->id.value, gid.value); } break; case Q_SO_GROUP_FPROG: { struct pfq_fprog fprog; pfq_gid_t gid; if (optlen != sizeof(fprog)) return -EINVAL; if (copy_from_user(&fprog, optval, optlen)) return -EFAULT; gid.value = fprog.gid; if (!pfq_has_joined_group(gid, so->id)) { /* don't set the first and return */ return 0; } if (fprog.fcode.len > 0) { /* set the filter */ struct sk_filter *filter; if (fprog.fcode.len == 1) { /* check for dummey BPF_CLASS == BPF_RET */ if (BPF_CLASS(fprog.fcode.filter[0].code) == BPF_RET) { pr_devel("[PFQ|%d] fprog: BPF_RET optimized out!\n", so->id.value); return 0; } } filter = pfq_alloc_sk_filter(&fprog.fcode); if (filter == NULL) { printk(KERN_INFO "[PFQ|%d] fprog error: alloc_sk_filter for gid=%d\n", so->id.value, fprog.gid); return -EINVAL; } pfq_set_group_filter(gid, filter); pr_devel("[PFQ|%d] fprog: gid=%d (fprog len %d bytes)\n", so->id.value, fprog.gid, fprog.fcode.len); } else { /* reset the filter */ pfq_set_group_filter(gid, NULL); pr_devel("[PFQ|%d] fprog: gid=%d (resetting filter)\n", so->id.value, fprog.gid); } } break; case Q_SO_GROUP_VLAN_FILT_TOGGLE: { struct pfq_vlan_toggle vlan; pfq_gid_t gid; if (optlen != sizeof(vlan)) return -EINVAL; if (copy_from_user(&vlan, optval, optlen)) return -EFAULT; gid.value = vlan.gid; if (!pfq_has_joined_group(gid, so->id)) { printk(KERN_INFO "[PFQ|%d] vlan filter toggle: gid=%d not joined!\n", so->id.value, vlan.gid); return -EACCES; } pfq_toggle_group_vlan_filters(gid, vlan.toggle); pr_devel("[PFQ|%d] vlan filters %s for gid=%d\n", so->id.value, (vlan.toggle ? "enabled" : "disabled"), vlan.gid); } break; case Q_SO_GROUP_VLAN_FILT: { struct pfq_vlan_toggle filt; pfq_gid_t gid; if (optlen != sizeof(filt)) return -EINVAL; if (copy_from_user(&filt, optval, optlen)) return -EFAULT; gid.value = filt.gid; if (!pfq_has_joined_group(gid, so->id)) { printk(KERN_INFO "[PFQ|%d] vlan filter: gid=%d not joined!\n", so->id.value, filt.gid); return -EACCES; } if (filt.vid < -1 || filt.vid > 4094) { printk(KERN_INFO "[PFQ|%d] vlan error: invalid vid=%d for gid=%d!\n", so->id.value, filt.vid, filt.gid); return -EINVAL; } if (!pfq_vlan_filters_enabled(gid)) { printk(KERN_INFO "[PFQ|%d] vlan error: vlan filters disabled for gid=%d!\n", so->id.value, filt.gid); return -EPERM; } if (filt.vid == -1) { /* any */ int i; for(i = 1; i < 4095; i++) { pfq_set_group_vlan_filter(gid, filt.toggle, i); } } else { pfq_set_group_vlan_filter(gid, filt.toggle, filt.vid); } pr_devel("[PFQ|%d] vlan filter vid %d set for gid=%d\n", so->id.value, filt.vid, filt.gid); } break; case Q_SO_TX_BIND: { struct pfq_binding info; size_t i; if (optlen != sizeof(info)) return -EINVAL; if (copy_from_user(&info, optval, optlen)) return -EFAULT; if (so->tx_opt.num_queues >= Q_MAX_TX_QUEUES) { printk(KERN_INFO "[PFQ|%d] Tx bind: max number of queues exceeded!\n", so->id.value); return -EPERM; } rcu_read_lock(); if (!dev_get_by_index_rcu(sock_net(&so->sk), info.if_index)) { rcu_read_unlock(); printk(KERN_INFO "[PFQ|%d] Tx bind: invalid if_index=%d\n", so->id.value, info.if_index); return -EPERM; } rcu_read_unlock(); if (info.hw_queue < -1) { printk(KERN_INFO "[PFQ|%d] Tx bind: invalid queue=%d\n", so->id.value, info.hw_queue); return -EPERM; } i = so->tx_opt.num_queues; if (info.cpu < -1) { printk(KERN_INFO "[PFQ|%d] Tx[%zu] thread: invalid cpu (%d)!\n", so->id.value, i, info.cpu); return -EPERM; } so->tx_opt.queue[i].if_index = info.if_index; so->tx_opt.queue[i].hw_queue = info.hw_queue; so->tx_opt.queue[i].cpu = info.cpu; so->tx_opt.num_queues++; pr_devel("[PFQ|%d] Tx[%zu] bind: if_index=%d hw_queue=%d cpu=%d\n", so->id.value, i, so->tx_opt.queue[i].if_index, so->tx_opt.queue[i].hw_queue, info.cpu); } break; case Q_SO_TX_UNBIND: { size_t n; for(n = 0; n < Q_MAX_TX_QUEUES; ++n) { so->tx_opt.queue[n].if_index = -1; so->tx_opt.queue[n].hw_queue = -1; so->tx_opt.queue[n].cpu = -1; } } break; case Q_SO_TX_FLUSH: { int queue, err = 0; size_t n; if (optlen != sizeof(queue)) return -EINVAL; if (copy_from_user(&queue, optval, optlen)) return -EFAULT; if (pfq_get_tx_queue(&so->tx_opt, 0) == NULL) { printk(KERN_INFO "[PFQ|%d] Tx queue flush: socket not enabled!\n", so->id.value); return -EPERM; } if (queue < -1 || (queue > 0 && queue >= so->tx_opt.num_queues)) { printk(KERN_INFO "[PFQ|%d] Tx queue flush: bad queue %d (num_queue=%zu)!\n", so->id.value, queue, so->tx_opt.num_queues); return -EPERM; } if (queue != -1) { pr_devel("[PFQ|%d] flushing Tx queue %d...\n", so->id.value, queue); return pfq_queue_flush(so, queue); } for(n = 0; n < so->tx_opt.num_queues; n++) { if (pfq_queue_flush(so, n) != 0) { printk(KERN_INFO "[PFQ|%d] Tx[%zu] queue flush: flush error (if_index=%d)!\n", so->id.value, n, so->tx_opt.queue[n].if_index); err = -EPERM; } } if (err) return err; } break; case Q_SO_TX_ASYNC: { int toggle, err = 0; size_t n; if (optlen != sizeof(toggle)) return -EINVAL; if (copy_from_user(&toggle, optval, optlen)) return -EFAULT; if (toggle) { size_t started = 0; if (pfq_get_tx_queue(&so->tx_opt, 0) == NULL) { printk(KERN_INFO "[PFQ|%d] Tx queue flush: socket not enabled!\n", so->id.value); return -EPERM; } /* start Tx kernel threads */ for(n = 0; n < Q_MAX_TX_QUEUES; n++) { struct pfq_thread_data *data; int node; if (so->tx_opt.queue[n].if_index == -1) break; if (so->tx_opt.queue[n].cpu == Q_NO_KTHREAD) continue; if (so->tx_opt.queue[n].task) { printk(KERN_INFO "[PFQ|%d] kernel_thread: Tx[%zu] thread already running!\n", so->id.value, n); continue; } data = kmalloc(sizeof(struct pfq_thread_data), GFP_KERNEL); if (!data) { printk(KERN_INFO "[PFQ|%d] kernel_thread: could not allocate thread_data! Failed starting thread on cpu %d!\n", so->id.value, so->tx_opt.queue[n].cpu); err = -EPERM; continue; } data->so = so; data->id = n; node = cpu_online(so->tx_opt.queue[n].cpu) ? cpu_to_node(so->tx_opt.queue[n].cpu) : NUMA_NO_NODE; pr_devel("[PFQ|%d] creating Tx[%zu] thread on cpu %d: if_index=%d hw_queue=%d\n", so->id.value, n, so->tx_opt.queue[n].cpu, so->tx_opt.queue[n].if_index, so->tx_opt.queue[n].hw_queue); so->tx_opt.queue[n].task = kthread_create_on_node(pfq_tx_thread, data, node, "pfq_tx_%d#%zu", so->id.value, n); if (IS_ERR(so->tx_opt.queue[n].task)) { printk(KERN_INFO "[PFQ|%d] kernel_thread: create failed on cpu %d!\n", so->id.value, so->tx_opt.queue[n].cpu); err = PTR_ERR(so->tx_opt.queue[n].task); so->tx_opt.queue[n].task = NULL; kfree (data); continue; } /* bind the thread */ kthread_bind(so->tx_opt.queue[n].task, so->tx_opt.queue[n].cpu); /* start it */ wake_up_process(so->tx_opt.queue[n].task); started++; } if (started == 0) { printk(KERN_INFO "[PFQ|%d] no kernel thread started!\n", so->id.value); err = -EPERM; } } else { /* stop running threads */ for(n = 0; n < so->tx_opt.num_queues; n++) { if (so->tx_opt.queue[n].task) { pr_devel("[PFQ|%d] stopping Tx[%zu] kernel thread@%p\n", so->id.value, n, so->tx_opt.queue[n].task); kthread_stop(so->tx_opt.queue[n].task); so->tx_opt.queue[n].task = NULL; } } } return err; } break; case Q_SO_GROUP_FUNCTION: { struct pfq_computation_descr *descr = NULL; struct pfq_computation_tree *comp = NULL; struct pfq_group_computation tmp; size_t psize, ucsize; void *context = NULL; pfq_gid_t gid; int err = 0; if (optlen != sizeof(tmp)) return -EINVAL; if (copy_from_user(&tmp, optval, optlen)) return -EFAULT; gid.value = tmp.gid; if (!pfq_has_joined_group(gid, so->id)) { printk(KERN_INFO "[PFQ|%d] group computation: gid=%d not joined!\n", so->id.value, tmp.gid); return -EACCES; } if (copy_from_user(&psize, tmp.prog, sizeof(size_t))) return -EFAULT; pr_devel("[PFQ|%d] computation size: %zu\n", so->id.value, psize); ucsize = sizeof(size_t) * 2 + psize * sizeof(struct pfq_functional_descr); descr = kmalloc(ucsize, GFP_KERNEL); if (descr == NULL) { printk(KERN_INFO "[PFQ|%d] computation: out of memory!\n", so->id.value); return -ENOMEM; } if (copy_from_user(descr, tmp.prog, ucsize)) { printk(KERN_INFO "[PFQ|%d] computation: copy_from_user error!\n", so->id.value); err = -EFAULT; goto error; } /* print user computation */ pr_devel_computation_descr(descr); /* check the correctness of computation */ if (pfq_check_computation_descr(descr) < 0) { printk(KERN_INFO "[PFQ|%d] invalid expression!\n", so->id.value); err = -EFAULT; goto error; } /* allocate context */ context = pfq_context_alloc(descr); if (context == NULL) { printk(KERN_INFO "[PFQ|%d] context: alloc error!\n", so->id.value); err = -EFAULT; goto error; } /* allocate a pfq_computation_tree */ comp = pfq_computation_alloc(descr); if (comp == NULL) { printk(KERN_INFO "[PFQ|%d] computation: alloc error!\n", so->id.value); err = -EFAULT; goto error; } /* link functions of computation */ if (pfq_computation_rtlink(descr, comp, context) < 0) { printk(KERN_INFO "[PFQ|%d] computation aborted!", so->id.value); err = -EPERM; goto error; } /* print executable tree data structure */ pr_devel_computation_tree(comp); /* run init functions */ if (pfq_computation_init(comp) < 0) { printk(KERN_INFO "[PFQ|%d] initialization of computation aborted!", so->id.value); pfq_computation_fini(comp); err = -EPERM; goto error; } /* enable functional program */ if (pfq_set_group_prog(gid, comp, context) < 0) { printk(KERN_INFO "[PFQ|%d] set group program error!\n", so->id.value); err = -EPERM; goto error; } kfree(descr); return 0; error: kfree(comp); kfree(context); kfree(descr); return err; } break; default: { found = false; } break; } return found ? 0 : sock_setsockopt(sock, level, optname, optval, optlen); }
int pfq_setsockopt(struct socket *sock, int level, int optname, char __user * optval, #if(LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,31)) unsigned #endif int optlen) { struct pfq_sock *so = pfq_sk(sock->sk); struct pfq_rx_opt * ro; struct pfq_tx_opt * to; bool found = true; if (so == NULL) return -EINVAL; ro = &so->rx_opt; to = &so->tx_opt; switch(optname) { case Q_SO_TOGGLE_QUEUE: { int active; if (optlen != sizeof(active)) return -EINVAL; if (copy_from_user(&active, optval, optlen)) return -EFAULT; if (active) { if (!so->mem_addr) { struct pfq_queue_hdr * queue; /* alloc queue memory */ if (pfq_shared_queue_alloc(so, pfq_queue_total_mem(so)) < 0) { return -ENOMEM; } /* so->mem_addr and so->mem_size are correctly configured */ /* initialize queues headers */ queue = (struct pfq_queue_hdr *)so->mem_addr; /* initialize rx queue header */ queue->rx.data = (1L << 24); queue->rx.poll_wait = 0; queue->rx.size = so->rx_opt.size; queue->rx.slot_size = so->rx_opt.slot_size; queue->tx.producer.index = 0; queue->tx.producer.cache = 0; queue->tx.consumer.index = 0; queue->tx.consumer.cache = 0; queue->tx.size_mask = so->tx_opt.size - 1; queue->tx.max_len = so->tx_opt.maxlen; queue->tx.size = so->tx_opt.size; queue->tx.slot_size = so->tx_opt.slot_size; /* update the queues base_addr */ so->rx_opt.base_addr = so->mem_addr + sizeof(struct pfq_queue_hdr); so->tx_opt.base_addr = so->mem_addr + sizeof(struct pfq_queue_hdr) + pfq_queue_mpdb_mem(so); /* commit both the queues */ smp_wmb(); so->rx_opt.queue_ptr = &queue->rx; so->tx_opt.queue_ptr = &queue->tx; pr_devel("[PFQ|%d] queue: rx_size:%d rx_slot_size:%d tx_size:%d tx_slot_size:%d\n", so->id, queue->rx.size, queue->rx.slot_size, queue->tx.size, queue->tx.slot_size); } } else { if (so->tx_opt.thread) { pr_devel("[PFQ|%d] stopping TX thread...\n", so->id); kthread_stop(so->tx_opt.thread); so->tx_opt.thread = NULL; } msleep(Q_GRACE_PERIOD); pfq_shared_queue_free(so); } } break; case Q_SO_GROUP_BIND: { struct pfq_binding bind; if (optlen != sizeof(struct pfq_binding)) return -EINVAL; if (copy_from_user(&bind, optval, optlen)) return -EFAULT; CHECK_GROUP_ACCES(so->id, bind.gid, "add binding"); pfq_devmap_update(map_set, bind.if_index, bind.hw_queue, bind.gid); } break; case Q_SO_GROUP_UNBIND: { struct pfq_binding bind; if (optlen != sizeof(struct pfq_binding)) return -EINVAL; if (copy_from_user(&bind, optval, optlen)) return -EFAULT; CHECK_GROUP_ACCES(so->id, bind.gid, "remove binding"); pfq_devmap_update(map_reset, bind.if_index, bind.hw_queue, bind.gid); } break; case Q_SO_EGRESS_BIND: { struct pfq_binding info; if (optlen != sizeof(info)) return -EINVAL; if (copy_from_user(&info, optval, optlen)) return -EFAULT; rcu_read_lock(); if (!dev_get_by_index_rcu(sock_net(&so->sk), info.if_index)) { rcu_read_unlock(); pr_devel("[PFQ|%d] TX bind: invalid if_index:%d\n", so->id, info.if_index); return -EPERM; } rcu_read_unlock(); if (info.hw_queue < -1) { pr_devel("[PFQ|%d] TX bind: invalid queue:%d\n", so->id, info.hw_queue); return -EPERM; } so->egress_index = info.if_index; so->egress_queue = info.hw_queue; pr_devel("[PFQ|%d] egress bind: if_index:%d hw_queue:%d\n", so->id, so->egress_index, so->egress_queue); } break; case Q_SO_EGRESS_UNBIND: { so->egress_index = 0; so->egress_queue = 0; pr_devel("[PFQ|%d] egress unbind.\n", so->id); } break; case Q_SO_SET_RX_TSTAMP: { int tstamp; if (optlen != sizeof(so->rx_opt.tstamp)) return -EINVAL; if (copy_from_user(&tstamp, optval, optlen)) return -EFAULT; tstamp = tstamp ? 1 : 0; /* update the timestamp_enabled counter */ atomic_add(tstamp - so->rx_opt.tstamp, ×tamp_enabled); so->rx_opt.tstamp = tstamp; pr_devel("[PFQ|%d] timestamp_enabled counter: %d\n", so->id, atomic_read(×tamp_enabled)); } break; case Q_SO_SET_RX_CAPLEN: { typeof(so->rx_opt.caplen) caplen; if (optlen != sizeof(caplen)) return -EINVAL; if (copy_from_user(&caplen, optval, optlen)) return -EFAULT; if (caplen > (size_t)cap_len) { pr_devel("[PFQ|%d] invalid caplen:%zu (max: %d)\n", so->id, caplen, cap_len); return -EPERM; } so->rx_opt.caplen = caplen; so->rx_opt.slot_size = MPDB_QUEUE_SLOT_SIZE(so->rx_opt.caplen); pr_devel("[PFQ|%d] caplen:%zu -> slot_size:%zu\n", so->id, so->rx_opt.caplen, so->rx_opt.slot_size); } break; case Q_SO_SET_RX_SLOTS: { typeof(so->rx_opt.size) slots; if (optlen != sizeof(slots)) return -EINVAL; if (copy_from_user(&slots, optval, optlen)) return -EFAULT; if (slots > (size_t)rx_queue_slots) { pr_devel("[PFQ|%d] invalid rx slots:%zu (max: %d)\n", so->id, slots, rx_queue_slots); return -EPERM; } so->rx_opt.size = slots; pr_devel("[PFQ|%d] rx_queue_slots:%zu\n", so->id, so->rx_opt.size); } break; case Q_SO_SET_TX_MAXLEN: { typeof (so->tx_opt.maxlen) maxlen; if (optlen != sizeof(maxlen)) return -EINVAL; if (copy_from_user(&maxlen, optval, optlen)) return -EFAULT; if (maxlen > (size_t)max_len) { pr_devel("[PFQ|%d] invalid maxlen:%zu (max: %d)\n", so->id, maxlen, max_len); return -EPERM; } so->tx_opt.maxlen = maxlen; so->tx_opt.slot_size = SPSC_QUEUE_SLOT_SIZE(so->tx_opt.maxlen); /* max_len: max length */ pr_devel("[PFQ|%d] tx_slot_size:%zu\n", so->id, so->rx_opt.slot_size); } break; case Q_SO_SET_TX_SLOTS: { typeof (so->tx_opt.size) slots; if (optlen != sizeof(slots)) return -EINVAL; if (copy_from_user(&slots, optval, optlen)) return -EFAULT; if (slots & (slots-1)) { pr_devel("[PFQ|%d] tx slots must be a power of two.\n", so->id); return -EINVAL; } if (slots > (size_t)tx_queue_slots) { pr_devel("[PFQ|%d] invalid tx slots:%zu (max: %d)\n", so->id, slots, tx_queue_slots); return -EPERM; } so->tx_opt.size = slots; pr_devel("[PFQ|%d] tx_queue_slots:%zu\n", so->id, so->tx_opt.size); } break; case Q_SO_GROUP_LEAVE: { int gid; if (optlen != sizeof(gid)) return -EINVAL; if (copy_from_user(&gid, optval, optlen)) return -EFAULT; if (pfq_leave_group(gid, so->id) < 0) { return -EFAULT; } pr_devel("[PFQ|%d] leave: gid:%d\n", so->id, gid); } break; case Q_SO_GROUP_FPROG: { struct pfq_fprog fprog; if (optlen != sizeof(fprog)) return -EINVAL; if (copy_from_user(&fprog, optval, optlen)) return -EFAULT; CHECK_GROUP_ACCES(so->id, fprog.gid, "group fprog"); if (fprog.fcode.len > 0) /* set the filter */ { struct sk_filter *filter = pfq_alloc_sk_filter(&fprog.fcode); if (filter == NULL) { pr_devel("[PFQ|%d] fprog error: alloc_sk_filter for gid:%d\n", so->id, fprog.gid); return -EINVAL; } __pfq_set_group_filter(fprog.gid, filter); pr_devel("[PFQ|%d] fprog: gid:%d (fprog len %d bytes)\n", so->id, fprog.gid, fprog.fcode.len); } else /* reset the filter */ { __pfq_set_group_filter(fprog.gid, NULL); pr_devel("[PFQ|%d] fprog: gid:%d (resetting filter)\n", so->id, fprog.gid); } } break; case Q_SO_GROUP_VLAN_FILT_TOGGLE: { struct pfq_vlan_toggle vlan; if (optlen != sizeof(vlan)) return -EINVAL; if (copy_from_user(&vlan, optval, optlen)) return -EFAULT; CHECK_GROUP_ACCES(so->id, vlan.gid, "group vlan filt toggle"); __pfq_toggle_group_vlan_filters(vlan.gid, vlan.toggle); pr_devel("[PFQ|%d] vlan filters %s for gid:%d\n", so->id, (vlan.toggle ? "enabled" : "disabled"), vlan.gid); } break; case Q_SO_GROUP_VLAN_FILT: { struct pfq_vlan_toggle filt; if (optlen != sizeof(filt)) return -EINVAL; if (copy_from_user(&filt, optval, optlen)) return -EFAULT; CHECK_GROUP_ACCES(so->id, filt.gid, "group vlan filt"); if (filt.vid < -1 || filt.vid > 4094) { pr_devel("[PFQ|%d] vlan_set error: gid:%d invalid vid:%d!\n", so->id, filt.gid, filt.vid); return -EINVAL; } if (!__pfq_vlan_filters_enabled(filt.gid)) { pr_devel("[PFQ|%d] vlan_set error: vlan filters disabled for gid:%d!\n", so->id, filt.gid); return -EPERM; } if (filt.vid == -1) /* any */ { int i; for(i = 1; i < 4095; i++) __pfq_set_group_vlan_filter(filt.gid, filt.toggle, i); } else { __pfq_set_group_vlan_filter(filt.gid, filt.toggle, filt.vid); } pr_devel("[PFQ|%d] vlan_set filter vid %d for gid:%d\n", so->id, filt.vid, filt.gid); } break; case Q_SO_TX_THREAD_BIND: { struct pfq_binding info; if (optlen != sizeof(info)) return -EINVAL; if (copy_from_user(&info, optval, optlen)) return -EFAULT; rcu_read_lock(); if (!dev_get_by_index_rcu(sock_net(&so->sk), info.if_index)) { rcu_read_unlock(); pr_devel("[PFQ|%d] TX bind: invalid if_index:%d\n", so->id, info.if_index); return -EPERM; } rcu_read_unlock(); if (info.hw_queue < -1) { pr_devel("[PFQ|%d] TX bind: invalid queue:%d\n", so->id, info.hw_queue); return -EPERM; } to->if_index = info.if_index; to->hw_queue = info.hw_queue; pr_devel("[PFQ|%d] TX bind: if_index:%d hw_queue:%d\n", so->id, to->if_index, to->hw_queue); } break; case Q_SO_TX_THREAD_START: { int cpu; if (to->thread) { pr_devel("[PFQ|%d] TX thread already created on cpu %d!\n", so->id, to->cpu); return -EPERM; } if (to->if_index == -1) { pr_devel("[PFQ|%d] socket TX not bound to any device!\n", so->id); return -EPERM; } if (to->queue_ptr == NULL) { pr_devel("[PFQ|%d] socket not enabled!\n", so->id); return -EPERM; } if (optlen != sizeof(cpu)) return -EINVAL; if (copy_from_user(&cpu, optval, optlen)) return -EFAULT; if (cpu < -1 || (cpu > -1 && !cpu_online(cpu))) { pr_devel("[PFQ|%d] invalid cpu (%d)!\n", so->id, cpu); return -EPERM; } to->cpu = cpu; pr_devel("[PFQ|%d] creating TX thread on cpu %d -> if_index:%d hw_queue:%d\n", so->id, to->cpu, to->if_index, to->hw_queue); to->thread = kthread_create_on_node(pfq_tx_thread, so, to->cpu == -1 ? -1 : cpu_to_node(to->cpu), "pfq_tx_%d", so->id); if (IS_ERR(to->thread)) { printk(KERN_INFO "[PFQ] kernel_thread() create failed on cpu %d!\n", to->cpu); return PTR_ERR(to->thread); } if (to->cpu != -1) kthread_bind(to->thread, to->cpu); } break; case Q_SO_TX_THREAD_STOP: { pr_devel("[PFQ|%d] stopping TX thread...\n", so->id); if (!to->thread) { pr_devel("[PFQ|%d] TX thread not running!\n", so->id); return -EPERM; } kthread_stop(to->thread); to->thread = NULL; pr_devel("[PFQ|%d] stop TX thread: done.\n", so->id); } break; case Q_SO_TX_THREAD_WAKEUP: { if (to->if_index == -1) { pr_devel("[PFQ|%d] socket TX not bound to any device!\n", so->id); return -EPERM; } if (!to->thread) { pr_devel("[PFQ|%d] TX thread not running!\n", so->id); return -EPERM; } wake_up_process(to->thread); } break; case Q_SO_TX_QUEUE_FLUSH: { struct net_device *dev; if (to->if_index == -1) { pr_devel("[PFQ|%d] socket TX not bound to any device!\n", so->id); return -EPERM; } if (to->thread && to->thread->state == TASK_RUNNING) { pr_devel("[PFQ|%d] TX thread is running!\n", so->id); return -EPERM; } if (to->queue_ptr == NULL) { pr_devel("[PFQ|%d] socket not enabled!\n", so->id); return -EPERM; } dev = dev_get_by_index(sock_net(&so->sk), to->if_index); if (!dev) { pr_devel("[PFQ|%d] No such device (if_index = %d)\n", so->id, to->if_index); return -EPERM; } pfq_tx_queue_flush(to, dev, get_cpu(), NUMA_NO_NODE); put_cpu(); dev_put(dev); } break; case Q_SO_GROUP_FUNCTION: { struct pfq_group_computation tmp; struct pfq_computation_descr *descr; size_t psize, ucsize; struct pfq_computation_tree *comp; void *context; if (optlen != sizeof(tmp)) return -EINVAL; if (copy_from_user(&tmp, optval, optlen)) return -EFAULT; CHECK_GROUP_ACCES(so->id, tmp.gid, "group computation"); if (copy_from_user(&psize, tmp.prog, sizeof(size_t))) return -EFAULT; pr_devel("[PFQ|%d] computation size: %zu\n", so->id, psize); ucsize = sizeof(size_t) * 2 + psize * sizeof(struct pfq_functional_descr); descr = kmalloc(ucsize, GFP_KERNEL); if (descr == NULL) { pr_devel("[PFQ|%d] computation: out of memory!\n", so->id); return -ENOMEM; } if (copy_from_user(descr, tmp.prog, ucsize)) { pr_devel("[PFQ|%d] computation: copy_from_user error!\n", so->id); kfree(descr); return -EFAULT; } /* print user computation */ pr_devel_computation_descr(descr); /* ensure the correctness of the specified functional computation */ if (pfq_validate_computation_descr(descr) < 0) { pr_devel("[PFQ|%d] invalid expression!\n", so->id); return -EFAULT; } /* allocate context */ context = pfq_context_alloc(descr); if (context == NULL) { pr_devel("[PFQ|%d] context: alloc error!\n", so->id); kfree(descr); return -EFAULT; } /* allocate struct pfq_computation_tree */ comp = pfq_computation_alloc(descr); if (comp == NULL) { pr_devel("[PFQ|%d] computation: alloc error!\n", so->id); kfree(context); kfree(descr); return -EFAULT; } /* link the functional computation */ if (pfq_computation_rtlink(descr, comp, context) < 0) { pr_devel("[PFQ|%d] computation aborted!", so->id); kfree(context); kfree(descr); kfree(comp); return -EPERM; } /* print executable tree data structure */ pr_devel_computation_tree(comp); /* exec init functions */ if (pfq_computation_init(comp) < 0) { pr_devel("[PFQ|%d] computation initialization aborted!", so->id); kfree(context); kfree(descr); kfree(comp); return -EPERM; } /* set the new program */ if (pfq_set_group_prog(tmp.gid, comp, context) < 0) { pr_devel("[PFQ|%d] set group program error!\n", so->id); kfree(context); kfree(descr); kfree(comp); return -EPERM; } kfree(descr); return 0; } break; default: { found = false; } break; } return found ? 0 : sock_setsockopt(sock, level, optname, optval, optlen); }
int pfq_setsockopt(struct socket *sock, int level, int optname, char __user * optval, #if(LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,31)) unsigned #endif int optlen) { struct pfq_sock *so = pfq_sk(sock->sk); bool found = true; if (so == NULL) return -EINVAL; switch(optname) { case Q_SO_ENABLE: { unsigned long addr; int err = 0; if (optlen != sizeof(addr)) return -EINVAL; if (copy_from_user(&addr, optval, optlen)) return -EFAULT; err = pfq_shared_queue_enable(so, addr); if (err < 0) { printk(KERN_INFO "[PFQ|%d] enable error!\n", so->id); return err; } return 0; } break; case Q_SO_DISABLE: { int err = 0; pfq_sock_tx_unbind(so); msleep(Q_GRACE_PERIOD); err = pfq_shared_queue_disable(so); if (err < 0) { printk(KERN_INFO "[PFQ|%d] disable error!\n", so->id); return err; } } break; case Q_SO_GROUP_BIND: { struct pfq_binding bind; pfq_gid_t gid; if (optlen != sizeof(struct pfq_binding)) return -EINVAL; if (copy_from_user(&bind, optval, optlen)) return -EFAULT; gid = (__force pfq_gid_t)bind.gid; if (!pfq_has_joined_group(gid, so->id)) { printk(KERN_INFO "[PFQ|%d] add bind: gid=%d not joined!\n", so->id, bind.gid); return -EACCES; } if (!dev_get_by_index(sock_net(&so->sk), bind.ifindex)) { printk(KERN_INFO "[PFQ|%d] bind: invalid ifindex=%d!\n", so->id, bind.ifindex); return -EACCES; } pfq_devmap_update(map_set, bind.ifindex, bind.qindex, gid); pr_devel("[PFQ|%d] group id=%d bind: device ifindex=%d qindex=%d\n", so->id, bind.gid, bind.ifindex, bind.qindex); } break; case Q_SO_GROUP_UNBIND: { struct pfq_binding bind; pfq_gid_t gid; if (optlen != sizeof(struct pfq_binding)) return -EINVAL; if (copy_from_user(&bind, optval, optlen)) return -EFAULT; gid = (__force pfq_gid_t)bind.gid; if (!pfq_has_joined_group(gid, so->id)) { printk(KERN_INFO "[PFQ|%d] group id=%d unbind: gid=%d not joined!\n", so->id, gid, bind.gid); return -EACCES; } if (dev_put_by_index(sock_net(&so->sk), bind.ifindex) < 0) { printk(KERN_INFO "[PFQ|%d] group id=%d unbind: invalid ifindex=%d!\n", so->id, gid, bind.ifindex); return -EPERM; } pfq_devmap_update(map_reset, bind.ifindex, bind.qindex, gid); pr_devel("[PFQ|%d] group id=%d unbind: device ifindex=%d qindex=%d\n", so->id, gid, bind.ifindex, bind.qindex); } break; case Q_SO_EGRESS_BIND: { struct pfq_binding bind; if (optlen != sizeof(bind)) return -EINVAL; if (copy_from_user(&bind, optval, optlen)) return -EFAULT; if (!dev_get_by_index(sock_net(&so->sk), bind.ifindex)) { printk(KERN_INFO "[PFQ|%d] egress bind: invalid ifindex=%d\n", so->id, bind.ifindex); return -EPERM; } if (bind.qindex < -1) { printk(KERN_INFO "[PFQ|%d] egress bind: invalid qindex=%d\n", so->id, bind.qindex); return -EPERM; } so->egress_type = pfq_endpoint_device; so->egress_index = bind.ifindex; so->egress_queue = bind.qindex; pr_devel("[PFQ|%d] egress bind: device ifindex=%d qindex=%d\n", so->id, so->egress_index, so->egress_queue); } break; case Q_SO_EGRESS_UNBIND: { if (so->egress_index && dev_put_by_index(sock_net(&so->sk), so->egress_index) < 0) { printk(KERN_INFO "[PFQ|%d] egress bind: invalid if_index=%d\n", so->id, so->egress_index); return -EPERM; } so->egress_type = pfq_endpoint_socket; so->egress_index = 0; so->egress_queue = 0; pr_devel("[PFQ|%d] egress unbind.\n", so->id); } break; case Q_SO_SET_RX_TSTAMP: { int tstamp; if (optlen != sizeof(so->opt.tstamp)) return -EINVAL; if (copy_from_user(&tstamp, optval, optlen)) return -EFAULT; tstamp = tstamp ? 1 : 0; so->opt.tstamp = tstamp; pr_devel("[PFQ|%d] timestamp enabled.\n", so->id); } break; case Q_SO_SET_RX_CAPLEN: { typeof(so->opt.caplen) caplen; if (optlen != sizeof(caplen)) return -EINVAL; if (copy_from_user(&caplen, optval, optlen)) return -EFAULT; if (caplen > (size_t)capt_slot_size) { printk(KERN_INFO "[PFQ|%d] invalid caplen=%zu (max %d)\n", so->id, caplen, capt_slot_size); return -EPERM; } so->opt.caplen = caplen; so->opt.rx_slot_size = Q_QUEUE_SLOT_SIZE(so->opt.caplen); pr_devel("[PFQ|%d] caplen=%zu, slot_size=%zu\n", so->id, so->opt.caplen, so->opt.rx_slot_size); } break; case Q_SO_SET_RX_SLOTS: { typeof(so->opt.rx_queue_len) slots; if (optlen != sizeof(slots)) return -EINVAL; if (copy_from_user(&slots, optval, optlen)) return -EFAULT; if (slots > Q_MAX_SOCKQUEUE_LEN) { printk(KERN_INFO "[PFQ|%d] invalid Rx slots=%zu (max %d)\n", so->id, slots, Q_MAX_SOCKQUEUE_LEN); return -EPERM; } so->opt.rx_queue_len = slots; pr_devel("[PFQ|%d] rx_queue slots=%zu\n", so->id, so->opt.rx_queue_len); } break; case Q_SO_SET_TX_SLOTS: { typeof (so->opt.tx_queue_len) slots; if (optlen != sizeof(slots)) return -EINVAL; if (copy_from_user(&slots, optval, optlen)) return -EFAULT; if (slots > Q_MAX_SOCKQUEUE_LEN) { printk(KERN_INFO "[PFQ|%d] invalid Tx slots=%zu (max %d)\n", so->id, slots, Q_MAX_SOCKQUEUE_LEN); return -EPERM; } so->opt.tx_queue_len = slots; pr_devel("[PFQ|%d] tx_queue slots=%zu\n", so->id, so->opt.tx_queue_len); } break; case Q_SO_SET_WEIGHT: { int weight; if (optlen != sizeof(so->weight)) return -EINVAL; if (copy_from_user(&weight, optval, optlen)) return -EFAULT; if (weight < 1 || weight > (Q_MAX_SOCK_MASK/Q_MAX_ID)) { printk(KERN_INFO "[PFQ|%d] weight=%d: invalid range (min 1, max %d)\n", so->id, weight, Q_MAX_SOCK_MASK/Q_MAX_ID); return -EPERM; } so->weight = weight; /* invalidate per-cpu sock mask cache */ pfq_invalidate_percpu_eligible_mask(so->id); pr_devel("[PFQ|%d] new weight set to %d.\n", so->id, weight); } break; case Q_SO_GROUP_LEAVE: { pfq_gid_t gid; if (optlen != sizeof(gid)) return -EINVAL; if (copy_from_user(&gid, optval, optlen)) return -EFAULT; if (pfq_leave_group(gid, so->id) < 0) return -EFAULT; pr_devel("[PFQ|%d] group id=%d left.\n", so->id, gid); } break; case Q_SO_GROUP_FPROG: { struct pfq_fprog fprog; pfq_gid_t gid; if (optlen != sizeof(fprog)) return -EINVAL; if (copy_from_user(&fprog, optval, optlen)) return -EFAULT; gid = (__force pfq_gid_t)fprog.gid; if (!pfq_has_joined_group(gid, so->id)) { /* don't set the first and return */ return 0; } if (fprog.fcode.len > 0) { /* set the filter */ struct sk_filter *filter; if (fprog.fcode.len == 1) { struct sock_filter tmp; /* get the first filter */ if (copy_from_user(&tmp, fprog.fcode.filter, sizeof(tmp))) return -EFAULT; /* check whether the first filter is a dummy BPF_RET */ if (BPF_CLASS(tmp.code) == BPF_RET) { pr_devel("[PFQ|%d] fprog: BPF_RET optimized out!\n", so->id); return 0; } } filter = pfq_alloc_sk_filter(&fprog.fcode); if (filter == NULL) { printk(KERN_INFO "[PFQ|%d] fprog error: alloc_sk_filter for gid=%d\n", so->id, fprog.gid); return -EINVAL; } pfq_set_group_filter(gid, filter); pr_devel("[PFQ|%d] fprog: gid=%d (fprog len %d bytes)\n", so->id, fprog.gid, fprog.fcode.len); } else { /* reset the filter */ pfq_set_group_filter(gid, NULL); pr_devel("[PFQ|%d] fprog: gid=%d (resetting filter)\n", so->id, fprog.gid); } } break; case Q_SO_GROUP_VLAN_FILT_TOGGLE: { struct pfq_vlan_toggle vlan; pfq_gid_t gid; if (optlen != sizeof(vlan)) return -EINVAL; if (copy_from_user(&vlan, optval, optlen)) return -EFAULT; gid = (__force pfq_gid_t)vlan.gid; if (!pfq_has_joined_group(gid, so->id)) { printk(KERN_INFO "[PFQ|%d] vlan filter toggle: gid=%d not joined!\n", so->id, vlan.gid); return -EACCES; } pfq_toggle_group_vlan_filters(gid, vlan.toggle); pr_devel("[PFQ|%d] vlan filters %s for gid=%d\n", so->id, (vlan.toggle ? "enabled" : "disabled"), vlan.gid); } break; case Q_SO_GROUP_VLAN_FILT: { struct pfq_vlan_toggle filt; pfq_gid_t gid; if (optlen != sizeof(filt)) return -EINVAL; if (copy_from_user(&filt, optval, optlen)) return -EFAULT; gid = (__force pfq_gid_t)filt.gid; if (!pfq_has_joined_group(gid, so->id)) { printk(KERN_INFO "[PFQ|%d] vlan filter: gid=%d not joined!\n", so->id, filt.gid); return -EACCES; } if (filt.vid < -1 || filt.vid > 4094) { printk(KERN_INFO "[PFQ|%d] vlan error: invalid vid=%d for gid=%d!\n", so->id, filt.vid, filt.gid); return -EINVAL; } if (!pfq_vlan_filters_enabled(gid)) { printk(KERN_INFO "[PFQ|%d] vlan error: vlan filters disabled for gid=%d!\n", so->id, filt.gid); return -EPERM; } if (filt.vid == -1) { /* any */ int i; for(i = 1; i < 4095; i++) { pfq_set_group_vlan_filter(gid, filt.toggle, i); } } else { pfq_set_group_vlan_filter(gid, filt.toggle, filt.vid); } pr_devel("[PFQ|%d] vlan filter vid %d set for gid=%d\n", so->id, filt.vid, filt.gid); } break; case Q_SO_TX_BIND: { struct pfq_binding bind; struct net_device *dev = NULL; if (optlen != sizeof(bind)) return -EINVAL; if (copy_from_user(&bind, optval, optlen)) return -EFAULT; if (bind.tid < -1) { printk(KERN_INFO "[PFQ|%d] Tx thread: invalid thread index (%d)!\n", so->id, bind.tid); return -EPERM; } if (bind.tid >= 0 && so->opt.tx_num_async_queues >= Q_MAX_TX_QUEUES) { printk(KERN_INFO "[PFQ|%d] Tx thread: max number of sock queues exceeded!\n", so->id); return -EPERM; } if (bind.qindex < -1) { printk(KERN_INFO "[PFQ|%d] Tx thread: invalid hw queue (%d)\n", so->id, bind.qindex); return -EPERM; } /* get device */ if (bind.ifindex != -1 && !(dev = dev_get_by_index(sock_net(&so->sk), bind.ifindex))) { printk(KERN_INFO "[PFQ|%d] Tx thread: invalid ifindex=%d\n", so->id, bind.ifindex); return -EPERM; } /* update the socket queue information */ if (bind.tid >= 0) /* async queues */ { int err = pfq_sock_tx_bind(so, bind.tid, bind.ifindex, bind.qindex, dev); if (err < 0) { if (bind.ifindex != -1) dev_put_by_index(sock_net(&so->sk), bind.ifindex); return err; } pr_devel("[PFQ|%d] Tx[%d] bind: if_index=%d qindex=%d\n", so->id, bind.tid, bind.ifindex, bind.qindex); } else /* sync queue */ { so->opt.txq.def_ifindex = bind.ifindex; so->opt.txq.def_queue = bind.qindex; so->opt.txq.def_dev = dev; pr_devel("[PFQ|%d] Tx bind: if_index=%d qindex=%d\n", so->id, so->opt.txq.def_ifindex, so->opt.txq.def_queue); } } break; case Q_SO_TX_UNBIND: { pfq_sock_tx_unbind(so); } break; case Q_SO_TX_QUEUE: { int queue; if (optlen != sizeof(queue)) return -EINVAL; if (copy_from_user(&queue, optval, optlen)) return -EFAULT; if (pfq_get_tx_queue(&so->opt, -1) == NULL) { printk(KERN_INFO "[PFQ|%d] Tx queue: socket not enabled!\n", so->id); return -EPERM; } if (queue == 0) { /* transmit Tx queue */ atomic_t stop = {0}; pfq_sk_queue_xmit(so, -1, Q_NO_KTHREAD, NUMA_NO_NODE, &stop); return 0; } printk(KERN_INFO "[PFQ|%d] Tx queue: bad queue %d!\n", so->id, queue); return -EPERM; } break; case Q_SO_GROUP_FUNCTION: { struct pfq_lang_computation_descr *descr = NULL; struct pfq_lang_computation_tree *comp = NULL; struct pfq_group_computation tmp; size_t psize, ucsize; void *context = NULL; pfq_gid_t gid; int err = 0; if (optlen != sizeof(tmp)) return -EINVAL; if (copy_from_user(&tmp, optval, optlen)) return -EFAULT; gid = (__force pfq_gid_t)tmp.gid; if (!pfq_has_joined_group(gid, so->id)) { printk(KERN_INFO "[PFQ|%d] group computation: gid=%d not joined!\n", so->id, tmp.gid); return -EACCES; } if (copy_from_user(&psize, tmp.prog, sizeof(size_t))) return -EFAULT; pr_devel("[PFQ|%d] computation size: %zu\n", so->id, psize); ucsize = sizeof(size_t) * 2 + psize * sizeof(struct pfq_lang_functional_descr); descr = kmalloc(ucsize, GFP_KERNEL); if (descr == NULL) { printk(KERN_INFO "[PFQ|%d] computation: out of memory!\n", so->id); return -ENOMEM; } if (copy_from_user(descr, tmp.prog, ucsize)) { printk(KERN_INFO "[PFQ|%d] computation: copy_from_user error!\n", so->id); err = -EFAULT; goto error; } /* print user computation */ pr_devel_computation_descr(descr); /* check the correctness of computation */ if (pfq_lang_check_computation_descr(descr) < 0) { printk(KERN_INFO "[PFQ|%d] invalid expression!\n", so->id); err = -EFAULT; goto error; } /* allocate context */ context = pfq_lang_context_alloc(descr); if (context == NULL) { printk(KERN_INFO "[PFQ|%d] context: alloc error!\n", so->id); err = -EFAULT; goto error; } /* allocate a pfq_lang_computation_tree */ comp = pfq_lang_computation_alloc(descr); if (comp == NULL) { printk(KERN_INFO "[PFQ|%d] computation: alloc error!\n", so->id); err = -EFAULT; goto error; } /* link functions of computation */ if (pfq_lang_computation_rtlink(descr, comp, context) < 0) { printk(KERN_INFO "[PFQ|%d] computation aborted!", so->id); err = -EPERM; goto error; } /* print executable tree data structure */ pr_devel_computation_tree(comp); /* run init functions */ if (pfq_lang_computation_init(comp) < 0) { printk(KERN_INFO "[PFQ|%d] initialization of computation aborted!", so->id); pfq_lang_computation_destruct(comp); err = -EPERM; goto error; } /* enable functional program */ if (pfq_set_group_prog(gid, comp, context) < 0) { printk(KERN_INFO "[PFQ|%d] set group program error!\n", so->id); err = -EPERM; goto error; } kfree(descr); return 0; error: kfree(comp); kfree(context); kfree(descr); return err; } break; default: { found = false; } break; } return found ? 0 : sock_setsockopt(sock, level, optname, optval, optlen); }