X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=net%2Feconet%2Faf_econet.c;h=4d66aac134835fb082044c26041c6f6039e3ba24;hb=97bf2856c6014879bd04983a3e9dfcdac1e7fe85;hp=b503ae3b40e8066cb546994da989c74edc11c409;hpb=5273a3df6485dc2ad6aa7ddd441b9a21970f003b;p=linux-2.6.git diff --git a/net/econet/af_econet.c b/net/econet/af_econet.c index b503ae3b4..4d66aac13 100644 --- a/net/econet/af_econet.c +++ b/net/econet/af_econet.c @@ -9,7 +9,6 @@ * */ -#include #include #include @@ -31,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -39,14 +39,17 @@ #include #include #include +#include +#include +#include #include #include -#include -static struct proto_ops econet_ops; +static const struct proto_ops econet_ops; static struct hlist_head econet_sklist; -static rwlock_t econet_lock = RW_LOCK_UNLOCKED; +static DEFINE_RWLOCK(econet_lock); +static DEFINE_MUTEX(econet_mutex); /* Since there are only 256 possible network numbers (or fewer, depends how you count) it makes sense to use a simple lookup table. */ @@ -55,7 +58,7 @@ static struct net_device *net2dev_map[256]; #define EC_PORT_IP 0xd2 #ifdef CONFIG_ECONET_AUNUDP -static spinlock_t aun_queue_lock; +static DEFINE_SPINLOCK(aun_queue_lock); static struct socket *udpsock; #define AUN_PORT 0x8000 @@ -122,6 +125,8 @@ static int econet_recvmsg(struct kiocb *iocb, struct socket *sock, msg->msg_namelen = sizeof(struct sockaddr_ec); + mutex_lock(&econet_mutex); + /* * Call the generic datagram receiver. This handles all sorts * of horrible races and re-entrancy so we can forget about it @@ -158,7 +163,7 @@ static int econet_recvmsg(struct kiocb *iocb, struct socket *sock, err = memcpy_toiovec(msg->msg_iov, skb->data, copied); if (err) goto out_free; - sk->sk_stamp = skb->stamp; + skb_get_timestamp(skb, &sk->sk_stamp); if (msg->msg_name) memcpy(msg->msg_name, skb->cb, msg->msg_namelen); @@ -172,6 +177,7 @@ static int econet_recvmsg(struct kiocb *iocb, struct socket *sock, out_free: skb_free_datagram(sk, skb); out: + mutex_unlock(&econet_mutex); return err; } @@ -182,8 +188,8 @@ out: static int econet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) { struct sockaddr_ec *sec = (struct sockaddr_ec *)uaddr; - struct sock *sk=sock->sk; - struct econet_opt *eo = ec_sk(sk); + struct sock *sk; + struct econet_sock *eo; /* * Check legality @@ -193,14 +199,22 @@ static int econet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len sec->sec_family != AF_ECONET) return -EINVAL; + mutex_lock(&econet_mutex); + + sk = sock->sk; + eo = ec_sk(sk); + eo->cb = sec->cb; eo->port = sec->port; eo->station = sec->addr.station; eo->net = sec->addr.net; + mutex_unlock(&econet_mutex); + return 0; } +#if defined(CONFIG_ECONET_AUNUDP) || defined(CONFIG_ECONET_NATIVE) /* * Queue a transmit result for the user to be told about. */ @@ -227,6 +241,7 @@ static void tx_result(struct sock *sk, unsigned long cookie, int result) if (sock_queue_rcv_skb(sk, skb) < 0) kfree_skb(skb); } +#endif #ifdef CONFIG_ECONET_NATIVE /* @@ -255,10 +270,9 @@ static int econet_sendmsg(struct kiocb *iocb, struct socket *sock, struct ec_addr addr; int err; unsigned char port, cb; +#if defined(CONFIG_ECONET_AUNUDP) || defined(CONFIG_ECONET_NATIVE) struct sk_buff *skb; struct ec_cb *eb; -#ifdef CONFIG_ECONET_NATIVE - unsigned short proto = 0; #endif #ifdef CONFIG_ECONET_AUNUDP struct msghdr udpmsg; @@ -274,23 +288,27 @@ static int econet_sendmsg(struct kiocb *iocb, struct socket *sock, * Check the flags. */ - if (msg->msg_flags&~MSG_DONTWAIT) - return(-EINVAL); + if (msg->msg_flags & ~(MSG_DONTWAIT|MSG_CMSG_COMPAT)) + return -EINVAL; /* * Get and verify the address. */ + mutex_lock(&econet_mutex); + if (saddr == NULL) { - struct econet_opt *eo = ec_sk(sk); + struct econet_sock *eo = ec_sk(sk); addr.station = eo->station; addr.net = eo->net; port = eo->port; cb = eo->cb; } else { - if (msg->msg_namelen < sizeof(struct sockaddr_ec)) + if (msg->msg_namelen < sizeof(struct sockaddr_ec)) { + mutex_unlock(&econet_mutex); return -EINVAL; + } addr.station = saddr->addr.station; addr.net = saddr->addr.net; port = saddr->port; @@ -301,21 +319,25 @@ static int econet_sendmsg(struct kiocb *iocb, struct socket *sock, dev = net2dev_map[addr.net]; /* If not directly reachable, use some default */ - if (dev == NULL) - { + if (dev == NULL) { dev = net2dev_map[0]; /* No interfaces at all? */ - if (dev == NULL) + if (dev == NULL) { + mutex_unlock(&econet_mutex); return -ENETDOWN; + } } - if (len + 15 > dev->mtu) + if (len + 15 > dev->mtu) { + mutex_unlock(&econet_mutex); return -EMSGSIZE; + } - if (dev->type == ARPHRD_ECONET) - { + if (dev->type == ARPHRD_ECONET) { /* Real hardware Econet. We're not worthy etc. */ #ifdef CONFIG_ECONET_NATIVE + unsigned short proto = 0; + dev_hold(dev); skb = sock_alloc_send_skb(sk, len+LL_RESERVED_SPACE(dev), @@ -369,6 +391,7 @@ static int econet_sendmsg(struct kiocb *iocb, struct socket *sock, dev_queue_xmit(skb); dev_put(dev); + mutex_unlock(&econet_mutex); return(len); out_free: @@ -379,14 +402,18 @@ static int econet_sendmsg(struct kiocb *iocb, struct socket *sock, #else err = -EPROTOTYPE; #endif + mutex_unlock(&econet_mutex); + return err; } #ifdef CONFIG_ECONET_AUNUDP /* AUN virtual Econet. */ - if (udpsock == NULL) + if (udpsock == NULL) { + mutex_unlock(&econet_mutex); return -ENETDOWN; /* No socket - can't send */ + } /* Make up a UDP datagram and hand it off to some higher intellect. */ @@ -398,16 +425,17 @@ static int econet_sendmsg(struct kiocb *iocb, struct socket *sock, y.x maps to IP a.b.c.x. This should be replaced with something more flexible and more aware of subnet masks. */ { - struct in_device *idev = in_dev_get(dev); + struct in_device *idev; unsigned long network = 0; + + rcu_read_lock(); + idev = __in_dev_get_rcu(dev); if (idev) { - read_lock(&idev->lock); if (idev->ifa_list) network = ntohl(idev->ifa_list->ifa_address) & 0xffffff00; /* !!! */ - read_unlock(&idev->lock); - in_dev_put(idev); } + rcu_read_unlock(); udpdest.sin_addr.s_addr = htonl(network | addr.station); } @@ -418,14 +446,24 @@ static int econet_sendmsg(struct kiocb *iocb, struct socket *sock, /* tack our header on the front of the iovec */ size = sizeof(struct aunhdr); + /* + * XXX: that is b0rken. We can't mix userland and kernel pointers + * in iovec, since on a lot of platforms copy_from_user() will + * *not* work with the kernel and userland ones at the same time, + * regardless of what we do with set_fs(). And we are talking about + * econet-over-ethernet here, so "it's only ARM anyway" doesn't + * apply. Any suggestions on fixing that code? -- AV + */ iov[0].iov_base = (void *)&ah; iov[0].iov_len = size; for (i = 0; i < msg->msg_iovlen; i++) { - void *base = msg->msg_iov[i].iov_base; + void __user *base = msg->msg_iov[i].iov_base; size_t len = msg->msg_iov[i].iov_len; /* Check it now since we switch to KERNEL_DS later. */ - if ((err = verify_area(VERIFY_READ, base, len)) < 0) - return err; + if (!access_ok(VERIFY_READ, base, len)) { + mutex_unlock(&econet_mutex); + return -EFAULT; + } iov[i+1].iov_base = base; iov[i+1].iov_len = len; size += len; @@ -433,8 +471,11 @@ static int econet_sendmsg(struct kiocb *iocb, struct socket *sock, /* Get a skbuff (no data, just holds our cb information) */ if ((skb = sock_alloc_send_skb(sk, 0, - msg->msg_flags & MSG_DONTWAIT, &err)) == NULL) + msg->msg_flags & MSG_DONTWAIT, + &err)) == NULL) { + mutex_unlock(&econet_mutex); return err; + } eb = (struct ec_cb *)&skb->cb; @@ -461,6 +502,8 @@ static int econet_sendmsg(struct kiocb *iocb, struct socket *sock, #else err = -EPROTOTYPE; #endif + mutex_unlock(&econet_mutex); + return err; } @@ -471,18 +514,25 @@ static int econet_sendmsg(struct kiocb *iocb, struct socket *sock, static int econet_getname(struct socket *sock, struct sockaddr *uaddr, int *uaddr_len, int peer) { - struct sock *sk = sock->sk; - struct econet_opt *eo = ec_sk(sk); + struct sock *sk; + struct econet_sock *eo; struct sockaddr_ec *sec = (struct sockaddr_ec *)uaddr; if (peer) return -EOPNOTSUPP; + mutex_lock(&econet_mutex); + + sk = sock->sk; + eo = ec_sk(sk); + sec->sec_family = AF_ECONET; sec->port = eo->port; sec->addr.station = eo->station; sec->addr.net = eo->net; + mutex_unlock(&econet_mutex); + *uaddr_len = sizeof(*sec); return 0; } @@ -508,10 +558,13 @@ static void econet_destroy_timer(unsigned long data) static int econet_release(struct socket *sock) { - struct sock *sk = sock->sk; + struct sock *sk; + mutex_lock(&econet_mutex); + + sk = sock->sk; if (!sk) - return 0; + goto out_unlock; econet_remove_socket(&econet_sklist, sk); @@ -535,13 +588,23 @@ static int econet_release(struct socket *sock) sk->sk_timer.expires = jiffies + HZ; sk->sk_timer.function = econet_destroy_timer; add_timer(&sk->sk_timer); - return 0; + + goto out_unlock; } sk_free(sk); + +out_unlock: + mutex_unlock(&econet_mutex); return 0; } +static struct proto econet_proto = { + .name = "ECONET", + .owner = THIS_MODULE, + .obj_size = sizeof(struct econet_sock), +}; + /* * Create an Econet socket */ @@ -549,7 +612,7 @@ static int econet_release(struct socket *sock) static int econet_create(struct socket *sock, int protocol) { struct sock *sk; - struct econet_opt *eo; + struct econet_sock *eo; int err; /* Econet only provides datagram services. */ @@ -559,28 +622,21 @@ static int econet_create(struct socket *sock, int protocol) sock->state = SS_UNCONNECTED; err = -ENOBUFS; - sk = sk_alloc(PF_ECONET, GFP_KERNEL, 1, NULL); + sk = sk_alloc(PF_ECONET, GFP_KERNEL, &econet_proto, 1); if (sk == NULL) goto out; sk->sk_reuse = 1; sock->ops = &econet_ops; - sock_init_data(sock,sk); - sk_set_owner(sk, THIS_MODULE); + sock_init_data(sock, sk); - eo = sk->sk_protinfo = kmalloc(sizeof(*eo), GFP_KERNEL); - if (!eo) - goto out_free; - memset(eo, 0, sizeof(*eo)); - sk->sk_zapped = 0; + eo = ec_sk(sk); + sock_reset_flag(sk, SOCK_ZAPPED); sk->sk_family = PF_ECONET; eo->num = protocol; econet_insert_socket(&econet_sklist, sk); return(0); - -out_free: - sk_free(sk); out: return err; } @@ -589,12 +645,13 @@ out: * Handle Econet specific ioctls */ -static int ec_dev_ioctl(struct socket *sock, unsigned int cmd, void *arg) +static int ec_dev_ioctl(struct socket *sock, unsigned int cmd, void __user *arg) { struct ifreq ifr; struct ec_device *edev; struct net_device *dev; struct sockaddr_ec *sec; + int err; /* * Fetch the caller's info block into kernel space @@ -608,38 +665,34 @@ static int ec_dev_ioctl(struct socket *sock, unsigned int cmd, void *arg) sec = (struct sockaddr_ec *)&ifr.ifr_addr; - switch (cmd) - { + mutex_lock(&econet_mutex); + + err = 0; + switch (cmd) { case SIOCSIFADDR: edev = dev->ec_ptr; - if (edev == NULL) - { + if (edev == NULL) { /* Magic up a new one. */ - edev = kmalloc(sizeof(struct ec_device), GFP_KERNEL); + edev = kzalloc(sizeof(struct ec_device), GFP_KERNEL); if (edev == NULL) { - printk("af_ec: memory squeeze.\n"); - dev_put(dev); - return -ENOMEM; + err = -ENOMEM; + break; } - memset(edev, 0, sizeof(struct ec_device)); dev->ec_ptr = edev; - } - else + } else net2dev_map[edev->net] = NULL; edev->station = sec->addr.station; edev->net = sec->addr.net; net2dev_map[sec->addr.net] = dev; if (!net2dev_map[0]) net2dev_map[0] = dev; - dev_put(dev); - return 0; + break; case SIOCGIFADDR: edev = dev->ec_ptr; - if (edev == NULL) - { - dev_put(dev); - return -ENODEV; + if (edev == NULL) { + err = -ENODEV; + break; } memset(sec, 0, sizeof(struct sockaddr_ec)); sec->addr.station = edev->station; @@ -647,12 +700,19 @@ static int ec_dev_ioctl(struct socket *sock, unsigned int cmd, void *arg) sec->sec_family = AF_ECONET; dev_put(dev); if (copy_to_user(arg, &ifr, sizeof(struct ifreq))) - return -EFAULT; - return 0; + err = -EFAULT; + break; + + default: + err = -EINVAL; + break; } + mutex_unlock(&econet_mutex); + dev_put(dev); - return -EINVAL; + + return err; } /* @@ -662,18 +722,19 @@ static int ec_dev_ioctl(struct socket *sock, unsigned int cmd, void *arg) static int econet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) { struct sock *sk = sock->sk; + void __user *argp = (void __user *)arg; switch(cmd) { case SIOCGSTAMP: - return sock_get_timestamp(sk,(struct timeval *)arg); + return sock_get_timestamp(sk, argp); case SIOCSIFADDR: case SIOCGIFADDR: - return ec_dev_ioctl(sock, cmd, (void *)arg); + return ec_dev_ioctl(sock, cmd, argp); break; default: - return dev_ioctl(cmd,(void *) arg); + return -ENOIOCTLCMD; } /*NOTREACHED*/ return 0; @@ -685,7 +746,7 @@ static struct net_proto_family econet_family_ops = { .owner = THIS_MODULE, }; -static struct proto_ops SOCKOPS_WRAPPED(econet_ops) = { +static const struct proto_ops econet_ops = { .family = PF_ECONET, .owner = THIS_MODULE, .release = econet_release, @@ -706,9 +767,7 @@ static struct proto_ops SOCKOPS_WRAPPED(econet_ops) = { .sendpage = sock_no_sendpage, }; -#include -SOCKOPS_WRAP(econet, PF_ECONET); - +#if defined(CONFIG_ECONET_AUNUDP) || defined(CONFIG_ECONET_NATIVE) /* * Find the listening socket, if any, for the given data. */ @@ -720,7 +779,7 @@ static struct sock *ec_listening_socket(unsigned char port, unsigned char struct hlist_node *node; sk_for_each(sk, node, &econet_sklist) { - struct econet_opt *opt = ec_sk(sk); + struct econet_sock *opt = ec_sk(sk); if ((opt->port == port || opt->port == 0) && (opt->station == station || opt->station == 0) && (opt->net == net || opt->net == 0)) @@ -752,47 +811,31 @@ static int ec_queue_packet(struct sock *sk, struct sk_buff *skb, return sock_queue_rcv_skb(sk, skb); } +#endif #ifdef CONFIG_ECONET_AUNUDP - /* * Send an AUN protocol response. */ static void aun_send_response(__u32 addr, unsigned long seq, int code, int cb) { - struct sockaddr_in sin; - struct iovec iov; - struct aunhdr ah; + struct sockaddr_in sin = { + .sin_family = AF_INET, + .sin_port = htons(AUN_PORT), + .sin_addr = {.s_addr = addr} + }; + struct aunhdr ah = {.code = code, .cb = cb, .handle = seq}; + struct kvec iov = {.iov_base = (void *)&ah, .iov_len = sizeof(ah)}; struct msghdr udpmsg; - int err; - mm_segment_t oldfs; - memset(&sin, 0, sizeof(sin)); - sin.sin_family = AF_INET; - sin.sin_port = htons(AUN_PORT); - sin.sin_addr.s_addr = addr; - - ah.code = code; - ah.pad = 0; - ah.port = 0; - ah.cb = cb; - ah.handle = seq; - - iov.iov_base = (void *)&ah; - iov.iov_len = sizeof(ah); - udpmsg.msg_name = (void *)&sin; udpmsg.msg_namelen = sizeof(sin); - udpmsg.msg_iov = &iov; - udpmsg.msg_iovlen = 1; udpmsg.msg_control = NULL; udpmsg.msg_controllen = 0; udpmsg.msg_flags=0; - oldfs = get_fs(); set_fs(KERNEL_DS); - err = sock_sendmsg(udpsock, &udpmsg, sizeof(ah)); - set_fs(oldfs); + kernel_sendmsg(udpsock, &udpmsg, &iov, 1, sizeof(ah)); } @@ -871,7 +914,7 @@ static void aun_tx_ack(unsigned long seq, int result) foundit: tx_result(skb->sk, eb->cookie, result); - skb_unlink(skb); + skb_unlink(skb, &aun_queue); spin_unlock_irqrestore(&aun_queue_lock, flags); kfree_skb(skb); } @@ -949,7 +992,7 @@ static void ab_cleanup(unsigned long h) { tx_result(skb->sk, eb->cookie, ECTYPE_TRANSMIT_NOT_PRESENT); - skb_unlink(skb); + skb_unlink(skb, &aun_queue); kfree_skb(skb); } skb = newskb; @@ -1011,7 +1054,7 @@ release: * Receive an Econet frame from a device. */ -static int econet_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt) +static int econet_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev) { struct ec_framehdr *hdr; struct sock *sk; @@ -1078,7 +1121,7 @@ static int econet_notifier(struct notifier_block *this, unsigned long msg, void if (edev) { if (net2dev_map[0] == dev) - net2dev_map[0] = 0; + net2dev_map[0] = NULL; net2dev_map[edev->net] = NULL; kfree(edev); dev->ec_ptr = NULL; @@ -1102,10 +1145,15 @@ static void __exit econet_proto_exit(void) #endif unregister_netdevice_notifier(&econet_netdev_notifier); sock_unregister(econet_family_ops.family); + proto_unregister(&econet_proto); } static int __init econet_proto_init(void) { + int err = proto_register(&econet_proto, 0); + + if (err != 0) + goto out; sock_register(&econet_family_ops); #ifdef CONFIG_ECONET_AUNUDP spin_lock_init(&aun_queue_lock); @@ -1115,7 +1163,8 @@ static int __init econet_proto_init(void) econet_hw_initialise(); #endif register_netdevice_notifier(&econet_netdev_notifier); - return 0; +out: + return err; } module_init(econet_proto_init);