* See the GNU General Public License for more details.
*/
#include <linux/config.h>
+#include <linux/compiler.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/rtnetlink.h>
static u16 llc_ui_sap_last_autoport = LLC_SAP_DYN_START;
static u16 llc_ui_sap_link_no_max[256];
static struct sockaddr_llc llc_ui_addrnull;
-static struct proto_ops llc_ui_ops;
+static const struct proto_ops llc_ui_ops;
-static int llc_ui_wait_for_conn(struct sock *sk, int timeout);
-static int llc_ui_wait_for_disc(struct sock *sk, int timeout);
-static int llc_ui_wait_for_data(struct sock *sk, int timeout);
-static int llc_ui_wait_for_busy_core(struct sock *sk, int timeout);
+static int llc_ui_wait_for_conn(struct sock *sk, long timeout);
+static int llc_ui_wait_for_disc(struct sock *sk, long timeout);
+static int llc_ui_wait_for_busy_core(struct sock *sk, long timeout);
#if 0
#define dprintk(args...) printk(KERN_DEBUG args)
struct llc_sock* llc = llc_sk(sk);
int rc = 0;
- if (llc_data_accept_state(llc->state) || llc->p_flag) {
- int timeout = sock_sndtimeo(sk, noblock);
+ if (unlikely(llc_data_accept_state(llc->state) ||
+ llc->remote_busy_flag ||
+ llc->p_flag)) {
+ long timeout = sock_sndtimeo(sk, noblock);
rc = llc_ui_wait_for_busy_core(sk, timeout);
}
- if (!rc)
+ if (unlikely(!rc))
rc = llc_build_and_send_pkt(sk, skb);
return rc;
}
if (!sock_flag(sk, SOCK_ZAPPED))
llc_sap_remove_socket(llc->sap, sk);
release_sock(sk);
- if (llc->sap && hlist_empty(&llc->sap->sk_list.list)) {
- llc_release_sockets(llc->sap);
- llc_sap_close(llc->sap);
- }
if (llc->dev)
dev_put(llc->dev);
sock_put(sk);
llc_ui_sap_last_autoport = i + 2;
goto out;
}
+ llc_sap_put(sap);
}
llc_ui_sap_last_autoport = LLC_SAP_DYN_START;
tries++;
rc = -EAFNOSUPPORT;
if (unlikely(addr->sllc_family != AF_LLC))
goto out;
+ rc = -ENODEV;
+ rtnl_lock();
+ llc->dev = dev_getbyhwaddr(addr->sllc_arphrd, addr->sllc_mac);
+ rtnl_unlock();
+ if (!llc->dev)
+ goto out;
if (!addr->sllc_sap) {
rc = -EUSERS;
addr->sllc_sap = llc_ui_autoport();
rc = -EBUSY; /* some other network layer is using the sap */
if (!sap)
goto out;
+ llc_sap_hold(sap);
} else {
struct llc_addr laddr, daddr;
struct sock *ask;
ask = llc_lookup_established(sap, &daddr, &laddr);
if (ask) {
sock_put(ask);
- goto out;
+ goto out_put;
}
}
llc->laddr.lsap = addr->sllc_sap;
llc_sap_add_socket(sap, sk);
sock_reset_flag(sk, SOCK_ZAPPED);
rc = 0;
+out_put:
+ llc_sap_put(sap);
out:
return rc;
}
rc = llc_ui_autobind(sock, addr);
if (rc)
goto out;
- llc->daddr.lsap = addr->sllc_sap;
- memcpy(llc->daddr.mac, addr->sllc_mac, IFHWADDRLEN);
}
+ llc->daddr.lsap = addr->sllc_sap;
+ memcpy(llc->daddr.mac, addr->sllc_mac, IFHWADDRLEN);
sock->state = SS_CONNECTING;
sk->sk_state = TCP_SYN_SENT;
llc->link = llc_ui_next_link_no(llc->sap->laddr.lsap);
}
if (sk->sk_state == TCP_SYN_SENT) {
- const int timeo = sock_sndtimeo(sk, flags & O_NONBLOCK);
+ const long timeo = sock_sndtimeo(sk, flags & O_NONBLOCK);
if (!timeo || !llc_ui_wait_for_conn(sk, timeo))
goto out;
return rc;
}
-static int llc_ui_wait_for_disc(struct sock *sk, int timeout)
+static int llc_ui_wait_for_disc(struct sock *sk, long timeout)
{
DEFINE_WAIT(wait);
int rc = 0;
- prepare_to_wait(sk->sk_sleep, &wait, TASK_INTERRUPTIBLE);
- while (sk->sk_state != TCP_CLOSE) {
- release_sock(sk);
- timeout = schedule_timeout(timeout);
- lock_sock(sk);
+ while (1) {
+ prepare_to_wait(sk->sk_sleep, &wait, TASK_INTERRUPTIBLE);
+ if (sk_wait_event(sk, &timeout, sk->sk_state == TCP_CLOSE))
+ break;
rc = -ERESTARTSYS;
if (signal_pending(current))
break;
if (!timeout)
break;
rc = 0;
- prepare_to_wait(sk->sk_sleep, &wait, TASK_INTERRUPTIBLE);
}
finish_wait(sk->sk_sleep, &wait);
return rc;
}
-static int llc_ui_wait_for_conn(struct sock *sk, int timeout)
+static int llc_ui_wait_for_conn(struct sock *sk, long timeout)
{
DEFINE_WAIT(wait);
- prepare_to_wait(sk->sk_sleep, &wait, TASK_INTERRUPTIBLE);
-
- while (sk->sk_state == TCP_SYN_SENT) {
- release_sock(sk);
- timeout = schedule_timeout(timeout);
- lock_sock(sk);
+ while (1) {
+ prepare_to_wait(sk->sk_sleep, &wait, TASK_INTERRUPTIBLE);
+ if (sk_wait_event(sk, &timeout, sk->sk_state != TCP_SYN_SENT))
+ break;
if (signal_pending(current) || !timeout)
break;
- prepare_to_wait(sk->sk_sleep, &wait, TASK_INTERRUPTIBLE);
}
finish_wait(sk->sk_sleep, &wait);
return timeout;
}
-static int llc_ui_wait_for_data(struct sock *sk, int timeout)
+static int llc_ui_wait_for_busy_core(struct sock *sk, long timeout)
{
DEFINE_WAIT(wait);
- int rc = 0;
+ struct llc_sock *llc = llc_sk(sk);
+ int rc;
- for (;;) {
+ while (1) {
prepare_to_wait(sk->sk_sleep, &wait, TASK_INTERRUPTIBLE);
- if (sk->sk_shutdown & RCV_SHUTDOWN)
- break;
- if (!skb_queue_empty(&sk->sk_receive_queue))
+ rc = 0;
+ if (sk_wait_event(sk, &timeout,
+ (sk->sk_shutdown & RCV_SHUTDOWN) ||
+ (!llc_data_accept_state(llc->state) &&
+ !llc->remote_busy_flag &&
+ !llc->p_flag)))
break;
- release_sock(sk);
- timeout = schedule_timeout(timeout);
- lock_sock(sk);
rc = -ERESTARTSYS;
if (signal_pending(current))
break;
rc = -EAGAIN;
if (!timeout)
break;
- rc = 0;
}
finish_wait(sk->sk_sleep, &wait);
return rc;
}
-static int llc_ui_wait_for_busy_core(struct sock *sk, int timeout)
+static int llc_wait_data(struct sock *sk, long timeo)
{
- DEFINE_WAIT(wait);
- struct llc_sock *llc = llc_sk(sk);
int rc;
- for (;;) {
- prepare_to_wait(sk->sk_sleep, &wait, TASK_INTERRUPTIBLE);
- rc = -ENOTCONN;
- if (sk->sk_shutdown & RCV_SHUTDOWN)
+ while (1) {
+ /*
+ * POSIX 1003.1g mandates this order.
+ */
+ rc = sock_error(sk);
+ if (rc)
break;
rc = 0;
- if (!llc_data_accept_state(llc->state) && !llc->p_flag)
+ if (sk->sk_shutdown & RCV_SHUTDOWN)
break;
- release_sock(sk);
- timeout = schedule_timeout(timeout);
- lock_sock(sk);
- rc = -ERESTARTSYS;
+ rc = -EAGAIN;
+ if (!timeo)
+ break;
+ rc = sock_intr_errno(timeo);
if (signal_pending(current))
break;
- rc = -EAGAIN;
- if (!timeout)
+ rc = 0;
+ if (sk_wait_data(sk, &timeo))
break;
}
- finish_wait(sk->sk_sleep, &wait);
return rc;
}
goto out;
/* wait for a connection to arrive. */
if (skb_queue_empty(&sk->sk_receive_queue)) {
- rc = llc_ui_wait_for_data(sk, sk->sk_rcvtimeo);
+ rc = llc_wait_data(sk, sk->sk_rcvtimeo);
if (rc)
goto out;
}
/* put original socket back into a clean listen state. */
sk->sk_state = TCP_LISTEN;
sk->sk_ack_backlog--;
- skb->sk = NULL;
dprintk("%s: ok success on %02X, client on %02X\n", __FUNCTION__,
llc_sk(sk)->addr.sllc_sap, newllc->daddr.lsap);
frees:
* llc_ui_recvmsg - copy received data to the socket user.
* @sock: Socket to copy data from.
* @msg: Various user space related information.
- * @size: Size of user buffer.
+ * @len: Size of user buffer.
* @flags: User specified flags.
*
* Copy received data to the socket user.
* Returns non-negative upon success, negative otherwise.
*/
static int llc_ui_recvmsg(struct kiocb *iocb, struct socket *sock,
- struct msghdr *msg, size_t size, int flags)
+ struct msghdr *msg, size_t len, int flags)
{
- struct sock *sk = sock->sk;
struct sockaddr_llc *uaddr = (struct sockaddr_llc *)msg->msg_name;
- struct sk_buff *skb;
+ const int nonblock = flags & MSG_DONTWAIT;
+ struct sk_buff *skb = NULL;
+ struct sock *sk = sock->sk;
+ struct llc_sock *llc = llc_sk(sk);
size_t copied = 0;
- int rc = -ENOMEM;
- int noblock = flags & MSG_DONTWAIT;
+ u32 peek_seq = 0;
+ u32 *seq;
+ unsigned long used;
+ int target; /* Read at least this many bytes */
+ long timeo;
- dprintk("%s: receiving in %02X from %02X\n", __FUNCTION__,
- llc_sk(sk)->laddr.lsap, llc_sk(sk)->daddr.lsap);
lock_sock(sk);
- if (skb_queue_empty(&sk->sk_receive_queue)) {
- rc = llc_ui_wait_for_data(sk, sock_rcvtimeo(sk, noblock));
- if (rc)
- goto out;
- }
- skb = skb_dequeue(&sk->sk_receive_queue);
- if (!skb) /* shutdown */
+ copied = -ENOTCONN;
+ if (sk->sk_state == TCP_LISTEN)
goto out;
- copied = skb->len;
- if (copied > size)
- copied = size;
- rc = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
- if (rc)
- goto dgram_free;
- if (skb->len > copied) {
- skb_pull(skb, copied);
- skb_queue_head(&sk->sk_receive_queue, skb);
- }
- if (uaddr)
- memcpy(uaddr, llc_ui_skb_cb(skb), sizeof(*uaddr));
- msg->msg_namelen = sizeof(*uaddr);
- if (!skb->next) {
-dgram_free:
- kfree_skb(skb);
- }
+
+ timeo = sock_rcvtimeo(sk, nonblock);
+
+ seq = &llc->copied_seq;
+ if (flags & MSG_PEEK) {
+ peek_seq = llc->copied_seq;
+ seq = &peek_seq;
+ }
+
+ target = sock_rcvlowat(sk, flags & MSG_WAITALL, len);
+ copied = 0;
+
+ do {
+ u32 offset;
+
+ /*
+ * We need to check signals first, to get correct SIGURG
+ * handling. FIXME: Need to check this doesn't impact 1003.1g
+ * and move it down to the bottom of the loop
+ */
+ if (signal_pending(current)) {
+ if (copied)
+ break;
+ copied = timeo ? sock_intr_errno(timeo) : -EAGAIN;
+ break;
+ }
+
+ /* Next get a buffer. */
+
+ skb = skb_peek(&sk->sk_receive_queue);
+ if (skb) {
+ offset = *seq;
+ goto found_ok_skb;
+ }
+ /* Well, if we have backlog, try to process it now yet. */
+
+ if (copied >= target && !sk->sk_backlog.tail)
+ break;
+
+ if (copied) {
+ if (sk->sk_err ||
+ sk->sk_state == TCP_CLOSE ||
+ (sk->sk_shutdown & RCV_SHUTDOWN) ||
+ !timeo ||
+ (flags & MSG_PEEK))
+ break;
+ } else {
+ if (sock_flag(sk, SOCK_DONE))
+ break;
+
+ if (sk->sk_err) {
+ copied = sock_error(sk);
+ break;
+ }
+ if (sk->sk_shutdown & RCV_SHUTDOWN)
+ break;
+
+ if (sk->sk_state == TCP_CLOSE) {
+ if (!sock_flag(sk, SOCK_DONE)) {
+ /*
+ * This occurs when user tries to read
+ * from never connected socket.
+ */
+ copied = -ENOTCONN;
+ break;
+ }
+ break;
+ }
+ if (!timeo) {
+ copied = -EAGAIN;
+ break;
+ }
+ }
+
+ if (copied >= target) { /* Do not sleep, just process backlog. */
+ release_sock(sk);
+ lock_sock(sk);
+ } else
+ sk_wait_data(sk, &timeo);
+
+ if ((flags & MSG_PEEK) && peek_seq != llc->copied_seq) {
+ if (net_ratelimit())
+ printk(KERN_DEBUG "LLC(%s:%d): Application "
+ "bug, race in MSG_PEEK.\n",
+ current->comm, current->pid);
+ peek_seq = llc->copied_seq;
+ }
+ continue;
+ found_ok_skb:
+ /* Ok so how much can we use? */
+ used = skb->len - offset;
+ if (len < used)
+ used = len;
+
+ if (!(flags & MSG_TRUNC)) {
+ int rc = skb_copy_datagram_iovec(skb, offset,
+ msg->msg_iov, used);
+ if (rc) {
+ /* Exception. Bailout! */
+ if (!copied)
+ copied = -EFAULT;
+ break;
+ }
+ }
+
+ *seq += used;
+ copied += used;
+ len -= used;
+
+ if (used + offset < skb->len)
+ continue;
+
+ if (!(flags & MSG_PEEK)) {
+ sk_eat_skb(sk, skb);
+ *seq = 0;
+ }
+ } while (len > 0);
+
+ /*
+ * According to UNIX98, msg_name/msg_namelen are ignored
+ * on connected socket. -ANK
+ * But... af_llc still doesn't have separate sets of methods for
+ * SOCK_DGRAM and SOCK_STREAM :-( So we have to do this test, will
+ * eventually fix this tho :-) -acme
+ */
+ if (sk->sk_type == SOCK_DGRAM)
+ goto copy_uaddr;
out:
release_sock(sk);
- return rc ? : copied;
+ return copied;
+copy_uaddr:
+ if (uaddr != NULL && skb != NULL) {
+ memcpy(uaddr, llc_ui_skb_cb(skb), sizeof(*uaddr));
+ msg->msg_namelen = sizeof(*uaddr);
+ }
+ goto out;
}
/**
lock_sock(sk);
if (!skb)
goto release;
- skb->sk = sk;
skb->dev = llc->dev;
skb->protocol = llc_proto_type(addr->sllc_arphrd);
skb_reserve(skb, hdrlen);
if (!(sk->sk_type == SOCK_STREAM && !addr->sllc_ua))
goto out;
rc = llc_ui_send_data(sk, skb, noblock);
- if (rc)
- dprintk("%s: llc_ui_send_data failed: %d\n", __FUNCTION__, rc);
out:
- if (rc)
+ if (rc) {
kfree_skb(skb);
release:
- if (rc)
dprintk("%s: failed sending from %02X to %02X: %d\n",
__FUNCTION__, llc->laddr.lsap, llc->daddr.lsap, rc);
+ }
release_sock(sk);
return rc ? : copied;
}
static int llc_ui_ioctl(struct socket *sock, unsigned int cmd,
unsigned long arg)
{
- return dev_ioctl(cmd, (void __user *)arg);
+ return -ENOIOCTLCMD;
}
/**
case LLC_OPT_ACK_TMR_EXP:
if (opt > LLC_OPT_MAX_ACK_TMR_EXP)
goto out;
- llc->ack_timer.expire = opt;
+ llc->ack_timer.expire = opt * HZ;
break;
case LLC_OPT_P_TMR_EXP:
if (opt > LLC_OPT_MAX_P_TMR_EXP)
goto out;
- llc->pf_cycle_timer.expire = opt;
+ llc->pf_cycle_timer.expire = opt * HZ;
break;
case LLC_OPT_REJ_TMR_EXP:
if (opt > LLC_OPT_MAX_REJ_TMR_EXP)
goto out;
- llc->rej_sent_timer.expire = opt;
+ llc->rej_sent_timer.expire = opt * HZ;
break;
case LLC_OPT_BUSY_TMR_EXP:
if (opt > LLC_OPT_MAX_BUSY_TMR_EXP)
goto out;
- llc->busy_state_timer.expire = opt;
+ llc->busy_state_timer.expire = opt * HZ;
break;
case LLC_OPT_TX_WIN:
if (opt > LLC_OPT_MAX_WIN)
goto out;
switch (optname) {
case LLC_OPT_RETRY:
- val = llc->n2; break;
+ val = llc->n2; break;
case LLC_OPT_SIZE:
- val = llc->n1; break;
+ val = llc->n1; break;
case LLC_OPT_ACK_TMR_EXP:
- val = llc->ack_timer.expire; break;
+ val = llc->ack_timer.expire / HZ; break;
case LLC_OPT_P_TMR_EXP:
- val = llc->pf_cycle_timer.expire; break;
+ val = llc->pf_cycle_timer.expire / HZ; break;
case LLC_OPT_REJ_TMR_EXP:
- val = llc->rej_sent_timer.expire; break;
+ val = llc->rej_sent_timer.expire / HZ; break;
case LLC_OPT_BUSY_TMR_EXP:
- val = llc->busy_state_timer.expire; break;
+ val = llc->busy_state_timer.expire / HZ; break;
case LLC_OPT_TX_WIN:
val = llc->k; break;
case LLC_OPT_RX_WIN:
.owner = THIS_MODULE,
};
-static struct proto_ops llc_ui_ops = {
+static const struct proto_ops llc_ui_ops = {
.family = PF_LLC,
.owner = THIS_MODULE,
.release = llc_ui_release,
.sendpage = sock_no_sendpage,
};
-extern void llc_sap_handler(struct llc_sap *sap, struct sk_buff *skb);
-extern void llc_conn_handler(struct llc_sap *sap, struct sk_buff *skb);
+static char llc_proc_err_msg[] __initdata =
+ KERN_CRIT "LLC: Unable to register the proc_fs entries\n";
+static char llc_sysctl_err_msg[] __initdata =
+ KERN_CRIT "LLC: Unable to register the sysctl entries\n";
+static char llc_sock_err_msg[] __initdata =
+ KERN_CRIT "LLC: Unable to register the network family\n";
static int __init llc2_init(void)
{
llc_station_init();
llc_ui_sap_last_autoport = LLC_SAP_DYN_START;
rc = llc_proc_init();
- if (rc != 0)
+ if (rc != 0) {
+ printk(llc_proc_err_msg);
goto out_unregister_llc_proto;
- sock_register(&llc_ui_family_ops);
+ }
+ rc = llc_sysctl_init();
+ if (rc) {
+ printk(llc_sysctl_err_msg);
+ goto out_proc;
+ }
+ rc = sock_register(&llc_ui_family_ops);
+ if (rc) {
+ printk(llc_sock_err_msg);
+ goto out_sysctl;
+ }
llc_add_pack(LLC_DEST_SAP, llc_sap_handler);
llc_add_pack(LLC_DEST_CONN, llc_conn_handler);
out:
return rc;
+out_sysctl:
+ llc_sysctl_exit();
+out_proc:
+ llc_proc_exit();
out_unregister_llc_proto:
proto_unregister(&llc_proto);
goto out;
llc_remove_pack(LLC_DEST_CONN);
sock_unregister(PF_LLC);
llc_proc_exit();
+ llc_sysctl_exit();
proto_unregister(&llc_proto);
}