11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* net/atm/pppoatm.c - RFC2364 PPP over ATM/AAL5 */ 21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Copyright 1999-2000 by Mitchell Blank Jr */ 41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Based on clip.c; 1995-1999 by Werner Almesberger, EPFL LRC/ICA */ 51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* And on ppp_async.c; Copyright 1999 Paul Mackerras */ 61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* And help from Jens Axboe */ 71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This program is free software; you can redistribute it and/or 101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * modify it under the terms of the GNU General Public License 111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * as published by the Free Software Foundation; either version 121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 2 of the License, or (at your option) any later version. 131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This driver provides the encapsulation and framing for sending 151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * and receiving PPP frames in ATM AAL5 PDUs. 161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * One shortcoming of this driver is that it does not comply with 201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * section 8 of RFC2364 - we are supposed to detect a change 211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * in encapsulation and immediately abort the connection (in order 221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * to avoid a black-hole being created if our peer loses state 231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * and changes encapsulation unilaterally. However, since the 241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * ppp_generic layer actually does the decapsulation, we need 251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * a way of notifying it when we _think_ there might be a problem) 261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * There's two cases: 271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 1. LLC-encapsulation was missing when it was enabled. In 281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * this case, we should tell the upper layer "tear down 291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * this session if this skb looks ok to you" 301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 2. LLC-encapsulation was present when it was disabled. Then 311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * we need to tell the upper layer "this packet may be 321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * ok, but if its in error tear down the session" 331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * These hooks are not yet available in ppp_generic 341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3699824461ea72ca0044cc6508f02c0e1cabf37ba5Joe Perches#define pr_fmt(fmt) KBUILD_MODNAME ":%s: " fmt, __func__ 3799824461ea72ca0044cc6508f02c0e1cabf37ba5Joe Perches 381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h> 391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h> 40a6b7a407865aab9f849dd99a71072b7cd1175116Alexey Dobriyan#include <linux/interrupt.h> 411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/skbuff.h> 425a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h> 431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/atm.h> 441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/atmdev.h> 454fc268d24ceb9f4150777c1b5b2b8e6214e56b2bRandy Dunlap#include <linux/capability.h> 461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/ppp_defs.h> 474b32da2bcf1de2b7a196a0e48389d231b4472c36Paul Mackerras#include <linux/ppp-ioctl.h> 481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/ppp_channel.h> 491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/atmppp.h> 501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include "common.h" 521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsenum pppoatm_encaps { 541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds e_autodetect = PPPOATM_ENCAPS_AUTODETECT, 551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds e_vc = PPPOATM_ENCAPS_VC, 561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds e_llc = PPPOATM_ENCAPS_LLC, 571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}; 581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstruct pppoatm_vcc { 601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct atm_vcc *atmvcc; /* VCC descriptor */ 611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds void (*old_push)(struct atm_vcc *, struct sk_buff *); 621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds void (*old_pop)(struct atm_vcc *, struct sk_buff *); 630e56d99a5b557c760394d6941d7d1fc8d279eff3David Woodhouse void (*old_release_cb)(struct atm_vcc *); 64e41faed9cde1acce657f75a0b19a1787e9850d3fKrzysztof Mazur struct module *old_owner; 651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* keep old push/pop for detaching */ 661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds enum pppoatm_encaps encaps; 679d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse atomic_t inflight; 689d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse unsigned long blocked; 691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int flags; /* SC_COMP_PROT - compress protocol */ 701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct ppp_channel chan; /* interface to generic ppp layer */ 711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct tasklet_struct wakeup_tasklet; 721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}; 731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 759d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse * We want to allow two packets in the queue. The one that's currently in 769d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse * flight, and *one* queued up ready for the ATM device to send immediately 779d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse * from its TX done IRQ. We want to be able to use atomic_inc_not_zero(), so 789d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse * inflight == -2 represents an empty queue, -1 one packet, and zero means 799d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse * there are two packets in the queue. 809d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse */ 819d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse#define NONE_INFLIGHT -2 829d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse 839d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse#define BLOCKED 0 849d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse 859d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse/* 861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Header used for LLC Encapsulated PPP (4 bytes) followed by the LCP protocol 871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * ID (0xC021) used in autodetection 881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic const unsigned char pppllc[6] = { 0xFE, 0xFE, 0x03, 0xCF, 0xC0, 0x21 }; 901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define LLC_LEN (4) 911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic inline struct pppoatm_vcc *atmvcc_to_pvcc(const struct atm_vcc *atmvcc) 931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return (struct pppoatm_vcc *) (atmvcc->user_back); 951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic inline struct pppoatm_vcc *chan_to_pvcc(const struct ppp_channel *chan) 981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return (struct pppoatm_vcc *) (chan->private); 1001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 1011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 1031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * We can't do this directly from our _pop handler, since the ppp code 1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * doesn't want to be called in interrupt context, so we do it from 1051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * a tasklet 1061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 1071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void pppoatm_wakeup_sender(unsigned long arg) 1081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 1091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ppp_output_wakeup((struct ppp_channel *) arg); 1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1120e56d99a5b557c760394d6941d7d1fc8d279eff3David Woodhousestatic void pppoatm_release_cb(struct atm_vcc *atmvcc) 1130e56d99a5b557c760394d6941d7d1fc8d279eff3David Woodhouse{ 1140e56d99a5b557c760394d6941d7d1fc8d279eff3David Woodhouse struct pppoatm_vcc *pvcc = atmvcc_to_pvcc(atmvcc); 1150e56d99a5b557c760394d6941d7d1fc8d279eff3David Woodhouse 1165b4d72080f49498d2390563aa90f5bc31785406cDavid Woodhouse /* 1175b4d72080f49498d2390563aa90f5bc31785406cDavid Woodhouse * As in pppoatm_pop(), it's safe to clear the BLOCKED bit here because 1185b4d72080f49498d2390563aa90f5bc31785406cDavid Woodhouse * the wakeup *can't* race with pppoatm_send(). They both hold the PPP 1195b4d72080f49498d2390563aa90f5bc31785406cDavid Woodhouse * channel's ->downl lock. And the potential race with *setting* it, 1205b4d72080f49498d2390563aa90f5bc31785406cDavid Woodhouse * which leads to the double-check dance in pppoatm_may_send(), doesn't 1215b4d72080f49498d2390563aa90f5bc31785406cDavid Woodhouse * exist here. In the sock_owned_by_user() case in pppoatm_send(), we 1225b4d72080f49498d2390563aa90f5bc31785406cDavid Woodhouse * set the BLOCKED bit while the socket is still locked. We know that 1235b4d72080f49498d2390563aa90f5bc31785406cDavid Woodhouse * ->release_cb() can't be called until that's done. 1245b4d72080f49498d2390563aa90f5bc31785406cDavid Woodhouse */ 1255b4d72080f49498d2390563aa90f5bc31785406cDavid Woodhouse if (test_and_clear_bit(BLOCKED, &pvcc->blocked)) 1265b4d72080f49498d2390563aa90f5bc31785406cDavid Woodhouse tasklet_schedule(&pvcc->wakeup_tasklet); 1270e56d99a5b557c760394d6941d7d1fc8d279eff3David Woodhouse if (pvcc->old_release_cb) 1280e56d99a5b557c760394d6941d7d1fc8d279eff3David Woodhouse pvcc->old_release_cb(atmvcc); 1290e56d99a5b557c760394d6941d7d1fc8d279eff3David Woodhouse} 1301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 1311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This gets called every time the ATM card has finished sending our 1321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * skb. The ->old_pop will take care up normal atm flow control, 1331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * but we also need to wake up the device if we blocked it 1341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 1351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void pppoatm_pop(struct atm_vcc *atmvcc, struct sk_buff *skb) 1361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 1371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct pppoatm_vcc *pvcc = atmvcc_to_pvcc(atmvcc); 1389d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse 1391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds pvcc->old_pop(atmvcc, skb); 1409d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse atomic_dec(&pvcc->inflight); 1419d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse 1421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* 1439d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse * We always used to run the wakeup tasklet unconditionally here, for 1449d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse * fear of race conditions where we clear the BLOCKED flag just as we 1459d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse * refuse another packet in pppoatm_send(). This was quite inefficient. 1469d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse * 1479d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse * In fact it's OK. The PPP core will only ever call pppoatm_send() 1489d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse * while holding the channel->downl lock. And ppp_output_wakeup() as 1499d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse * called by the tasklet will *also* grab that lock. So even if another 1509d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse * CPU is in pppoatm_send() right now, the tasklet isn't going to race 1519d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse * with it. The wakeup *will* happen after the other CPU is safely out 1529d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse * of pppoatm_send() again. 1539d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse * 1549d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse * So if the CPU in pppoatm_send() has already set the BLOCKED bit and 1559d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse * it about to return, that's fine. We trigger a wakeup which will 1569d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse * happen later. And if the CPU in pppoatm_send() *hasn't* set the 1579d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse * BLOCKED bit yet, that's fine too because of the double check in 1589d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse * pppoatm_may_send() which is commented there. 1591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 1609d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse if (test_and_clear_bit(BLOCKED, &pvcc->blocked)) 1619d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse tasklet_schedule(&pvcc->wakeup_tasklet); 1621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 1631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 1651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Unbind from PPP - currently we only do this when closing the socket, 1661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * but we could put this into an ioctl if need be 1671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 1681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void pppoatm_unassign_vcc(struct atm_vcc *atmvcc) 1691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 1701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct pppoatm_vcc *pvcc; 1711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds pvcc = atmvcc_to_pvcc(atmvcc); 1721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds atmvcc->push = pvcc->old_push; 1731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds atmvcc->pop = pvcc->old_pop; 1740e56d99a5b557c760394d6941d7d1fc8d279eff3David Woodhouse atmvcc->release_cb = pvcc->old_release_cb; 1751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds tasklet_kill(&pvcc->wakeup_tasklet); 1761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ppp_unregister_channel(&pvcc->chan); 1771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds atmvcc->user_back = NULL; 1781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds kfree(pvcc); 1791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 1801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Called when an AAL5 PDU comes in */ 1821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void pppoatm_push(struct atm_vcc *atmvcc, struct sk_buff *skb) 1831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 1841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct pppoatm_vcc *pvcc = atmvcc_to_pvcc(atmvcc); 18599824461ea72ca0044cc6508f02c0e1cabf37ba5Joe Perches pr_debug("\n"); 1861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (skb == NULL) { /* VCC was closed */ 187e41faed9cde1acce657f75a0b19a1787e9850d3fKrzysztof Mazur struct module *module; 188e41faed9cde1acce657f75a0b19a1787e9850d3fKrzysztof Mazur 189522400623e240ad134cb4101b1fddc3245d2a7edStephen Hemminger pr_debug("removing ATMPPP VCC %p\n", pvcc); 190e41faed9cde1acce657f75a0b19a1787e9850d3fKrzysztof Mazur module = pvcc->old_owner; 1911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds pppoatm_unassign_vcc(atmvcc); 1921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds atmvcc->push(atmvcc, NULL); /* Pass along bad news */ 193e41faed9cde1acce657f75a0b19a1787e9850d3fKrzysztof Mazur module_put(module); 1941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return; 1951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 1961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds atm_return(atmvcc, skb->truesize); 1971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds switch (pvcc->encaps) { 1981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case e_llc: 1991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (skb->len < LLC_LEN || 2001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds memcmp(skb->data, pppllc, LLC_LEN)) 2011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto error; 2021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds skb_pull(skb, LLC_LEN); 2031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 2041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case e_autodetect: 2051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (pvcc->chan.ppp == NULL) { /* Not bound yet! */ 2061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds kfree_skb(skb); 2071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return; 2081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 2091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (skb->len >= sizeof(pppllc) && 2101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds !memcmp(skb->data, pppllc, sizeof(pppllc))) { 2111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds pvcc->encaps = e_llc; 2121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds skb_pull(skb, LLC_LEN); 2131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 2141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 2151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (skb->len >= (sizeof(pppllc) - LLC_LEN) && 2161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds !memcmp(skb->data, &pppllc[LLC_LEN], 2171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds sizeof(pppllc) - LLC_LEN)) { 2181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds pvcc->encaps = e_vc; 2191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds pvcc->chan.mtu += LLC_LEN; 2201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 2211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 22299824461ea72ca0044cc6508f02c0e1cabf37ba5Joe Perches pr_debug("Couldn't autodetect yet (skb: %02X %02X %02X %02X %02X %02X)\n", 22399824461ea72ca0044cc6508f02c0e1cabf37ba5Joe Perches skb->data[0], skb->data[1], skb->data[2], 22499824461ea72ca0044cc6508f02c0e1cabf37ba5Joe Perches skb->data[3], skb->data[4], skb->data[5]); 2251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto error; 2261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case e_vc: 2271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 2281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 2291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ppp_input(&pvcc->chan, skb); 2301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return; 231d81219db6add0a176c37d6fe4e1c050778de9d2fJoe Perches 232d81219db6add0a176c37d6fe4e1c050778de9d2fJoe Percheserror: 2331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds kfree_skb(skb); 2341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ppp_input_error(&pvcc->chan, 0); 2351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 2361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 237397ff16dce53888ec693b3718640be2560204751Krzysztof Mazurstatic int pppoatm_may_send(struct pppoatm_vcc *pvcc, int size) 2389d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse{ 2399d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse /* 2409d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse * It's not clear that we need to bother with using atm_may_send() 2419d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse * to check we don't exceed sk->sk_sndbuf. If userspace sets a 2429d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse * value of sk_sndbuf which is lower than the MTU, we're going to 2439d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse * block for ever. But the code always did that before we introduced 2449d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse * the packet count limit, so... 2459d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse */ 2469d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse if (atm_may_send(pvcc->atmvcc, size) && 2479d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse atomic_inc_not_zero_hint(&pvcc->inflight, NONE_INFLIGHT)) 2489d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse return 1; 2499d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse 2509d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse /* 2519d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse * We use test_and_set_bit() rather than set_bit() here because 2529d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse * we need to ensure there's a memory barrier after it. The bit 2539d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse * *must* be set before we do the atomic_inc() on pvcc->inflight. 2549d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse * There's no smp_mb__after_set_bit(), so it's this or abuse 2554e857c58efeb99393cba5a5d0d8ec7117183137cPeter Zijlstra * smp_mb__after_atomic(). 2569d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse */ 2579d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse test_and_set_bit(BLOCKED, &pvcc->blocked); 2589d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse 2599d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse /* 2609d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse * We may have raced with pppoatm_pop(). If it ran for the 2619d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse * last packet in the queue, *just* before we set the BLOCKED 2629d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse * bit, then it might never run again and the channel could 2639d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse * remain permanently blocked. Cope with that race by checking 2649d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse * *again*. If it did run in that window, we'll have space on 2659d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse * the queue now and can return success. It's harmless to leave 2669d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse * the BLOCKED flag set, since it's only used as a trigger to 2679d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse * run the wakeup tasklet. Another wakeup will never hurt. 2689d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse * If pppoatm_pop() is running but hasn't got as far as making 2699d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse * space on the queue yet, then it hasn't checked the BLOCKED 2709d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse * flag yet either, so we're safe in that case too. It'll issue 2719d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse * an "immediate" wakeup... where "immediate" actually involves 2729d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse * taking the PPP channel's ->downl lock, which is held by the 2739d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse * code path that calls pppoatm_send(), and is thus going to 2749d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse * wait for us to finish. 2759d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse */ 2769d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse if (atm_may_send(pvcc->atmvcc, size) && 2779d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse atomic_inc_not_zero(&pvcc->inflight)) 2789d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse return 1; 2799d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse 2809d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse return 0; 2819d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse} 2821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 2831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Called by the ppp_generic.c to send a packet - returns true if packet 2841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * was accepted. If we return false, then it's our job to call 2851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * ppp_output_wakeup(chan) when we're feeling more up to it. 2861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Note that in the ENOMEM case (as opposed to the !atm_may_send case) 2871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * we should really drop the packet, but the generic layer doesn't 2881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * support this yet. We just return 'DROP_PACKET' which we actually define 2891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * as success, just to be clear what we're really doing. 2901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 2911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DROP_PACKET 1 2921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int pppoatm_send(struct ppp_channel *chan, struct sk_buff *skb) 2931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 2941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct pppoatm_vcc *pvcc = chan_to_pvcc(chan); 2953ac108006fd7f20cb8fc8ea2287f1497bcda00a1Krzysztof Mazur struct atm_vcc *vcc; 2963ac108006fd7f20cb8fc8ea2287f1497bcda00a1Krzysztof Mazur int ret; 2973ac108006fd7f20cb8fc8ea2287f1497bcda00a1Krzysztof Mazur 2981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ATM_SKB(skb)->vcc = pvcc->atmvcc; 29999824461ea72ca0044cc6508f02c0e1cabf37ba5Joe Perches pr_debug("(skb=0x%p, vcc=0x%p)\n", skb, pvcc->atmvcc); 3001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (skb->data[0] == '\0' && (pvcc->flags & SC_COMP_PROT)) 3011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds (void) skb_pull(skb, 1); 3023ac108006fd7f20cb8fc8ea2287f1497bcda00a1Krzysztof Mazur 3033ac108006fd7f20cb8fc8ea2287f1497bcda00a1Krzysztof Mazur vcc = ATM_SKB(skb)->vcc; 3043ac108006fd7f20cb8fc8ea2287f1497bcda00a1Krzysztof Mazur bh_lock_sock(sk_atm(vcc)); 3055b4d72080f49498d2390563aa90f5bc31785406cDavid Woodhouse if (sock_owned_by_user(sk_atm(vcc))) { 3065b4d72080f49498d2390563aa90f5bc31785406cDavid Woodhouse /* 3075b4d72080f49498d2390563aa90f5bc31785406cDavid Woodhouse * Needs to happen (and be flushed, hence test_and_) before we unlock 3085b4d72080f49498d2390563aa90f5bc31785406cDavid Woodhouse * the socket. It needs to be seen by the time our ->release_cb gets 3095b4d72080f49498d2390563aa90f5bc31785406cDavid Woodhouse * called. 3105b4d72080f49498d2390563aa90f5bc31785406cDavid Woodhouse */ 3115b4d72080f49498d2390563aa90f5bc31785406cDavid Woodhouse test_and_set_bit(BLOCKED, &pvcc->blocked); 3123ac108006fd7f20cb8fc8ea2287f1497bcda00a1Krzysztof Mazur goto nospace; 3135b4d72080f49498d2390563aa90f5bc31785406cDavid Woodhouse } 314071d93931a75dc1f82f0baa9959613af81c5a032Krzysztof Mazur if (test_bit(ATM_VF_RELEASED, &vcc->flags) || 315071d93931a75dc1f82f0baa9959613af81c5a032Krzysztof Mazur test_bit(ATM_VF_CLOSE, &vcc->flags) || 316071d93931a75dc1f82f0baa9959613af81c5a032Krzysztof Mazur !test_bit(ATM_VF_READY, &vcc->flags)) { 317071d93931a75dc1f82f0baa9959613af81c5a032Krzysztof Mazur bh_unlock_sock(sk_atm(vcc)); 318071d93931a75dc1f82f0baa9959613af81c5a032Krzysztof Mazur kfree_skb(skb); 319071d93931a75dc1f82f0baa9959613af81c5a032Krzysztof Mazur return DROP_PACKET; 320071d93931a75dc1f82f0baa9959613af81c5a032Krzysztof Mazur } 3213ac108006fd7f20cb8fc8ea2287f1497bcda00a1Krzysztof Mazur 3221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds switch (pvcc->encaps) { /* LLC encapsulation needed */ 3231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case e_llc: 3241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (skb_headroom(skb) < LLC_LEN) { 3251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct sk_buff *n; 3261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds n = skb_realloc_headroom(skb, LLC_LEN); 3271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (n != NULL && 3289d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse !pppoatm_may_send(pvcc, n->truesize)) { 3291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds kfree_skb(n); 3301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto nospace; 3311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 3325d0ba55b6486f58cc890918d7167063d83f7fbb4Eric Dumazet consume_skb(skb); 333d81219db6add0a176c37d6fe4e1c050778de9d2fJoe Perches skb = n; 3343ac108006fd7f20cb8fc8ea2287f1497bcda00a1Krzysztof Mazur if (skb == NULL) { 3353ac108006fd7f20cb8fc8ea2287f1497bcda00a1Krzysztof Mazur bh_unlock_sock(sk_atm(vcc)); 3361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return DROP_PACKET; 3373ac108006fd7f20cb8fc8ea2287f1497bcda00a1Krzysztof Mazur } 3389d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse } else if (!pppoatm_may_send(pvcc, skb->truesize)) 3391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto nospace; 3401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds memcpy(skb_push(skb, LLC_LEN), pppllc, LLC_LEN); 3411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 3421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case e_vc: 3439d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse if (!pppoatm_may_send(pvcc, skb->truesize)) 3441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto nospace; 3451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 3461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case e_autodetect: 3473ac108006fd7f20cb8fc8ea2287f1497bcda00a1Krzysztof Mazur bh_unlock_sock(sk_atm(vcc)); 348522400623e240ad134cb4101b1fddc3245d2a7edStephen Hemminger pr_debug("Trying to send without setting encaps!\n"); 3491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds kfree_skb(skb); 3501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 1; 3511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 3521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds atomic_add(skb->truesize, &sk_atm(ATM_SKB(skb)->vcc)->sk_wmem_alloc); 3541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ATM_SKB(skb)->atm_options = ATM_SKB(skb)->vcc->atm_options; 35599824461ea72ca0044cc6508f02c0e1cabf37ba5Joe Perches pr_debug("atm_skb(%p)->vcc(%p)->dev(%p)\n", 35699824461ea72ca0044cc6508f02c0e1cabf37ba5Joe Perches skb, ATM_SKB(skb)->vcc, ATM_SKB(skb)->vcc->dev); 3573ac108006fd7f20cb8fc8ea2287f1497bcda00a1Krzysztof Mazur ret = ATM_SKB(skb)->vcc->send(ATM_SKB(skb)->vcc, skb) 3581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ? DROP_PACKET : 1; 3593ac108006fd7f20cb8fc8ea2287f1497bcda00a1Krzysztof Mazur bh_unlock_sock(sk_atm(vcc)); 3603ac108006fd7f20cb8fc8ea2287f1497bcda00a1Krzysztof Mazur return ret; 361d81219db6add0a176c37d6fe4e1c050778de9d2fJoe Perchesnospace: 3623ac108006fd7f20cb8fc8ea2287f1497bcda00a1Krzysztof Mazur bh_unlock_sock(sk_atm(vcc)); 3631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* 3641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * We don't have space to send this SKB now, but we might have 3651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * already applied SC_COMP_PROT compression, so may need to undo 3661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 3671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if ((pvcc->flags & SC_COMP_PROT) && skb_headroom(skb) > 0 && 3681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds skb->data[-1] == '\0') 3691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds (void) skb_push(skb, 1); 3701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 3711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 3721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* This handles ioctls sent to the /dev/ppp interface */ 3741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int pppoatm_devppp_ioctl(struct ppp_channel *chan, unsigned int cmd, 3751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned long arg) 3761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 3771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds switch (cmd) { 3781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case PPPIOCGFLAGS: 3791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return put_user(chan_to_pvcc(chan)->flags, (int __user *) arg) 3801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ? -EFAULT : 0; 3811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case PPPIOCSFLAGS: 3821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return get_user(chan_to_pvcc(chan)->flags, (int __user *) arg) 3831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ? -EFAULT : 0; 3841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 3851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ENOTTY; 3861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 3871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 388d7100da026317fcf07411f765fe1cdb044053917stephen hemmingerstatic const struct ppp_channel_ops pppoatm_ops = { 3891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .start_xmit = pppoatm_send, 3901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .ioctl = pppoatm_devppp_ioctl, 3911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}; 3921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int pppoatm_assign_vcc(struct atm_vcc *atmvcc, void __user *arg) 3941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 3951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct atm_backend_ppp be; 3961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct pppoatm_vcc *pvcc; 3971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int err; 3981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* 3991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Each PPPoATM instance has its own tasklet - this is just a 4001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * prototypical one used to initialize them 4011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 4021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds static const DECLARE_TASKLET(tasklet_proto, pppoatm_wakeup_sender, 0); 4031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (copy_from_user(&be, arg, sizeof be)) 4041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EFAULT; 4051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (be.encaps != PPPOATM_ENCAPS_AUTODETECT && 4061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds be.encaps != PPPOATM_ENCAPS_VC && be.encaps != PPPOATM_ENCAPS_LLC) 4071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 4080da974f4f303a6842516b764507e3c0a03f41e5aPanagiotis Issaris pvcc = kzalloc(sizeof(*pvcc), GFP_KERNEL); 4091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (pvcc == NULL) 4101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ENOMEM; 4111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds pvcc->atmvcc = atmvcc; 4129d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse 4139d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse /* Maximum is zero, so that we can use atomic_inc_not_zero() */ 4149d02daf754238adac48fa075ee79e7edd3d79ed3David Woodhouse atomic_set(&pvcc->inflight, NONE_INFLIGHT); 4151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds pvcc->old_push = atmvcc->push; 4161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds pvcc->old_pop = atmvcc->pop; 417e41faed9cde1acce657f75a0b19a1787e9850d3fKrzysztof Mazur pvcc->old_owner = atmvcc->owner; 4180e56d99a5b557c760394d6941d7d1fc8d279eff3David Woodhouse pvcc->old_release_cb = atmvcc->release_cb; 4191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds pvcc->encaps = (enum pppoatm_encaps) be.encaps; 4201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds pvcc->chan.private = pvcc; 4211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds pvcc->chan.ops = &pppoatm_ops; 4221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds pvcc->chan.mtu = atmvcc->qos.txtp.max_sdu - PPP_HDRLEN - 4231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds (be.encaps == e_vc ? 0 : LLC_LEN); 4241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds pvcc->wakeup_tasklet = tasklet_proto; 4251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds pvcc->wakeup_tasklet.data = (unsigned long) &pvcc->chan; 426d81219db6add0a176c37d6fe4e1c050778de9d2fJoe Perches err = ppp_register_channel(&pvcc->chan); 427d81219db6add0a176c37d6fe4e1c050778de9d2fJoe Perches if (err != 0) { 4281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds kfree(pvcc); 4291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return err; 4301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 4311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds atmvcc->user_back = pvcc; 4321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds atmvcc->push = pppoatm_push; 4331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds atmvcc->pop = pppoatm_pop; 4340e56d99a5b557c760394d6941d7d1fc8d279eff3David Woodhouse atmvcc->release_cb = pppoatm_release_cb; 4351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds __module_get(THIS_MODULE); 436e41faed9cde1acce657f75a0b19a1787e9850d3fKrzysztof Mazur atmvcc->owner = THIS_MODULE; 4374e55f5785825f18b1eb6c5cc5a9717e276925805Jorge Boncompte [DTI 4384e55f5785825f18b1eb6c5cc5a9717e276925805Jorge Boncompte [DTI /* re-process everything received between connection setup and 4394e55f5785825f18b1eb6c5cc5a9717e276925805Jorge Boncompte [DTI backend setup */ 4404e55f5785825f18b1eb6c5cc5a9717e276925805Jorge Boncompte [DTI vcc_process_recv_queue(atmvcc); 4411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 4421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 4431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 4451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This handles ioctls actually performed on our vcc - we must return 4461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * -ENOIOCTLCMD for any unrecognized ioctl 4471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 4481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int pppoatm_ioctl(struct socket *sock, unsigned int cmd, 4491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned long arg) 4501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 4511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct atm_vcc *atmvcc = ATM_SD(sock); 4521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds void __user *argp = (void __user *)arg; 4531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (cmd != ATM_SETBACKEND && atmvcc->push != pppoatm_push) 4551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ENOIOCTLCMD; 4561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds switch (cmd) { 4571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case ATM_SETBACKEND: { 4581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds atm_backend_t b; 4591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (get_user(b, (atm_backend_t __user *) argp)) 4601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EFAULT; 4611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (b != ATM_BACKEND_PPP) 4621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ENOIOCTLCMD; 4631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!capable(CAP_NET_ADMIN)) 4641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EPERM; 4653b1a914595f3f9beb9e38ff3ddc7bdafa092ba22Krzysztof Mazur if (sock->state != SS_CONNECTED) 4663b1a914595f3f9beb9e38ff3ddc7bdafa092ba22Krzysztof Mazur return -EINVAL; 4671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return pppoatm_assign_vcc(atmvcc, argp); 4681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 4691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case PPPIOCGCHAN: 4701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return put_user(ppp_channel_index(&atmvcc_to_pvcc(atmvcc)-> 4711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds chan), (int __user *) argp) ? -EFAULT : 0; 4721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case PPPIOCGUNIT: 4731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return put_user(ppp_unit_number(&atmvcc_to_pvcc(atmvcc)-> 4741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds chan), (int __user *) argp) ? -EFAULT : 0; 4751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 4761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ENOIOCTLCMD; 4771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 4781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct atm_ioctl pppoatm_ioctl_ops = { 4801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .owner = THIS_MODULE, 4811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .ioctl = pppoatm_ioctl, 4821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}; 4831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init pppoatm_init(void) 4851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 4861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds register_atm_ioctl(&pppoatm_ioctl_ops); 4871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 4881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 4891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void __exit pppoatm_exit(void) 4911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 4921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds deregister_atm_ioctl(&pppoatm_ioctl_ops); 4931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 4941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_init(pppoatm_init); 4961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_exit(pppoatm_exit); 4971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_AUTHOR("Mitchell Blank Jr <mitch@sfgoth.com>"); 4991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DESCRIPTION("RFC2364 PPP over ATM/AAL5"); 5001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL"); 501