#include <net/udp.h>
#include <net/ip.h>
#include <linux/spinlock.h>
+#include <linux/rcupdate.h>
+#include <linux/bitops.h>
#include <asm/uaccess.h>
#include <asm/system.h>
-#include <asm/bitops.h>
static struct proto_ops econet_ops;
static struct hlist_head econet_sklist;
-static rwlock_t econet_lock = RW_LOCK_UNLOCKED;
+static DEFINE_RWLOCK(econet_lock);
/* Since there are only 256 possible network numbers (or fewer, depends
how you count) it makes sense to use a simple lookup table. */
return 0;
}
+#if defined(CONFIG_ECONET_AUNUDP) || defined(CONFIG_ECONET_NATIVE)
/*
* Queue a transmit result for the user to be told about.
*/
if (sock_queue_rcv_skb(sk, skb) < 0)
kfree_skb(skb);
}
+#endif
#ifdef CONFIG_ECONET_NATIVE
/*
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;
* 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.
{
/* 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),
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(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);
}
/* 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)
* 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;
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 dev_ioctl(cmd, argp);
}
/*NOTREACHED*/
return 0;
#include <linux/smp_lock.h>
SOCKOPS_WRAP(econet, PF_ECONET);
+#if defined(CONFIG_ECONET_AUNUDP) || defined(CONFIG_ECONET_NATIVE)
/*
* Find the listening socket, if any, for the given data.
*/
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));
}
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;