diff options
author | 2023-02-21 18:24:12 -0800 | |
---|---|---|
committer | 2023-02-21 18:24:12 -0800 | |
commit | 5b7c4cabbb65f5c469464da6c5f614cbd7f730f2 (patch) | |
tree | cc5c2d0a898769fd59549594fedb3ee6f84e59a0 /fs/coda/upcall.c | |
download | linux-5b7c4cabbb65f5c469464da6c5f614cbd7f730f2.tar.gz linux-5b7c4cabbb65f5c469464da6c5f614cbd7f730f2.zip |
Merge tag 'net-next-6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-nextgrafted
Pull networking updates from Jakub Kicinski:
"Core:
- Add dedicated kmem_cache for typical/small skb->head, avoid having
to access struct page at kfree time, and improve memory use.
- Introduce sysctl to set default RPS configuration for new netdevs.
- Define Netlink protocol specification format which can be used to
describe messages used by each family and auto-generate parsers.
Add tools for generating kernel data structures and uAPI headers.
- Expose all net/core sysctls inside netns.
- Remove 4s sleep in netpoll if carrier is instantly detected on
boot.
- Add configurable limit of MDB entries per port, and port-vlan.
- Continue populating drop reasons throughout the stack.
- Retire a handful of legacy Qdiscs and classifiers.
Protocols:
- Support IPv4 big TCP (TSO frames larger than 64kB).
- Add IP_LOCAL_PORT_RANGE socket option, to control local port range
on socket by socket basis.
- Track and report in procfs number of MPTCP sockets used.
- Support mixing IPv4 and IPv6 flows in the in-kernel MPTCP path
manager.
- IPv6: don't check net.ipv6.route.max_size and rely on garbage
collection to free memory (similarly to IPv4).
- Support Penultimate Segment Pop (PSP) flavor in SRv6 (RFC8986).
- ICMP: add per-rate limit counters.
- Add support for user scanning requests in ieee802154.
- Remove static WEP support.
- Support minimal Wi-Fi 7 Extremely High Throughput (EHT) rate
reporting.
- WiFi 7 EHT channel puncturing support (client & AP).
BPF:
- Add a rbtree data structure following the "next-gen data structure"
precedent set by recently added linked list, that is, by using
kfunc + kptr instead of adding a new BPF map type.
- Expose XDP hints via kfuncs with initial support for RX hash and
timestamp metadata.
- Add BPF_F_NO_TUNNEL_KEY extension to bpf_skb_set_tunnel_key to
better support decap on GRE tunnel devices not operating in collect
metadata.
- Improve x86 JIT's codegen for PROBE_MEM runtime error checks.
- Remove the need for trace_printk_lock for bpf_trace_printk and
bpf_trace_vprintk helpers.
- Extend libbpf's bpf_tracing.h support for tracing arguments of
kprobes/uprobes and syscall as a special case.
- Significantly reduce the search time for module symbols by
livepatch and BPF.
- Enable cpumasks to be used as kptrs, which is useful for tracing
programs tracking which tasks end up running on which CPUs in
different time intervals.
- Add support for BPF trampoline on s390x and riscv64.
- Add capability to export the XDP features supported by the NIC.
- Add __bpf_kfunc tag for marking kernel functions as kfuncs.
- Add cgroup.memory=nobpf kernel parameter option to disable BPF
memory accounting for container environments.
Netfilter:
- Remove the CLUSTERIP target. It has been marked as obsolete for
years, and we still have WARN splats wrt races of the out-of-band
/proc interface installed by this target.
- Add 'destroy' commands to nf_tables. They are identical to the
existing 'delete' commands, but do not return an error if the
referenced object (set, chain, rule...) did not exist.
Driver API:
- Improve cpumask_local_spread() locality to help NICs set the right
IRQ affinity on AMD platforms.
- Separate C22 and C45 MDIO bus transactions more clearly.
- Introduce new DCB table to control DSCP rewrite on egress.
- Support configuration of Physical Layer Collision Avoidance (PLCA)
Reconciliation Sublayer (RS) (802.3cg-2019). Modern version of
shared medium Ethernet.
- Support for MAC Merge layer (IEEE 802.3-2018 clause 99). Allowing
preemption of low priority frames by high priority frames.
- Add support for controlling MACSec offload using netlink SET.
- Rework devlink instance refcounts to allow registration and
de-registration under the instance lock. Split the code into
multiple files, drop some of the unnecessarily granular locks and
factor out common parts of netlink operation handling.
- Add TX frame aggregation parameters (for USB drivers).
- Add a new attr TCA_EXT_WARN_MSG to report TC (offload) warning
messages with notifications for debug.
- Allow offloading of UDP NEW connections via act_ct.
- Add support for per action HW stats in TC.
- Support hardware miss to TC action (continue processing in SW from
a specific point in the action chain).
- Warn if old Wireless Extension user space interface is used with
modern cfg80211/mac80211 drivers. Do not support Wireless
Extensions for Wi-Fi 7 devices at all. Everyone should switch to
using nl80211 interface instead.
- Improve the CAN bit timing configuration. Use extack to return
error messages directly to user space, update the SJW handling,
including the definition of a new default value that will benefit
CAN-FD controllers, by increasing their oscillator tolerance.
New hardware / drivers:
- Ethernet:
- nVidia BlueField-3 support (control traffic driver)
- Ethernet support for imx93 SoCs
- Motorcomm yt8531 gigabit Ethernet PHY
- onsemi NCN26000 10BASE-T1S PHY (with support for PLCA)
- Microchip LAN8841 PHY (incl. cable diagnostics and PTP)
- Amlogic gxl MDIO mux
- WiFi:
- RealTek RTL8188EU (rtl8xxxu)
- Qualcomm Wi-Fi 7 devices (ath12k)
- CAN:
- Renesas R-Car V4H
Drivers:
- Bluetooth:
- Set Per Platform Antenna Gain (PPAG) for Intel controllers.
- Ethernet NICs:
- Intel (1G, igc):
- support TSN / Qbv / packet scheduling features of i226 model
- Intel (100G, ice):
- use GNSS subsystem instead of TTY
- multi-buffer XDP support
- extend support for GPIO pins to E823 devices
- nVidia/Mellanox:
- update the shared buffer configuration on PFC commands
- implement PTP adjphase function for HW offset control
- TC support for Geneve and GRE with VF tunnel offload
- more efficient crypto key management method
- multi-port eswitch support
- Netronome/Corigine:
- add DCB IEEE support
- support IPsec offloading for NFP3800
- Freescale/NXP (enetc):
- support XDP_REDIRECT for XDP non-linear buffers
- improve reconfig, avoid link flap and waiting for idle
- support MAC Merge layer
- Other NICs:
- sfc/ef100: add basic devlink support for ef100
- ionic: rx_push mode operation (writing descriptors via MMIO)
- bnxt: use the auxiliary bus abstraction for RDMA
- r8169: disable ASPM and reset bus in case of tx timeout
- cpsw: support QSGMII mode for J721e CPSW9G
- cpts: support pulse-per-second output
- ngbe: add an mdio bus driver
- usbnet: optimize usbnet_bh() by avoiding unnecessary queuing
- r8152: handle devices with FW with NCM support
- amd-xgbe: support 10Mbps, 2.5GbE speeds and rx-adaptation
- virtio-net: support multi buffer XDP
- virtio/vsock: replace virtio_vsock_pkt with sk_buff
- tsnep: XDP support
- Ethernet high-speed switches:
- nVidia/Mellanox (mlxsw):
- add support for latency TLV (in FW control messages)
- Microchip (sparx5):
- separate explicit and implicit traffic forwarding rules, make
the implicit rules always active
- add support for egress DSCP rewrite
- IS0 VCAP support (Ingress Classification)
- IS2 VCAP filters (protos, L3 addrs, L4 ports, flags, ToS
etc.)
- ES2 VCAP support (Egress Access Control)
- support for Per-Stream Filtering and Policing (802.1Q,
8.6.5.1)
- Ethernet embedded switches:
- Marvell (mv88e6xxx):
- add MAB (port auth) offload support
- enable PTP receive for mv88e6390
- NXP (ocelot):
- support MAC Merge layer
- support for the the vsc7512 internal copper phys
- Microchip:
- lan9303: convert to PHYLINK
- lan966x: support TC flower filter statistics
- lan937x: PTP support for KSZ9563/KSZ8563 and LAN937x
- lan937x: support Credit Based Shaper configuration
- ksz9477: support Energy Efficient Ethernet
- other:
- qca8k: convert to regmap read/write API, use bulk operations
- rswitch: Improve TX timestamp accuracy
- Intel WiFi (iwlwifi):
- EHT (Wi-Fi 7) rate reporting
- STEP equalizer support: transfer some STEP (connection to radio
on platforms with integrated wifi) related parameters from the
BIOS to the firmware.
- Qualcomm 802.11ax WiFi (ath11k):
- IPQ5018 support
- Fine Timing Measurement (FTM) responder role support
- channel 177 support
- MediaTek WiFi (mt76):
- per-PHY LED support
- mt7996: EHT (Wi-Fi 7) support
- Wireless Ethernet Dispatch (WED) reset support
- switch to using page pool allocator
- RealTek WiFi (rtw89):
- support new version of Bluetooth co-existance
- Mobile:
- rmnet: support TX aggregation"
* tag 'net-next-6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next: (1872 commits)
page_pool: add a comment explaining the fragment counter usage
net: ethtool: fix __ethtool_dev_mm_supported() implementation
ethtool: pse-pd: Fix double word in comments
xsk: add linux/vmalloc.h to xsk.c
sefltests: netdevsim: wait for devlink instance after netns removal
selftest: fib_tests: Always cleanup before exit
net/mlx5e: Align IPsec ASO result memory to be as required by hardware
net/mlx5e: TC, Set CT miss to the specific ct action instance
net/mlx5e: Rename CHAIN_TO_REG to MAPPED_OBJ_TO_REG
net/mlx5: Refactor tc miss handling to a single function
net/mlx5: Kconfig: Make tc offload depend on tc skb extension
net/sched: flower: Support hardware miss to tc action
net/sched: flower: Move filter handle initialization earlier
net/sched: cls_api: Support hardware miss to tc action
net/sched: Rename user cookie and act cookie
sfc: fix builds without CONFIG_RTC_LIB
sfc: clean up some inconsistent indentings
net/mlx4_en: Introduce flexible array to silence overflow warning
net: lan966x: Fix possible deadlock inside PTP
net/ulp: Remove redundant ->clone() test in inet_clone_ulp().
...
Diffstat (limited to 'fs/coda/upcall.c')
-rw-r--r-- | fs/coda/upcall.c | 961 |
1 files changed, 961 insertions, 0 deletions
diff --git a/fs/coda/upcall.c b/fs/coda/upcall.c new file mode 100644 index 000000000..cd6a3721f --- /dev/null +++ b/fs/coda/upcall.c @@ -0,0 +1,961 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Mostly platform independent upcall operations to Venus: + * -- upcalls + * -- upcall routines + * + * Linux 2.0 version + * Copyright (C) 1996 Peter J. Braam <braam@maths.ox.ac.uk>, + * Michael Callahan <callahan@maths.ox.ac.uk> + * + * Redone for Linux 2.1 + * Copyright (C) 1997 Carnegie Mellon University + * + * Carnegie Mellon University encourages users of this code to contribute + * improvements to the Coda project. Contact Peter Braam <coda@cs.cmu.edu>. + */ + +#include <linux/signal.h> +#include <linux/sched/signal.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/time.h> +#include <linux/fs.h> +#include <linux/file.h> +#include <linux/stat.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/slab.h> +#include <linux/mutex.h> +#include <linux/uaccess.h> +#include <linux/vmalloc.h> +#include <linux/vfs.h> + +#include <linux/coda.h> +#include "coda_psdev.h" +#include "coda_linux.h" +#include "coda_cache.h" + +#include "coda_int.h" + +static int coda_upcall(struct venus_comm *vc, int inSize, int *outSize, + union inputArgs *buffer); + +static void *alloc_upcall(int opcode, int size) +{ + union inputArgs *inp; + + inp = kvzalloc(size, GFP_KERNEL); + if (!inp) + return ERR_PTR(-ENOMEM); + + inp->ih.opcode = opcode; + inp->ih.pid = task_pid_nr_ns(current, &init_pid_ns); + inp->ih.pgid = task_pgrp_nr_ns(current, &init_pid_ns); + inp->ih.uid = from_kuid(&init_user_ns, current_fsuid()); + + return (void*)inp; +} + +#define UPARG(op)\ +do {\ + inp = (union inputArgs *)alloc_upcall(op, insize); \ + if (IS_ERR(inp)) { return PTR_ERR(inp); }\ + outp = (union outputArgs *)(inp); \ + outsize = insize; \ +} while (0) + +#define INSIZE(tag) sizeof(struct coda_ ## tag ## _in) +#define OUTSIZE(tag) sizeof(struct coda_ ## tag ## _out) +#define SIZE(tag) max_t(unsigned int, INSIZE(tag), OUTSIZE(tag)) + + +/* the upcalls */ +int venus_rootfid(struct super_block *sb, struct CodaFid *fidp) +{ + union inputArgs *inp; + union outputArgs *outp; + int insize, outsize, error; + + insize = SIZE(root); + UPARG(CODA_ROOT); + + error = coda_upcall(coda_vcp(sb), insize, &outsize, inp); + if (!error) + *fidp = outp->coda_root.VFid; + + kvfree(inp); + return error; +} + +int venus_getattr(struct super_block *sb, struct CodaFid *fid, + struct coda_vattr *attr) +{ + union inputArgs *inp; + union outputArgs *outp; + int insize, outsize, error; + + insize = SIZE(getattr); + UPARG(CODA_GETATTR); + inp->coda_getattr.VFid = *fid; + + error = coda_upcall(coda_vcp(sb), insize, &outsize, inp); + if (!error) + *attr = outp->coda_getattr.attr; + + kvfree(inp); + return error; +} + +int venus_setattr(struct super_block *sb, struct CodaFid *fid, + struct coda_vattr *vattr) +{ + union inputArgs *inp; + union outputArgs *outp; + int insize, outsize, error; + + insize = SIZE(setattr); + UPARG(CODA_SETATTR); + + inp->coda_setattr.VFid = *fid; + inp->coda_setattr.attr = *vattr; + + error = coda_upcall(coda_vcp(sb), insize, &outsize, inp); + + kvfree(inp); + return error; +} + +int venus_lookup(struct super_block *sb, struct CodaFid *fid, + const char *name, int length, int * type, + struct CodaFid *resfid) +{ + union inputArgs *inp; + union outputArgs *outp; + int insize, outsize, error; + int offset; + + offset = INSIZE(lookup); + insize = max_t(unsigned int, offset + length +1, OUTSIZE(lookup)); + UPARG(CODA_LOOKUP); + + inp->coda_lookup.VFid = *fid; + inp->coda_lookup.name = offset; + inp->coda_lookup.flags = CLU_CASE_SENSITIVE; + /* send Venus a null terminated string */ + memcpy((char *)(inp) + offset, name, length); + *((char *)inp + offset + length) = '\0'; + + error = coda_upcall(coda_vcp(sb), insize, &outsize, inp); + if (!error) { + *resfid = outp->coda_lookup.VFid; + *type = outp->coda_lookup.vtype; + } + + kvfree(inp); + return error; +} + +int venus_close(struct super_block *sb, struct CodaFid *fid, int flags, + kuid_t uid) +{ + union inputArgs *inp; + union outputArgs *outp; + int insize, outsize, error; + + insize = SIZE(release); + UPARG(CODA_CLOSE); + + inp->ih.uid = from_kuid(&init_user_ns, uid); + inp->coda_close.VFid = *fid; + inp->coda_close.flags = flags; + + error = coda_upcall(coda_vcp(sb), insize, &outsize, inp); + + kvfree(inp); + return error; +} + +int venus_open(struct super_block *sb, struct CodaFid *fid, + int flags, struct file **fh) +{ + union inputArgs *inp; + union outputArgs *outp; + int insize, outsize, error; + + insize = SIZE(open_by_fd); + UPARG(CODA_OPEN_BY_FD); + + inp->coda_open_by_fd.VFid = *fid; + inp->coda_open_by_fd.flags = flags; + + error = coda_upcall(coda_vcp(sb), insize, &outsize, inp); + if (!error) + *fh = outp->coda_open_by_fd.fh; + + kvfree(inp); + return error; +} + +int venus_mkdir(struct super_block *sb, struct CodaFid *dirfid, + const char *name, int length, + struct CodaFid *newfid, struct coda_vattr *attrs) +{ + union inputArgs *inp; + union outputArgs *outp; + int insize, outsize, error; + int offset; + + offset = INSIZE(mkdir); + insize = max_t(unsigned int, offset + length + 1, OUTSIZE(mkdir)); + UPARG(CODA_MKDIR); + + inp->coda_mkdir.VFid = *dirfid; + inp->coda_mkdir.attr = *attrs; + inp->coda_mkdir.name = offset; + /* Venus must get null terminated string */ + memcpy((char *)(inp) + offset, name, length); + *((char *)inp + offset + length) = '\0'; + + error = coda_upcall(coda_vcp(sb), insize, &outsize, inp); + if (!error) { + *attrs = outp->coda_mkdir.attr; + *newfid = outp->coda_mkdir.VFid; + } + + kvfree(inp); + return error; +} + + +int venus_rename(struct super_block *sb, struct CodaFid *old_fid, + struct CodaFid *new_fid, size_t old_length, + size_t new_length, const char *old_name, + const char *new_name) +{ + union inputArgs *inp; + union outputArgs *outp; + int insize, outsize, error; + int offset, s; + + offset = INSIZE(rename); + insize = max_t(unsigned int, offset + new_length + old_length + 8, + OUTSIZE(rename)); + UPARG(CODA_RENAME); + + inp->coda_rename.sourceFid = *old_fid; + inp->coda_rename.destFid = *new_fid; + inp->coda_rename.srcname = offset; + + /* Venus must receive an null terminated string */ + s = ( old_length & ~0x3) +4; /* round up to word boundary */ + memcpy((char *)(inp) + offset, old_name, old_length); + *((char *)inp + offset + old_length) = '\0'; + + /* another null terminated string for Venus */ + offset += s; + inp->coda_rename.destname = offset; + s = ( new_length & ~0x3) +4; /* round up to word boundary */ + memcpy((char *)(inp) + offset, new_name, new_length); + *((char *)inp + offset + new_length) = '\0'; + + error = coda_upcall(coda_vcp(sb), insize, &outsize, inp); + + kvfree(inp); + return error; +} + +int venus_create(struct super_block *sb, struct CodaFid *dirfid, + const char *name, int length, int excl, int mode, + struct CodaFid *newfid, struct coda_vattr *attrs) +{ + union inputArgs *inp; + union outputArgs *outp; + int insize, outsize, error; + int offset; + + offset = INSIZE(create); + insize = max_t(unsigned int, offset + length + 1, OUTSIZE(create)); + UPARG(CODA_CREATE); + + inp->coda_create.VFid = *dirfid; + inp->coda_create.attr.va_mode = mode; + inp->coda_create.excl = excl; + inp->coda_create.mode = mode; + inp->coda_create.name = offset; + + /* Venus must get null terminated string */ + memcpy((char *)(inp) + offset, name, length); + *((char *)inp + offset + length) = '\0'; + + error = coda_upcall(coda_vcp(sb), insize, &outsize, inp); + if (!error) { + *attrs = outp->coda_create.attr; + *newfid = outp->coda_create.VFid; + } + + kvfree(inp); + return error; +} + +int venus_rmdir(struct super_block *sb, struct CodaFid *dirfid, + const char *name, int length) +{ + union inputArgs *inp; + union outputArgs *outp; + int insize, outsize, error; + int offset; + + offset = INSIZE(rmdir); + insize = max_t(unsigned int, offset + length + 1, OUTSIZE(rmdir)); + UPARG(CODA_RMDIR); + + inp->coda_rmdir.VFid = *dirfid; + inp->coda_rmdir.name = offset; + memcpy((char *)(inp) + offset, name, length); + *((char *)inp + offset + length) = '\0'; + + error = coda_upcall(coda_vcp(sb), insize, &outsize, inp); + + kvfree(inp); + return error; +} + +int venus_remove(struct super_block *sb, struct CodaFid *dirfid, + const char *name, int length) +{ + union inputArgs *inp; + union outputArgs *outp; + int error=0, insize, outsize, offset; + + offset = INSIZE(remove); + insize = max_t(unsigned int, offset + length + 1, OUTSIZE(remove)); + UPARG(CODA_REMOVE); + + inp->coda_remove.VFid = *dirfid; + inp->coda_remove.name = offset; + memcpy((char *)(inp) + offset, name, length); + *((char *)inp + offset + length) = '\0'; + + error = coda_upcall(coda_vcp(sb), insize, &outsize, inp); + + kvfree(inp); + return error; +} + +int venus_readlink(struct super_block *sb, struct CodaFid *fid, + char *buffer, int *length) +{ + union inputArgs *inp; + union outputArgs *outp; + int insize, outsize, error; + int retlen; + char *result; + + insize = max_t(unsigned int, + INSIZE(readlink), OUTSIZE(readlink)+ *length); + UPARG(CODA_READLINK); + + inp->coda_readlink.VFid = *fid; + + error = coda_upcall(coda_vcp(sb), insize, &outsize, inp); + if (!error) { + retlen = outp->coda_readlink.count; + if (retlen >= *length) + retlen = *length - 1; + *length = retlen; + result = (char *)outp + (long)outp->coda_readlink.data; + memcpy(buffer, result, retlen); + *(buffer + retlen) = '\0'; + } + + kvfree(inp); + return error; +} + + + +int venus_link(struct super_block *sb, struct CodaFid *fid, + struct CodaFid *dirfid, const char *name, int len ) +{ + union inputArgs *inp; + union outputArgs *outp; + int insize, outsize, error; + int offset; + + offset = INSIZE(link); + insize = max_t(unsigned int, offset + len + 1, OUTSIZE(link)); + UPARG(CODA_LINK); + + inp->coda_link.sourceFid = *fid; + inp->coda_link.destFid = *dirfid; + inp->coda_link.tname = offset; + + /* make sure strings are null terminated */ + memcpy((char *)(inp) + offset, name, len); + *((char *)inp + offset + len) = '\0'; + + error = coda_upcall(coda_vcp(sb), insize, &outsize, inp); + + kvfree(inp); + return error; +} + +int venus_symlink(struct super_block *sb, struct CodaFid *fid, + const char *name, int len, + const char *symname, int symlen) +{ + union inputArgs *inp; + union outputArgs *outp; + int insize, outsize, error; + int offset, s; + + offset = INSIZE(symlink); + insize = max_t(unsigned int, offset + len + symlen + 8, OUTSIZE(symlink)); + UPARG(CODA_SYMLINK); + + /* inp->coda_symlink.attr = *tva; XXXXXX */ + inp->coda_symlink.VFid = *fid; + + /* Round up to word boundary and null terminate */ + inp->coda_symlink.srcname = offset; + s = ( symlen & ~0x3 ) + 4; + memcpy((char *)(inp) + offset, symname, symlen); + *((char *)inp + offset + symlen) = '\0'; + + /* Round up to word boundary and null terminate */ + offset += s; + inp->coda_symlink.tname = offset; + s = (len & ~0x3) + 4; + memcpy((char *)(inp) + offset, name, len); + *((char *)inp + offset + len) = '\0'; + + error = coda_upcall(coda_vcp(sb), insize, &outsize, inp); + + kvfree(inp); + return error; +} + +int venus_fsync(struct super_block *sb, struct CodaFid *fid) +{ + union inputArgs *inp; + union outputArgs *outp; + int insize, outsize, error; + + insize=SIZE(fsync); + UPARG(CODA_FSYNC); + + inp->coda_fsync.VFid = *fid; + error = coda_upcall(coda_vcp(sb), insize, &outsize, inp); + + kvfree(inp); + return error; +} + +int venus_access(struct super_block *sb, struct CodaFid *fid, int mask) +{ + union inputArgs *inp; + union outputArgs *outp; + int insize, outsize, error; + + insize = SIZE(access); + UPARG(CODA_ACCESS); + + inp->coda_access.VFid = *fid; + inp->coda_access.flags = mask; + + error = coda_upcall(coda_vcp(sb), insize, &outsize, inp); + + kvfree(inp); + return error; +} + + +int venus_pioctl(struct super_block *sb, struct CodaFid *fid, + unsigned int cmd, struct PioctlData *data) +{ + union inputArgs *inp; + union outputArgs *outp; + int insize, outsize, error; + int iocsize; + + insize = VC_MAXMSGSIZE; + UPARG(CODA_IOCTL); + + /* build packet for Venus */ + if (data->vi.in_size > VC_MAXDATASIZE) { + error = -EINVAL; + goto exit; + } + + if (data->vi.out_size > VC_MAXDATASIZE) { + error = -EINVAL; + goto exit; + } + + inp->coda_ioctl.VFid = *fid; + + /* the cmd field was mutated by increasing its size field to + * reflect the path and follow args. We need to subtract that + * out before sending the command to Venus. */ + inp->coda_ioctl.cmd = (cmd & ~(PIOCPARM_MASK << 16)); + iocsize = ((cmd >> 16) & PIOCPARM_MASK) - sizeof(char *) - sizeof(int); + inp->coda_ioctl.cmd |= (iocsize & PIOCPARM_MASK) << 16; + + /* in->coda_ioctl.rwflag = flag; */ + inp->coda_ioctl.len = data->vi.in_size; + inp->coda_ioctl.data = (char *)(INSIZE(ioctl)); + + /* get the data out of user space */ + if (copy_from_user((char *)inp + (long)inp->coda_ioctl.data, + data->vi.in, data->vi.in_size)) { + error = -EINVAL; + goto exit; + } + + error = coda_upcall(coda_vcp(sb), SIZE(ioctl) + data->vi.in_size, + &outsize, inp); + + if (error) { + pr_warn("%s: Venus returns: %d for %s\n", + __func__, error, coda_f2s(fid)); + goto exit; + } + + if (outsize < (long)outp->coda_ioctl.data + outp->coda_ioctl.len) { + error = -EINVAL; + goto exit; + } + + /* Copy out the OUT buffer. */ + if (outp->coda_ioctl.len > data->vi.out_size) { + error = -EINVAL; + goto exit; + } + + /* Copy out the OUT buffer. */ + if (copy_to_user(data->vi.out, + (char *)outp + (long)outp->coda_ioctl.data, + outp->coda_ioctl.len)) { + error = -EFAULT; + goto exit; + } + + exit: + kvfree(inp); + return error; +} + +int venus_statfs(struct dentry *dentry, struct kstatfs *sfs) +{ + union inputArgs *inp; + union outputArgs *outp; + int insize, outsize, error; + + insize = SIZE(statfs); + UPARG(CODA_STATFS); + + error = coda_upcall(coda_vcp(dentry->d_sb), insize, &outsize, inp); + if (!error) { + sfs->f_blocks = outp->coda_statfs.stat.f_blocks; + sfs->f_bfree = outp->coda_statfs.stat.f_bfree; + sfs->f_bavail = outp->coda_statfs.stat.f_bavail; + sfs->f_files = outp->coda_statfs.stat.f_files; + sfs->f_ffree = outp->coda_statfs.stat.f_ffree; + } + + kvfree(inp); + return error; +} + +int venus_access_intent(struct super_block *sb, struct CodaFid *fid, + bool *access_intent_supported, + size_t count, loff_t ppos, int type) +{ + union inputArgs *inp; + union outputArgs *outp; + int insize, outsize, error; + bool finalizer = + type == CODA_ACCESS_TYPE_READ_FINISH || + type == CODA_ACCESS_TYPE_WRITE_FINISH; + + if (!*access_intent_supported && !finalizer) + return 0; + + insize = SIZE(access_intent); + UPARG(CODA_ACCESS_INTENT); + + inp->coda_access_intent.VFid = *fid; + inp->coda_access_intent.count = count; + inp->coda_access_intent.pos = ppos; + inp->coda_access_intent.type = type; + + error = coda_upcall(coda_vcp(sb), insize, + finalizer ? NULL : &outsize, inp); + + /* + * we have to free the request buffer for synchronous upcalls + * or when asynchronous upcalls fail, but not when asynchronous + * upcalls succeed + */ + if (!finalizer || error) + kvfree(inp); + + /* Chunked access is not supported or an old Coda client */ + if (error == -EOPNOTSUPP) { + *access_intent_supported = false; + error = 0; + } + return error; +} + +/* + * coda_upcall and coda_downcall routines. + */ +static void coda_block_signals(sigset_t *old) +{ + spin_lock_irq(¤t->sighand->siglock); + *old = current->blocked; + + sigfillset(¤t->blocked); + sigdelset(¤t->blocked, SIGKILL); + sigdelset(¤t->blocked, SIGSTOP); + sigdelset(¤t->blocked, SIGINT); + + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); +} + +static void coda_unblock_signals(sigset_t *old) +{ + spin_lock_irq(¤t->sighand->siglock); + current->blocked = *old; + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); +} + +/* Don't allow signals to interrupt the following upcalls before venus + * has seen them, + * - CODA_CLOSE or CODA_RELEASE upcall (to avoid reference count problems) + * - CODA_STORE (to avoid data loss) + * - CODA_ACCESS_INTENT (to avoid reference count problems) + */ +#define CODA_INTERRUPTIBLE(r) (!coda_hard && \ + (((r)->uc_opcode != CODA_CLOSE && \ + (r)->uc_opcode != CODA_STORE && \ + (r)->uc_opcode != CODA_ACCESS_INTENT && \ + (r)->uc_opcode != CODA_RELEASE) || \ + (r)->uc_flags & CODA_REQ_READ)) + +static inline void coda_waitfor_upcall(struct venus_comm *vcp, + struct upc_req *req) +{ + DECLARE_WAITQUEUE(wait, current); + unsigned long timeout = jiffies + coda_timeout * HZ; + sigset_t old; + int blocked; + + coda_block_signals(&old); + blocked = 1; + + add_wait_queue(&req->uc_sleep, &wait); + for (;;) { + if (CODA_INTERRUPTIBLE(req)) + set_current_state(TASK_INTERRUPTIBLE); + else + set_current_state(TASK_UNINTERRUPTIBLE); + + /* got a reply */ + if (req->uc_flags & (CODA_REQ_WRITE | CODA_REQ_ABORT)) + break; + + if (blocked && time_after(jiffies, timeout) && + CODA_INTERRUPTIBLE(req)) + { + coda_unblock_signals(&old); + blocked = 0; + } + + if (signal_pending(current)) { + list_del(&req->uc_chain); + break; + } + + mutex_unlock(&vcp->vc_mutex); + if (blocked) + schedule_timeout(HZ); + else + schedule(); + mutex_lock(&vcp->vc_mutex); + } + if (blocked) + coda_unblock_signals(&old); + + remove_wait_queue(&req->uc_sleep, &wait); + set_current_state(TASK_RUNNING); +} + + +/* + * coda_upcall will return an error in the case of + * failed communication with Venus _or_ will peek at Venus + * reply and return Venus' error. + * + * As venus has 2 types of errors, normal errors (positive) and internal + * errors (negative), normal errors are negated, while internal errors + * are all mapped to -EINTR, while showing a nice warning message. (jh) + */ +static int coda_upcall(struct venus_comm *vcp, + int inSize, int *outSize, + union inputArgs *buffer) +{ + union outputArgs *out; + union inputArgs *sig_inputArgs; + struct upc_req *req = NULL, *sig_req; + int error; + + mutex_lock(&vcp->vc_mutex); + + if (!vcp->vc_inuse) { + pr_notice("Venus dead, not sending upcall\n"); + error = -ENXIO; + goto exit; + } + + /* Format the request message. */ + req = kmalloc(sizeof(struct upc_req), GFP_KERNEL); + if (!req) { + error = -ENOMEM; + goto exit; + } + + buffer->ih.unique = ++vcp->vc_seq; + + req->uc_data = (void *)buffer; + req->uc_flags = outSize ? 0 : CODA_REQ_ASYNC; + req->uc_inSize = inSize; + req->uc_outSize = (outSize && *outSize) ? *outSize : inSize; + req->uc_opcode = buffer->ih.opcode; + req->uc_unique = buffer->ih.unique; + init_waitqueue_head(&req->uc_sleep); + + /* Append msg to pending queue and poke Venus. */ + list_add_tail(&req->uc_chain, &vcp->vc_pending); + wake_up_interruptible(&vcp->vc_waitq); + + /* We can return early on asynchronous requests */ + if (outSize == NULL) { + mutex_unlock(&vcp->vc_mutex); + return 0; + } + + /* We can be interrupted while we wait for Venus to process + * our request. If the interrupt occurs before Venus has read + * the request, we dequeue and return. If it occurs after the + * read but before the reply, we dequeue, send a signal + * message, and return. If it occurs after the reply we ignore + * it. In no case do we want to restart the syscall. If it + * was interrupted by a venus shutdown (psdev_close), return + * ENODEV. */ + + /* Go to sleep. Wake up on signals only after the timeout. */ + coda_waitfor_upcall(vcp, req); + + /* Op went through, interrupt or not... */ + if (req->uc_flags & CODA_REQ_WRITE) { + out = (union outputArgs *)req->uc_data; + /* here we map positive Venus errors to kernel errors */ + error = -out->oh.result; + *outSize = req->uc_outSize; + goto exit; + } + + error = -EINTR; + if ((req->uc_flags & CODA_REQ_ABORT) || !signal_pending(current)) { + pr_warn("Unexpected interruption.\n"); + goto exit; + } + + /* Interrupted before venus read it. */ + if (!(req->uc_flags & CODA_REQ_READ)) + goto exit; + + /* Venus saw the upcall, make sure we can send interrupt signal */ + if (!vcp->vc_inuse) { + pr_info("Venus dead, not sending signal.\n"); + goto exit; + } + + error = -ENOMEM; + sig_req = kmalloc(sizeof(struct upc_req), GFP_KERNEL); + if (!sig_req) goto exit; + + sig_inputArgs = kvzalloc(sizeof(*sig_inputArgs), GFP_KERNEL); + if (!sig_inputArgs) { + kfree(sig_req); + goto exit; + } + + error = -EINTR; + sig_inputArgs->ih.opcode = CODA_SIGNAL; + sig_inputArgs->ih.unique = req->uc_unique; + + sig_req->uc_flags = CODA_REQ_ASYNC; + sig_req->uc_opcode = sig_inputArgs->ih.opcode; + sig_req->uc_unique = sig_inputArgs->ih.unique; + sig_req->uc_data = (void *)sig_inputArgs; + sig_req->uc_inSize = sizeof(struct coda_in_hdr); + sig_req->uc_outSize = sizeof(struct coda_in_hdr); + + /* insert at head of queue! */ + list_add(&(sig_req->uc_chain), &vcp->vc_pending); + wake_up_interruptible(&vcp->vc_waitq); + +exit: + kfree(req); + mutex_unlock(&vcp->vc_mutex); + return error; +} + +/* + The statements below are part of the Coda opportunistic + programming -- taken from the Mach/BSD kernel code for Coda. + You don't get correct semantics by stating what needs to be + done without guaranteeing the invariants needed for it to happen. + When will be have time to find out what exactly is going on? (pjb) +*/ + + +/* + * There are 7 cases where cache invalidations occur. The semantics + * of each is listed here: + * + * CODA_FLUSH -- flush all entries from the name cache and the cnode cache. + * CODA_PURGEUSER -- flush all entries from the name cache for a specific user + * This call is a result of token expiration. + * + * The next arise as the result of callbacks on a file or directory. + * CODA_ZAPFILE -- flush the cached attributes for a file. + + * CODA_ZAPDIR -- flush the attributes for the dir and + * force a new lookup for all the children + of this dir. + + * + * The next is a result of Venus detecting an inconsistent file. + * CODA_PURGEFID -- flush the attribute for the file + * purge it and its children from the dcache + * + * The last allows Venus to replace local fids with global ones + * during reintegration. + * + * CODA_REPLACE -- replace one CodaFid with another throughout the name cache */ + +int coda_downcall(struct venus_comm *vcp, int opcode, union outputArgs *out, + size_t nbytes) +{ + struct inode *inode = NULL; + struct CodaFid *fid = NULL, *newfid; + struct super_block *sb; + + /* + * Make sure we have received enough data from the cache + * manager to populate the necessary fields in the buffer + */ + switch (opcode) { + case CODA_PURGEUSER: + if (nbytes < sizeof(struct coda_purgeuser_out)) + return -EINVAL; + break; + + case CODA_ZAPDIR: + if (nbytes < sizeof(struct coda_zapdir_out)) + return -EINVAL; + break; + + case CODA_ZAPFILE: + if (nbytes < sizeof(struct coda_zapfile_out)) + return -EINVAL; + break; + + case CODA_PURGEFID: + if (nbytes < sizeof(struct coda_purgefid_out)) + return -EINVAL; + break; + + case CODA_REPLACE: + if (nbytes < sizeof(struct coda_replace_out)) + return -EINVAL; + break; + } + + /* Handle invalidation requests. */ + mutex_lock(&vcp->vc_mutex); + sb = vcp->vc_sb; + if (!sb || !sb->s_root) + goto unlock_out; + + switch (opcode) { + case CODA_FLUSH: + coda_cache_clear_all(sb); + shrink_dcache_sb(sb); + if (d_really_is_positive(sb->s_root)) + coda_flag_inode(d_inode(sb->s_root), C_FLUSH); + break; + + case CODA_PURGEUSER: + coda_cache_clear_all(sb); + break; + + case CODA_ZAPDIR: + fid = &out->coda_zapdir.CodaFid; + break; + + case CODA_ZAPFILE: + fid = &out->coda_zapfile.CodaFid; + break; + + case CODA_PURGEFID: + fid = &out->coda_purgefid.CodaFid; + break; + + case CODA_REPLACE: + fid = &out->coda_replace.OldFid; + break; + } + if (fid) + inode = coda_fid_to_inode(fid, sb); + +unlock_out: + mutex_unlock(&vcp->vc_mutex); + + if (!inode) + return 0; + + switch (opcode) { + case CODA_ZAPDIR: + coda_flag_inode_children(inode, C_PURGE); + coda_flag_inode(inode, C_VATTR); + break; + + case CODA_ZAPFILE: + coda_flag_inode(inode, C_VATTR); + break; + + case CODA_PURGEFID: + coda_flag_inode_children(inode, C_PURGE); + + /* catch the dentries later if some are still busy */ + coda_flag_inode(inode, C_PURGE); + d_prune_aliases(inode); + break; + + case CODA_REPLACE: + newfid = &out->coda_replace.NewFid; + coda_replace_fid(inode, fid, newfid); + break; + } + iput(inode); + return 0; +} |